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