GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: scanopt.c,v 1.5 2015/12/11 00:08:43 mmcc 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 |
||
45 |
static int STRCASECMP PROTO ((const char *, const char *)); |
||
46 |
|||
47 |
static 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_NONE 0x01 |
||
57 |
#define ARG_REQ 0x02 |
||
58 |
#define ARG_OPT 0x04 |
||
59 |
#define IS_LONG 0x08 |
||
60 |
|||
61 |
struct _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 |
|||
68 |
struct _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. */ |
||
82 |
static const char *NAME PROTO ((struct _scanopt_t *, int)); |
||
83 |
static int PRINTLEN PROTO ((struct _scanopt_t *, int)); |
||
84 |
static int RVAL PROTO ((struct _scanopt_t *, int)); |
||
85 |
static int FLAGS PROTO ((struct _scanopt_t *, int)); |
||
86 |
static const char *DESC PROTO ((struct _scanopt_t *, int)); |
||
87 |
static int scanopt_err PROTO ((struct _scanopt_t *, int, int, int)); |
||
88 |
static int matchlongopt PROTO ((char *, char **, int *, char **, int *)); |
||
89 |
static int find_opt |
||
90 |
PROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset)); |
||
91 |
|||
92 |
static 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_LONG) ? 2 : 1); |
||
98 |
} |
||
99 |
|||
100 |
static int PRINTLEN (s, i) |
||
101 |
struct _scanopt_t *s; |
||
102 |
int i; |
||
103 |
{ |
||
104 |
return s->aux[i].printlen; |
||
105 |
} |
||
106 |
|||
107 |
static int RVAL (s, i) |
||
108 |
struct _scanopt_t *s; |
||
109 |
int i; |
||
110 |
{ |
||
111 |
return s->options[i].r_val; |
||
112 |
} |
||
113 |
|||
114 |
static int FLAGS (s, i) |
||
115 |
struct _scanopt_t *s; |
||
116 |
int i; |
||
117 |
{ |
||
118 |
return s->aux[i].flags; |
||
119 |
} |
||
120 |
|||
121 |
static 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 |
||
129 |
static int get_cols PROTO ((void)); |
||
130 |
|||
131 |
static 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) |
||
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) \ |
||
152 |
do{ \ |
||
153 |
if((ptr)!=NULL) \ |
||
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) \ |
||
159 |
do{ \ |
||
160 |
(s)->index += (n); \ |
||
161 |
(s)->subscript= 0; \ |
||
162 |
}while(0) |
||
163 |
|||
164 |
scanopt_t *scanopt_init (options, argc, argv, flags) |
||
165 |
const optspec_t *options; |
||
166 |
int argc; |
||
167 |
char **argv; |
||
168 |
int flags; |
||
169 |
5 |
{ |
|
170 |
int i; |
||
171 |
struct _scanopt_t *s; |
||
172 |
5 |
s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t)); |
|
173 |
|||
174 |
5 |
s->options = options; |
|
175 |
5 |
s->optc = 0; |
|
176 |
5 |
s->argc = argc; |
|
177 |
5 |
s->argv = (char **) argv; |
|
178 |
5 |
s->index = 1; |
|
179 |
5 |
s->subscript = 0; |
|
180 |
5 |
s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG); |
|
181 |
5 |
s->has_long = 0; |
|
182 |
5 |
s->has_short = 0; |
|
183 |
|||
184 |
/* Determine option count. (Find entry with all zeros). */ |
||
185 |
5 |
s->optc = 0; |
|
186 |
✓✓✗✓ ✗✓ |
580 |
while (options[s->optc].opt_fmt |
187 |
|| options[s->optc].r_val || options[s->optc].desc) |
||
188 |
570 |
s->optc++; |
|
189 |
|||
190 |
/* Build auxiliary data */ |
||
191 |
5 |
s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux)); |
|
192 |
|||
193 |
✓✓ | 575 |
for (i = 0; i < s->optc; i++) { |
194 |
const u_char *p, *pname; |
||
195 |
const struct optspec_t *opt; |
||
196 |
struct _aux *aux; |
||
197 |
|||
198 |
570 |
opt = s->options + i; |
|
199 |
570 |
aux = s->aux + i; |
|
200 |
|||
201 |
570 |
aux->flags = ARG_NONE; |
|
202 |
|||
203 |
✓✗✓✓ |
990 |
if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') { |
204 |
420 |
aux->flags |= IS_LONG; |
|
205 |
420 |
pname = (const u_char *)(opt->opt_fmt + 2); |
|
206 |
420 |
s->has_long = 1; |
|
207 |
} |
||
208 |
else { |
||
209 |
150 |
pname = (const u_char *)(opt->opt_fmt + 1); |
|
210 |
150 |
s->has_short = 1; |
|
211 |
} |
||
212 |
570 |
aux->printlen = strlen (opt->opt_fmt); |
|
213 |
|||
214 |
570 |
aux->namelen = 0; |
|
215 |
✓✓ | 4210 |
for (p = pname + 1; *p; p++) { |
216 |
/* detect required arg */ |
||
217 |
✓✓✓✓ ✓✓ |
3655 |
if (*p == '=' || isspace (*p) |
218 |
|| !(aux->flags & IS_LONG)) { |
||
219 |
✓✓ | 140 |
if (aux->namelen == 0) |
220 |
50 |
aux->namelen = p - pname; |
|
221 |
140 |
aux->flags |= ARG_REQ; |
|
222 |
140 |
aux->flags &= ~ARG_NONE; |
|
223 |
} |
||
224 |
/* detect optional arg. This overrides required arg. */ |
||
225 |
✓✓ | 3655 |
if (*p == '[') { |
226 |
✓✓ | 15 |
if (aux->namelen == 0) |
227 |
10 |
aux->namelen = p - pname; |
|
228 |
15 |
aux->flags &= ~(ARG_REQ | ARG_NONE); |
|
229 |
15 |
aux->flags |= ARG_OPT; |
|
230 |
15 |
break; |
|
231 |
} |
||
232 |
} |
||
233 |
✓✓ | 570 |
if (aux->namelen == 0) |
234 |
510 |
aux->namelen = p - pname; |
|
235 |
} |
||
236 |
5 |
return (scanopt_t *) s; |
|
237 |
} |
||
238 |
|||
239 |
#ifndef NO_SCANOPT_USAGE |
||
240 |
/* these structs are for scanopt_usage(). */ |
||
241 |
struct usg_elem { |
||
242 |
int idx; |
||
243 |
struct usg_elem *next; |
||
244 |
struct usg_elem *alias; |
||
245 |
}; |
||
246 |
typedef 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 |
*/ |
||
260 |
int 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; /* 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"), 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; |
||
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; |
||
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 {\ |
||
366 |
if(FLAGS(s,i) & IS_LONG) \ |
||
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_LONG)) |
||
373 |
CALC_LEN (ue->idx); |
||
374 |
|||
375 |
/* do short aliases first. */ |
||
376 |
for (ap = ue->alias; ap; ap = ap->next) { |
||
377 |
if (FLAGS (s, ap->idx) & IS_LONG) |
||
378 |
continue; |
||
379 |
CALC_LEN (ap->idx); |
||
380 |
} |
||
381 |
|||
382 |
if (FLAGS (s, ue->idx) & IS_LONG) |
||
383 |
CALC_LEN (ue->idx); |
||
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_LONG)) |
||
388 |
continue; |
||
389 |
CALC_LEN (ap->idx); |
||
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_MAX; |
||
409 |
} |
||
410 |
desccol = maxlen[0] + indent * 2; |
||
411 |
|||
412 |
#define PRINT_SPACES(fp,n)\ |
||
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_LONG); |
||
431 |
for (ap = ue->alias; ap; ap = ap->next) { |
||
432 |
if (!(FLAGS (s, ap->idx) & IS_LONG)) { |
||
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); |
||
442 |
nchars += indent; |
||
443 |
|||
444 |
/* Print, adding a ", " between aliases. */ |
||
445 |
#define PRINT_IT(i) 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_LONG)) |
||
452 |
PRINT_IT (ue->idx); |
||
453 |
|||
454 |
/* print short aliases first. */ |
||
455 |
for (ap = ue->alias; ap; ap = ap->next) { |
||
456 |
if (!(FLAGS (s, ap->idx) & IS_LONG)) |
||
457 |
PRINT_IT (ap->idx); |
||
458 |
} |
||
459 |
|||
460 |
|||
461 |
if (FLAGS (s, ue->idx) & IS_LONG) |
||
462 |
PRINT_IT (ue->idx); |
||
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_LONG) |
||
467 |
PRINT_IT (ap->idx); |
||
468 |
} |
||
469 |
|||
470 |
/* pad to desccol */ |
||
471 |
PRINT_SPACES (fp, desccol - nchars); |
||
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, *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); |
||
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); |
||
522 |
continue; |
||
523 |
} |
||
524 |
} |
||
525 |
} |
||
526 |
} |
||
527 |
} /* end while */ |
||
528 |
free (store); |
||
529 |
return 0; |
||
530 |
} |
||
531 |
#endif /* no scanopt_usage */ |
||
532 |
|||
533 |
|||
534 |
static 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; |
||
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, "%s: ", s->argv[0]); |
||
562 |
switch (err) { |
||
563 |
case SCANOPT_ERR_ARG_NOT_ALLOWED: |
||
564 |
fprintf (stderr, |
||
565 |
_ |
||
566 |
("option `%s' doesn't allow an argument\n"), |
||
567 |
optname); |
||
568 |
break; |
||
569 |
case SCANOPT_ERR_ARG_NOT_FOUND: |
||
570 |
fprintf (stderr, |
||
571 |
_("option `%s' requires an argument\n"), |
||
572 |
optname); |
||
573 |
break; |
||
574 |
case SCANOPT_ERR_OPT_AMBIGUOUS: |
||
575 |
fprintf (stderr, _("option `%s' is ambiguous\n"), |
||
576 |
optname); |
||
577 |
break; |
||
578 |
case SCANOPT_ERR_OPT_UNRECOGNIZED: |
||
579 |
fprintf (stderr, _("Unrecognized option `%s'\n"), |
||
580 |
optname); |
||
581 |
break; |
||
582 |
default: |
||
583 |
fprintf (stderr, _("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 |
*/ |
||
597 |
static int matchlongopt (str, optname, optlen, arg, arglen) |
||
598 |
char *str; |
||
599 |
char **optname; |
||
600 |
int *optlen; |
||
601 |
char **arg; |
||
602 |
int *arglen; |
||
603 |
11 |
{ |
|
604 |
char *p; |
||
605 |
|||
606 |
11 |
*optname = *arg = (char *) 0; |
|
607 |
11 |
*optlen = *arglen = 0; |
|
608 |
|||
609 |
/* Match regex /--./ */ |
||
610 |
11 |
p = str; |
|
611 |
✓✓✗✓ ✗✗ |
11 |
if (p[0] != '-' || p[1] != '-' || !p[2]) |
612 |
11 |
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. */ |
||
644 |
static 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 |
6 |
{ |
|
652 |
6 |
int nmatch = 0, lastr_val = 0, i; |
|
653 |
|||
654 |
6 |
*err_code = 0; |
|
655 |
6 |
*opt_offset = -1; |
|
656 |
|||
657 |
✗✓ | 6 |
if (!optstart) |
658 |
return 0; |
||
659 |
|||
660 |
✓✓ | 690 |
for (i = 0; i < s->optc; i++) { |
661 |
char *optname; |
||
662 |
|||
663 |
✗✓ | 684 |
optname = |
664 |
(char *) (s->options[i].opt_fmt + |
||
665 |
(lookup_long ? 2 : 1)); |
||
666 |
|||
667 |
✗✓✗✗ |
684 |
if (lookup_long && (s->aux[i].flags & IS_LONG)) { |
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 |
✓✗✓✓ |
684 |
else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) { |
689 |
✓✓ | 180 |
if (optname[0] == optstart[0]) { |
690 |
6 |
nmatch++; |
|
691 |
6 |
*opt_offset = i; |
|
692 |
} |
||
693 |
} |
||
694 |
} |
||
695 |
|||
696 |
✗✓ | 6 |
if (nmatch == 0) { |
697 |
*err_code = SCANOPT_ERR_OPT_UNRECOGNIZED; |
||
698 |
*opt_offset = -1; |
||
699 |
} |
||
700 |
✗✓ | 6 |
else if (nmatch > 1) { |
701 |
*err_code = SCANOPT_ERR_OPT_AMBIGUOUS; |
||
702 |
*opt_offset = -1; |
||
703 |
} |
||
704 |
|||
705 |
6 |
return *err_code ? 0 : 1; |
|
706 |
} |
||
707 |
|||
708 |
|||
709 |
int scanopt (svoid, arg, optindex) |
||
710 |
scanopt_t *svoid; |
||
711 |
char **arg; |
||
712 |
int *optindex; |
||
713 |
11 |
{ |
|
714 |
11 |
char *optname = NULL, *optarg = NULL, *pstart; |
|
715 |
11 |
int namelen = 0, arglen = 0; |
|
716 |
11 |
int errcode = 0, has_next; |
|
717 |
const optspec_t *optp; |
||
718 |
struct _scanopt_t *s; |
||
719 |
struct _aux *auxp; |
||
720 |
int is_short; |
||
721 |
11 |
int opt_offset = -1; |
|
722 |
|||
723 |
11 |
s = (struct _scanopt_t *) svoid; |
|
724 |
|||
725 |
/* Normalize return-parameters. */ |
||
726 |
✓✗ | 11 |
SAFE_ASSIGN (arg, NULL); |
727 |
✓✗ | 11 |
SAFE_ASSIGN (optindex, s->index); |
728 |
|||
729 |
✗✓ | 11 |
if (s->index >= s->argc) |
730 |
return 0; |
||
731 |
|||
732 |
/* pstart always points to the start of our current scan. */ |
||
733 |
11 |
pstart = s->argv[s->index] + s->subscript; |
|
734 |
✗✓ | 11 |
if (!pstart) |
735 |
return 0; |
||
736 |
|||
737 |
✓✗ | 11 |
if (s->subscript == 0) { |
738 |
|||
739 |
/* test for exact match of "--" */ |
||
740 |
✓✓✗✓ ✗✗ |
11 |
if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) { |
741 |
SAFE_ASSIGN (optindex, s->index + 1); |
||
742 |
INC_INDEX (s, 1); |
||
743 |
return 0; |
||
744 |
} |
||
745 |
|||
746 |
/* Match an opt. */ |
||
747 |
✗✓ | 11 |
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 |
✓✓✓✗ |
17 |
else if (pstart[0] == '-' && pstart[1]) { |
763 |
/* Pass through to below. */ |
||
764 |
6 |
is_short = 1; |
|
765 |
6 |
s->subscript++; |
|
766 |
6 |
pstart++; |
|
767 |
} |
||
768 |
|||
769 |
else { |
||
770 |
/* It's not an option. We're done. */ |
||
771 |
5 |
return 0; |
|
772 |
} |
||
773 |
} |
||
774 |
|||
775 |
/* We have to re-check the subscript status because it |
||
776 |
* may have changed above. */ |
||
777 |
|||
778 |
✓✗ | 6 |
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 |
6 |
optname = pstart; |
|
784 |
6 |
namelen = 1; |
|
785 |
6 |
is_short = 1; |
|
786 |
|||
787 |
✗✓ | 6 |
if (!find_opt |
788 |
(s, 0, pstart, namelen, &errcode, &opt_offset)) { |
||
789 |
return scanopt_err (s, opt_offset, 1, errcode); |
||
790 |
} |
||
791 |
|||
792 |
6 |
optarg = pstart + 1; |
|
793 |
✓✗ | 6 |
if (!*optarg) { |
794 |
6 |
optarg = NULL; |
|
795 |
6 |
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 |
✓✗✓✗ |
6 |
has_next = s->index + 1 < s->argc |
809 |
&& strcmp ("--", s->argv[s->index + 1]) != 0; |
||
810 |
|||
811 |
6 |
optp = s->options + opt_offset; |
|
812 |
6 |
auxp = s->aux + opt_offset; |
|
813 |
|||
814 |
/* case: no args allowed */ |
||
815 |
✓✗ | 6 |
if (auxp->flags & ARG_NONE) { |
816 |
✗✓ | 6 |
if (optarg && !is_short) { |
817 |
scanopt_err (s, opt_offset, is_short, errcode = |
||
818 |
SCANOPT_ERR_ARG_NOT_ALLOWED); |
||
819 |
INC_INDEX (s, 1); |
||
820 |
return errcode; |
||
821 |
} |
||
822 |
✓✗ | 6 |
else if (!optarg) |
823 |
6 |
INC_INDEX (s, 1); |
|
824 |
else |
||
825 |
s->subscript++; |
||
826 |
6 |
return optp->r_val; |
|
827 |
} |
||
828 |
|||
829 |
/* case: required */ |
||
830 |
if (auxp->flags & ARG_REQ) { |
||
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]); |
||
838 |
INC_INDEX (s, 2); |
||
839 |
} |
||
840 |
else { |
||
841 |
SAFE_ASSIGN (arg, (char *) optarg); |
||
842 |
INC_INDEX (s, 1); |
||
843 |
} |
||
844 |
return optp->r_val; |
||
845 |
} |
||
846 |
|||
847 |
/* case: optional */ |
||
848 |
if (auxp->flags & ARG_OPT) { |
||
849 |
SAFE_ASSIGN (arg, optarg); |
||
850 |
INC_INDEX (s, 1); |
||
851 |
return optp->r_val; |
||
852 |
} |
||
853 |
|||
854 |
|||
855 |
/* Should not reach here. */ |
||
856 |
return 0; |
||
857 |
} |
||
858 |
|||
859 |
|||
860 |
int scanopt_destroy (svoid) |
||
861 |
scanopt_t *svoid; |
||
862 |
5 |
{ |
|
863 |
struct _scanopt_t *s; |
||
864 |
|||
865 |
5 |
s = (struct _scanopt_t *) svoid; |
|
866 |
✓✗ | 5 |
if (s) { |
867 |
5 |
free(s->aux); |
|
868 |
5 |
free (s); |
|
869 |
} |
||
870 |
5 |
return 0; |
|
871 |
} |
Generated by: GCOVR (Version 3.3) |