Coverage Report

Created: 2026-07-16 07:16

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