]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/flex/src/scanopt.c
MFV: r362286
[FreeBSD/FreeBSD.git] / contrib / flex / src / 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 #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
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
81 static int PRINTLEN (struct _scanopt_t *s, int i)
82 {
83         return s->aux[i].printlen;
84 }
85
86 static int RVAL (struct _scanopt_t *s, int i)
87 {
88         return s->options[i].r_val;
89 }
90
91 static int FLAGS (struct _scanopt_t *s, int i)
92 {
93         return s->aux[i].flags;
94 }
95
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
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
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  */
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
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 \f
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  */
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 \f
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. */
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 \f
654
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
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: */