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