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