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