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