Home | History | Annotate | Download | only in flex-2.5.35
      1 /* flex - tool to generate fast lexical analyzers */
      2 
      3 /*  Copyright (c) 1990 The Regents of the University of California. */
      4 /*  All rights reserved. */
      5 
      6 /*  This code is derived from software contributed to Berkeley by */
      7 /*  Vern Paxson. */
      8 
      9 /*  The United States Government has rights in this work pursuant */
     10 /*  to contract no. DE-AC03-76SF00098 between the United States */
     11 /*  Department of Energy and the University of California. */
     12 
     13 /*  This file is part of flex. */
     14 
     15 /*  Redistribution and use in source and binary forms, with or without */
     16 /*  modification, are permitted provided that the following conditions */
     17 /*  are met: */
     18 
     19 /*  1. Redistributions of source code must retain the above copyright */
     20 /*     notice, this list of conditions and the following disclaimer. */
     21 /*  2. Redistributions in binary form must reproduce the above copyright */
     22 /*     notice, this list of conditions and the following disclaimer in the */
     23 /*     documentation and/or other materials provided with the distribution. */
     24 
     25 /*  Neither the name of the University nor the names of its contributors */
     26 /*  may be used to endorse or promote products derived from this software */
     27 /*  without specific prior written permission. */
     28 
     29 /*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
     30 /*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
     31 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
     32 /*  PURPOSE. */
     33 
     34 #include "flexdef.h"
     36 #include "scanopt.h"
     37 
     38 
     39 /* Internal structures */
     40 
     41 #ifdef HAVE_STRCASECMP
     42 #define STRCASECMP(a,b) strcasecmp(a,b)
     43 #else
     44 static int STRCASECMP PROTO ((const char *, const char *));
     45 
     46 static int STRCASECMP (a, b)
     47      const char *a;
     48      const char *b;
     49 {
     50 	while (tolower (*a++) == tolower (*b++)) ;
     51 	return b - a;
     52 }
     53 #endif
     54 
     55 #define ARG_NONE 0x01
     56 #define ARG_REQ  0x02
     57 #define ARG_OPT  0x04
     58 #define IS_LONG  0x08
     59 
     60 struct _aux {
     61 	int     flags;		/* The above hex flags. */
     62 	int     namelen;	/* Length of the actual option word, e.g., "--file[=foo]" is 4 */
     63 	int     printlen;	/* Length of entire string, e.g., "--file[=foo]" is 12 */
     64 };
     65 
     66 
     67 struct _scanopt_t {
     68 	const optspec_t *options;	/* List of options. */
     69 	struct _aux *aux;	/* Auxiliary data about options. */
     70 	int     optc;		/* Number of options. */
     71 	int     argc;		/* Number of args. */
     72 	char  **argv;		/* Array of strings. */
     73 	int     index;		/* Used as: argv[index][subscript]. */
     74 	int     subscript;
     75 	char    no_err_msg;	/* If true, do not print errors. */
     76 	char    has_long;
     77 	char    has_short;
     78 };
     79 
     80 /* Accessor functions. These WOULD be one-liners, but portability calls. */
     81 static const char *NAME PROTO ((struct _scanopt_t *, int));
     82 static int PRINTLEN PROTO ((struct _scanopt_t *, int));
     83 static int RVAL PROTO ((struct _scanopt_t *, int));
     84 static int FLAGS PROTO ((struct _scanopt_t *, int));
     85 static const char *DESC PROTO ((struct _scanopt_t *, int));
     86 static int scanopt_err PROTO ((struct _scanopt_t *, int, int, int));
     87 static int matchlongopt PROTO ((char *, char **, int *, char **, int *));
     88 static int find_opt
     89 PROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset));
     90 
     91 static const char *NAME (s, i)
     92      struct _scanopt_t *s;
     93      int     i;
     94 {
     95 	return s->options[i].opt_fmt +
     96 		((s->aux[i].flags & IS_LONG) ? 2 : 1);
     97 }
     98 
     99 static int PRINTLEN (s, i)
    100      struct _scanopt_t *s;
    101      int     i;
    102 {
    103 	return s->aux[i].printlen;
    104 }
    105 
    106 static int RVAL (s, i)
    107      struct _scanopt_t *s;
    108      int     i;
    109 {
    110 	return s->options[i].r_val;
    111 }
    112 
    113 static int FLAGS (s, i)
    114      struct _scanopt_t *s;
    115      int     i;
    116 {
    117 	return s->aux[i].flags;
    118 }
    119 
    120 static const char *DESC (s, i)
    121      struct _scanopt_t *s;
    122      int     i;
    123 {
    124 	return s->options[i].desc ? s->options[i].desc : "";
    125 }
    126 
    127 #ifndef NO_SCANOPT_USAGE
    128 static int get_cols PROTO ((void));
    129 
    130 static int get_cols ()
    131 {
    132 	char   *env;
    133 	int     cols = 80;	/* default */
    134 
    135 #ifdef HAVE_NCURSES_H
    136 	initscr ();
    137 	endwin ();
    138 	if (COLS > 0)
    139 		return COLS;
    140 #endif
    141 
    142 	if ((env = getenv ("COLUMNS")) != NULL)
    143 		cols = atoi (env);
    144 
    145 	return cols;
    146 }
    147 #endif
    148 
    149 /* Macro to check for NULL before assigning a value. */
    150 #define SAFE_ASSIGN(ptr,val) \
    151     do{                      \
    152         if((ptr)!=NULL)      \
    153             *(ptr) = val;    \
    154     }while(0)
    155 
    156 /* Macro to assure we reset subscript whenever we adjust s->index.*/
    157 #define INC_INDEX(s,n)     \
    158     do{                    \
    159        (s)->index += (n);  \
    160        (s)->subscript= 0;  \
    161     }while(0)
    162 
    163 scanopt_t *scanopt_init (options, argc, argv, flags)
    164      const optspec_t *options;
    165      int     argc;
    166      char  **argv;
    167      int     flags;
    168 {
    169 	int     i;
    170 	struct _scanopt_t *s;
    171 	s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t));
    172 
    173 	s->options = options;
    174 	s->optc = 0;
    175 	s->argc = argc;
    176 	s->argv = (char **) argv;
    177 	s->index = 1;
    178 	s->subscript = 0;
    179 	s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG);
    180 	s->has_long = 0;
    181 	s->has_short = 0;
    182 
    183 	/* Determine option count. (Find entry with all zeros). */
    184 	s->optc = 0;
    185 	while (options[s->optc].opt_fmt
    186 	       || options[s->optc].r_val || options[s->optc].desc)
    187 		s->optc++;
    188 
    189 	/* Build auxiliary data */
    190 	s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux));
    191 
    192 	for (i = 0; i < s->optc; i++) {
    193 		const char *p, *pname;
    194 		const struct optspec_t *opt;
    195 		struct _aux *aux;
    196 
    197 		opt = s->options + i;
    198 		aux = s->aux + i;
    199 
    200 		aux->flags = ARG_NONE;
    201 
    202 		if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') {
    203 			aux->flags |= IS_LONG;
    204 			pname = opt->opt_fmt + 2;
    205 			s->has_long = 1;
    206 		}
    207 		else {
    208 			pname = opt->opt_fmt + 1;
    209 			s->has_short = 1;
    210 		}
    211 		aux->printlen = strlen (opt->opt_fmt);
    212 
    213 		aux->namelen = 0;
    214 		for (p = pname + 1; *p; p++) {
    215 			/* detect required arg */
    216 			if (*p == '=' || isspace (*p)
    217 			    || !(aux->flags & IS_LONG)) {
    218 				if (aux->namelen == 0)
    219 					aux->namelen = p - pname;
    220 				aux->flags |= ARG_REQ;
    221 				aux->flags &= ~ARG_NONE;
    222 			}
    223 			/* detect optional arg. This overrides required arg. */
    224 			if (*p == '[') {
    225 				if (aux->namelen == 0)
    226 					aux->namelen = p - pname;
    227 				aux->flags &= ~(ARG_REQ | ARG_NONE);
    228 				aux->flags |= ARG_OPT;
    229 				break;
    230 			}
    231 		}
    232 		if (aux->namelen == 0)
    233 			aux->namelen = p - pname;
    234 	}
    235 	return (scanopt_t *) s;
    236 }
    237 
    238 #ifndef NO_SCANOPT_USAGE
    239 /* these structs are for scanopt_usage(). */
    240 struct usg_elem {
    241 	int     idx;
    242 	struct usg_elem *next;
    243 	struct usg_elem *alias;
    244 };
    245 typedef struct usg_elem usg_elem;
    246 
    247 
    248 /* Prints a usage message based on contents of optlist.
    249  * Parameters:
    250  *   scanner  - The scanner, already initialized with scanopt_init().
    251  *   fp       - The file stream to write to.
    252  *   usage    - Text to be prepended to option list.
    253  * Return:  Always returns 0 (zero).
    254  * The output looks something like this:
    255 
    256 [indent][option, alias1, alias2...][indent][description line1
    257                                             description line2...]
    258  */
    259 int     scanopt_usage (scanner, fp, usage)
    260      scanopt_t *scanner;
    261      FILE   *fp;
    262      const char *usage;
    263 {
    264 	struct _scanopt_t *s;
    265 	int     i, columns, indent = 2;
    266 	usg_elem *byr_val = NULL;	/* option indices sorted by r_val */
    267 	usg_elem *store;	/* array of preallocated elements. */
    268 	int     store_idx = 0;
    269 	usg_elem *ue;
    270 	int     maxlen[2];
    271 	int     desccol = 0;
    272 	int     print_run = 0;
    273 
    274 	maxlen[0] = 0;
    275 	maxlen[1] = 0;
    276 
    277 	s = (struct _scanopt_t *) scanner;
    278 
    279 	if (usage) {
    280 		fprintf (fp, "%s\n", usage);
    281 	}
    282 	else {
    283 		/* Find the basename of argv[0] */
    284 		const char *p;
    285 
    286 		p = s->argv[0] + strlen (s->argv[0]);
    287 		while (p != s->argv[0] && *p != '/')
    288 			--p;
    289 		if (*p == '/')
    290 			p++;
    291 
    292 		fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p);
    293 	}
    294 	fprintf (fp, "\n");
    295 
    296 	/* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
    297 	store = (usg_elem *) malloc (s->optc * sizeof (usg_elem));
    298 	for (i = 0; i < s->optc; i++) {
    299 
    300 		/* grab the next preallocate node. */
    301 		ue = store + store_idx++;
    302 		ue->idx = i;
    303 		ue->next = ue->alias = NULL;
    304 
    305 		/* insert into list. */
    306 		if (!byr_val)
    307 			byr_val = ue;
    308 		else {
    309 			int     found_alias = 0;
    310 			usg_elem **ue_curr, **ptr_if_no_alias = NULL;
    311 
    312 			ue_curr = &byr_val;
    313 			while (*ue_curr) {
    314 				if (RVAL (s, (*ue_curr)->idx) ==
    315 				    RVAL (s, ue->idx)) {
    316 					/* push onto the alias list. */
    317 					ue_curr = &((*ue_curr)->alias);
    318 					found_alias = 1;
    319 					break;
    320 				}
    321 				if (!ptr_if_no_alias
    322 				    &&
    323 				    STRCASECMP (NAME (s, (*ue_curr)->idx),
    324 						NAME (s, ue->idx)) > 0) {
    325 					ptr_if_no_alias = ue_curr;
    326 				}
    327 				ue_curr = &((*ue_curr)->next);
    328 			}
    329 			if (!found_alias && ptr_if_no_alias)
    330 				ue_curr = ptr_if_no_alias;
    331 			ue->next = *ue_curr;
    332 			*ue_curr = ue;
    333 		}
    334 	}
    335 
    336 #if 0
    337 	if (1) {
    338 		printf ("ORIGINAL:\n");
    339 		for (i = 0; i < s->optc; i++)
    340 			printf ("%2d: %s\n", i, NAME (s, i));
    341 		printf ("SORTED:\n");
    342 		ue = byr_val;
    343 		while (ue) {
    344 			usg_elem *ue2;
    345 
    346 			printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx));
    347 			for (ue2 = ue->alias; ue2; ue2 = ue2->next)
    348 				printf ("  +---> %2d: %s\n", ue2->idx,
    349 					NAME (s, ue2->idx));
    350 			ue = ue->next;
    351 		}
    352 	}
    353 #endif
    354 
    355 	/* Now build each row of output. */
    356 
    357 	/* first pass calculate how much room we need. */
    358 	for (ue = byr_val; ue; ue = ue->next) {
    359 		usg_elem *ap;
    360 		int     len = 0;
    361 		int     nshort = 0, nlong = 0;
    362 
    363 
    364 #define CALC_LEN(i) do {\
    365           if(FLAGS(s,i) & IS_LONG) \
    366               len +=  (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
    367           else\
    368               len +=  (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
    369         }while(0)
    370 
    371 		if (!(FLAGS (s, ue->idx) & IS_LONG))
    372 			CALC_LEN (ue->idx);
    373 
    374 		/* do short aliases first. */
    375 		for (ap = ue->alias; ap; ap = ap->next) {
    376 			if (FLAGS (s, ap->idx) & IS_LONG)
    377 				continue;
    378 			CALC_LEN (ap->idx);
    379 		}
    380 
    381 		if (FLAGS (s, ue->idx) & IS_LONG)
    382 			CALC_LEN (ue->idx);
    383 
    384 		/* repeat the above loop, this time for long aliases. */
    385 		for (ap = ue->alias; ap; ap = ap->next) {
    386 			if (!(FLAGS (s, ap->idx) & IS_LONG))
    387 				continue;
    388 			CALC_LEN (ap->idx);
    389 		}
    390 
    391 		if (len > maxlen[0])
    392 			maxlen[0] = len;
    393 
    394 		/* It's much easier to calculate length for description column! */
    395 		len = strlen (DESC (s, ue->idx));
    396 		if (len > maxlen[1])
    397 			maxlen[1] = len;
    398 	}
    399 
    400 	/* Determine how much room we have, and how much we will allocate to each col.
    401 	 * Do not address pathological cases. Output will just be ugly. */
    402 	columns = get_cols () - 1;
    403 	if (maxlen[0] + maxlen[1] + indent * 2 > columns) {
    404 		/* col 0 gets whatever it wants. we'll wrap the desc col. */
    405 		maxlen[1] = columns - (maxlen[0] + indent * 2);
    406 		if (maxlen[1] < 14)	/* 14 is arbitrary lower limit on desc width. */
    407 			maxlen[1] = INT_MAX;
    408 	}
    409 	desccol = maxlen[0] + indent * 2;
    410 
    411 #define PRINT_SPACES(fp,n)\
    412     do{\
    413         int _n;\
    414         _n=(n);\
    415         while(_n-- > 0)\
    416             fputc(' ',(fp));\
    417     }while(0)
    418 
    419 
    420 	/* Second pass (same as above loop), this time we print. */
    421 	/* Sloppy hack: We iterate twice. The first time we print short and long options.
    422 	   The second time we print those lines that have ONLY long options. */
    423 	while (print_run++ < 2) {
    424 		for (ue = byr_val; ue; ue = ue->next) {
    425 			usg_elem *ap;
    426 			int     nwords = 0, nchars = 0, has_short = 0;
    427 
    428 /* TODO: get has_short schtick to work */
    429 			has_short = !(FLAGS (s, ue->idx) & IS_LONG);
    430 			for (ap = ue->alias; ap; ap = ap->next) {
    431 				if (!(FLAGS (s, ap->idx) & IS_LONG)) {
    432 					has_short = 1;
    433 					break;
    434 				}
    435 			}
    436 			if ((print_run == 1 && !has_short) ||
    437 			    (print_run == 2 && has_short))
    438 				continue;
    439 
    440 			PRINT_SPACES (fp, indent);
    441 			nchars += indent;
    442 
    443 /* Print, adding a ", " between aliases. */
    444 #define PRINT_IT(i) do{\
    445                   if(nwords++)\
    446                       nchars+=fprintf(fp,", ");\
    447                   nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
    448             }while(0)
    449 
    450 			if (!(FLAGS (s, ue->idx) & IS_LONG))
    451 				PRINT_IT (ue->idx);
    452 
    453 			/* print short aliases first. */
    454 			for (ap = ue->alias; ap; ap = ap->next) {
    455 				if (!(FLAGS (s, ap->idx) & IS_LONG))
    456 					PRINT_IT (ap->idx);
    457 			}
    458 
    459 
    460 			if (FLAGS (s, ue->idx) & IS_LONG)
    461 				PRINT_IT (ue->idx);
    462 
    463 			/* repeat the above loop, this time for long aliases. */
    464 			for (ap = ue->alias; ap; ap = ap->next) {
    465 				if (FLAGS (s, ap->idx) & IS_LONG)
    466 					PRINT_IT (ap->idx);
    467 			}
    468 
    469 			/* pad to desccol */
    470 			PRINT_SPACES (fp, desccol - nchars);
    471 
    472 			/* Print description, wrapped to maxlen[1] columns. */
    473 			if (1) {
    474 				const char *pstart;
    475 
    476 				pstart = DESC (s, ue->idx);
    477 				while (1) {
    478 					int     n = 0;
    479 					const char *lastws = NULL, *p;
    480 
    481 					p = pstart;
    482 
    483 					while (*p && n < maxlen[1]
    484 					       && *p != '\n') {
    485 						if (isspace (*p)
    486 						    || *p == '-') lastws =
    487 								p;
    488 						n++;
    489 						p++;
    490 					}
    491 
    492 					if (!*p) {	/* hit end of desc. done. */
    493 						fprintf (fp, "%s\n",
    494 							 pstart);
    495 						break;
    496 					}
    497 					else if (*p == '\n') {	/* print everything up to here then wrap. */
    498 						fprintf (fp, "%.*s\n", n,
    499 							 pstart);
    500 						PRINT_SPACES (fp, desccol);
    501 						pstart = p + 1;
    502 						continue;
    503 					}
    504 					else {	/* we hit the edge of the screen. wrap at space if possible. */
    505 						if (lastws) {
    506 							fprintf (fp,
    507 								 "%.*s\n",
    508 								 lastws -
    509 								 pstart,
    510 								 pstart);
    511 							pstart =
    512 								lastws + 1;
    513 						}
    514 						else {
    515 							fprintf (fp,
    516 								 "%.*s\n",
    517 								 n,
    518 								 pstart);
    519 							pstart = p + 1;
    520 						}
    521 						PRINT_SPACES (fp, desccol);
    522 						continue;
    523 					}
    524 				}
    525 			}
    526 		}
    527 	}			/* end while */
    528 	free (store);
    529 	return 0;
    530 }
    531 #endif /* no scanopt_usage */
    532 
    533 
    534 static int scanopt_err (s, opt_offset, is_short, err)
    535      struct _scanopt_t *s;
    536      int     opt_offset;
    537      int     is_short;
    538      int     err;
    539 {
    540 	const char *optname = "";
    541 	char    optchar[2];
    542 	const optspec_t *opt = NULL;
    543 
    544 	if (opt_offset >= 0)
    545 		opt = s->options + opt_offset;
    546 
    547 	if (!s->no_err_msg) {
    548 
    549 		if (s->index > 0 && s->index < s->argc) {
    550 			if (is_short) {
    551 				optchar[0] =
    552 					s->argv[s->index][s->subscript];
    553 				optchar[1] = '\0';
    554 				optname = optchar;
    555 			}
    556 			else {
    557 				optname = s->argv[s->index];
    558 			}
    559 		}
    560 
    561 		fprintf (stderr, "%s: ", s->argv[0]);
    562 		switch (err) {
    563 		case SCANOPT_ERR_ARG_NOT_ALLOWED:
    564 			fprintf (stderr,
    565 				 _
    566 				 ("option `%s' doesn't allow an argument\n"),
    567 				 optname);
    568 			break;
    569 		case SCANOPT_ERR_ARG_NOT_FOUND:
    570 			fprintf (stderr,
    571 				 _("option `%s' requires an argument\n"),
    572 				 optname);
    573 			break;
    574 		case SCANOPT_ERR_OPT_AMBIGUOUS:
    575 			fprintf (stderr, _("option `%s' is ambiguous\n"),
    576 				 optname);
    577 			break;
    578 		case SCANOPT_ERR_OPT_UNRECOGNIZED:
    579 			fprintf (stderr, _("Unrecognized option `%s'\n"),
    580 				 optname);
    581 			break;
    582 		default:
    583 			fprintf (stderr, _("Unknown error=(%d)\n"), err);
    584 			break;
    585 		}
    586 	}
    587 	return err;
    588 }
    589 
    590 
    592 /* Internal. Match str against the regex  ^--([^=]+)(=(.*))?
    593  * return 1 if *looks* like a long option.
    594  * 'str' is the only input argument, the rest of the arguments are output only.
    595  * optname will point to str + 2
    596  *
    597  */
    598 static int matchlongopt (str, optname, optlen, arg, arglen)
    599      char   *str;
    600      char  **optname;
    601      int    *optlen;
    602      char  **arg;
    603      int    *arglen;
    604 {
    605 	char   *p;
    606 
    607 	*optname = *arg = (char *) 0;
    608 	*optlen = *arglen = 0;
    609 
    610 	/* Match regex /--./   */
    611 	p = str;
    612 	if (p[0] != '-' || p[1] != '-' || !p[2])
    613 		return 0;
    614 
    615 	p += 2;
    616 	*optname = (char *) p;
    617 
    618 	/* find the end of optname */
    619 	while (*p && *p != '=')
    620 		++p;
    621 
    622 	*optlen = p - *optname;
    623 
    624 	if (!*p)
    625 		/* an option with no '=...' part. */
    626 		return 1;
    627 
    628 
    629 	/* We saw an '=' char. The rest of p is the arg. */
    630 	p++;
    631 	*arg = p;
    632 	while (*p)
    633 		++p;
    634 	*arglen = p - *arg;
    635 
    636 	return 1;
    637 }
    638 
    639 
    641 /* Internal. Look up long or short option by name.
    642  * Long options must match a non-ambiguous prefix, or exact match.
    643  * Short options must be exact.
    644  * Return boolean true if found and no error.
    645  * Error stored in err_code or zero if no error. */
    646 static int find_opt (s, lookup_long, optstart, len, err_code, opt_offset)
    647      struct _scanopt_t *s;
    648      int     lookup_long;
    649      char   *optstart;
    650      int     len;
    651      int    *err_code;
    652      int    *opt_offset;
    653 {
    654 	int     nmatch = 0, lastr_val = 0, i;
    655 
    656 	*err_code = 0;
    657 	*opt_offset = -1;
    658 
    659 	if (!optstart)
    660 		return 0;
    661 
    662 	for (i = 0; i < s->optc; i++) {
    663 		char   *optname;
    664 
    665 		optname =
    666 			(char *) (s->options[i].opt_fmt +
    667 				  (lookup_long ? 2 : 1));
    668 
    669 		if (lookup_long && (s->aux[i].flags & IS_LONG)) {
    670 			if (len > s->aux[i].namelen)
    671 				continue;
    672 
    673 			if (strncmp (optname, optstart, len) == 0) {
    674 				nmatch++;
    675 				*opt_offset = i;
    676 
    677 				/* exact match overrides all. */
    678 				if (len == s->aux[i].namelen) {
    679 					nmatch = 1;
    680 					break;
    681 				}
    682 
    683 				/* ambiguity is ok between aliases. */
    684 				if (lastr_val
    685 				    && lastr_val ==
    686 				    s->options[i].r_val) nmatch--;
    687 				lastr_val = s->options[i].r_val;
    688 			}
    689 		}
    690 		else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) {
    691 			if (optname[0] == optstart[0]) {
    692 				nmatch++;
    693 				*opt_offset = i;
    694 			}
    695 		}
    696 	}
    697 
    698 	if (nmatch == 0) {
    699 		*err_code = SCANOPT_ERR_OPT_UNRECOGNIZED;
    700 		*opt_offset = -1;
    701 	}
    702 	else if (nmatch > 1) {
    703 		*err_code = SCANOPT_ERR_OPT_AMBIGUOUS;
    704 		*opt_offset = -1;
    705 	}
    706 
    707 	return *err_code ? 0 : 1;
    708 }
    709 
    710 
    712 int     scanopt (svoid, arg, optindex)
    713      scanopt_t *svoid;
    714      char  **arg;
    715      int    *optindex;
    716 {
    717 	char   *optname = NULL, *optarg = NULL, *pstart;
    718 	int     namelen = 0, arglen = 0;
    719 	int     errcode = 0, has_next;
    720 	const optspec_t *optp;
    721 	struct _scanopt_t *s;
    722 	struct _aux *auxp;
    723 	int     is_short;
    724 	int     opt_offset = -1;
    725 
    726 	s = (struct _scanopt_t *) svoid;
    727 
    728 	/* Normalize return-parameters. */
    729 	SAFE_ASSIGN (arg, NULL);
    730 	SAFE_ASSIGN (optindex, s->index);
    731 
    732 	if (s->index >= s->argc)
    733 		return 0;
    734 
    735 	/* pstart always points to the start of our current scan. */
    736 	pstart = s->argv[s->index] + s->subscript;
    737 	if (!pstart)
    738 		return 0;
    739 
    740 	if (s->subscript == 0) {
    741 
    742 		/* test for exact match of "--" */
    743 		if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) {
    744 			SAFE_ASSIGN (optindex, s->index + 1);
    745 			INC_INDEX (s, 1);
    746 			return 0;
    747 		}
    748 
    749 		/* Match an opt. */
    750 		if (matchlongopt
    751 		    (pstart, &optname, &namelen, &optarg, &arglen)) {
    752 
    753 			/* it LOOKS like an opt, but is it one?! */
    754 			if (!find_opt
    755 			    (s, 1, optname, namelen, &errcode,
    756 			     &opt_offset)) {
    757 				scanopt_err (s, opt_offset, 0, errcode);
    758 				return errcode;
    759 			}
    760 			/* We handle this below. */
    761 			is_short = 0;
    762 
    763 			/* Check for short opt.  */
    764 		}
    765 		else if (pstart[0] == '-' && pstart[1]) {
    766 			/* Pass through to below. */
    767 			is_short = 1;
    768 			s->subscript++;
    769 			pstart++;
    770 		}
    771 
    772 		else {
    773 			/* It's not an option. We're done. */
    774 			return 0;
    775 		}
    776 	}
    777 
    778 	/* We have to re-check the subscript status because it
    779 	 * may have changed above. */
    780 
    781 	if (s->subscript != 0) {
    782 
    783 		/* we are somewhere in a run of short opts,
    784 		 * e.g., at the 'z' in `tar -xzf` */
    785 
    786 		optname = pstart;
    787 		namelen = 1;
    788 		is_short = 1;
    789 
    790 		if (!find_opt
    791 		    (s, 0, pstart, namelen, &errcode, &opt_offset)) {
    792 			return scanopt_err (s, opt_offset, 1, errcode);
    793 		}
    794 
    795 		optarg = pstart + 1;
    796 		if (!*optarg) {
    797 			optarg = NULL;
    798 			arglen = 0;
    799 		}
    800 		else
    801 			arglen = strlen (optarg);
    802 	}
    803 
    804 	/* At this point, we have a long or short option matched at opt_offset into
    805 	 * the s->options array (and corresponding aux array).
    806 	 * A trailing argument is in {optarg,arglen}, if any.
    807 	 */
    808 
    809 	/* Look ahead in argv[] to see if there is something
    810 	 * that we can use as an argument (if needed). */
    811 	has_next = s->index + 1 < s->argc
    812 		&& strcmp ("--", s->argv[s->index + 1]) != 0;
    813 
    814 	optp = s->options + opt_offset;
    815 	auxp = s->aux + opt_offset;
    816 
    817 	/* case: no args allowed */
    818 	if (auxp->flags & ARG_NONE) {
    819 		if (optarg && !is_short) {
    820 			scanopt_err (s, opt_offset, is_short, errcode =
    821 				     SCANOPT_ERR_ARG_NOT_ALLOWED);
    822 			INC_INDEX (s, 1);
    823 			return errcode;
    824 		}
    825 		else if (!optarg)
    826 			INC_INDEX (s, 1);
    827 		else
    828 			s->subscript++;
    829 		return optp->r_val;
    830 	}
    831 
    832 	/* case: required */
    833 	if (auxp->flags & ARG_REQ) {
    834 		if (!optarg && !has_next)
    835 			return scanopt_err (s, opt_offset, is_short,
    836 					    SCANOPT_ERR_ARG_NOT_FOUND);
    837 
    838 		if (!optarg) {
    839 			/* Let the next argv element become the argument. */
    840 			SAFE_ASSIGN (arg, s->argv[s->index + 1]);
    841 			INC_INDEX (s, 2);
    842 		}
    843 		else {
    844 			SAFE_ASSIGN (arg, (char *) optarg);
    845 			INC_INDEX (s, 1);
    846 		}
    847 		return optp->r_val;
    848 	}
    849 
    850 	/* case: optional */
    851 	if (auxp->flags & ARG_OPT) {
    852 		SAFE_ASSIGN (arg, optarg);
    853 		INC_INDEX (s, 1);
    854 		return optp->r_val;
    855 	}
    856 
    857 
    858 	/* Should not reach here. */
    859 	return 0;
    860 }
    861 
    862 
    863 int     scanopt_destroy (svoid)
    864      scanopt_t *svoid;
    865 {
    866 	struct _scanopt_t *s;
    867 
    868 	s = (struct _scanopt_t *) svoid;
    869 	if (s) {
    870 		if (s->aux)
    871 			free (s->aux);
    872 		free (s);
    873 	}
    874 	return 0;
    875 }
    876 
    877 
    878 /* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */
    879