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