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