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