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