Coverage Report

Created: 2025-10-13 07:06

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