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
43static int STRCASECMP PROTO ((const char *, const char *));
44
45static 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
59struct _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
66struct _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. */
80static const char *NAME PROTO ((struct _scanopt_t *, int));
81static int PRINTLEN PROTO ((struct _scanopt_t *, int));
82static int RVAL PROTO ((struct _scanopt_t *, int));
83static int FLAGS PROTO ((struct _scanopt_t *, int));
84static const char *DESC PROTO ((struct _scanopt_t *, int));
85static int scanopt_err PROTO ((struct _scanopt_t *, int, int, int));
86static int matchlongopt PROTO ((char *, char **, int *, char **, int *));
87static int find_opt
88PROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset));
89
90static 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
98static int PRINTLEN (s, i)
99     struct _scanopt_t *s;
100     int     i;
101{
102	return s->aux[i].printlen;
103}
104
105static int RVAL (s, i)
106     struct _scanopt_t *s;
107     int     i;
108{
109	return s->options[i].r_val;
110}
111
112static int FLAGS (s, i)
113     struct _scanopt_t *s;
114     int     i;
115{
116	return s->aux[i].flags;
117}
118
119static 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
127static int get_cols PROTO ((void));
128
129static 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
162scanopt_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(). */
239struct usg_elem {
240	int     idx;
241	struct usg_elem *next;
242	struct usg_elem *alias;
243};
244typedef 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 */
258int     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
532static 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 */
595static 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. */
642static 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
707int     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
858int     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