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