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: */ |