Bug Summary

File:src/usr.bin/lex/scanopt.c
Warning:line 203, column 7
Array access (via field 'opt_fmt') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

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