]>
Commit | Line | Data |
---|---|---|
9385eb3d A |
1 | /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ |
2 | /* $FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.2 2002/10/16 22:18:42 alfred Exp $ */ | |
3 | ||
4 | /*- | |
5 | * Copyright (c) 2000 The NetBSD Foundation, Inc. | |
6 | * All rights reserved. | |
7 | * | |
8 | * This code is derived from software contributed to The NetBSD Foundation | |
9 | * by Dieter Baron and Thomas Klausner. | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or without | |
12 | * modification, are permitted provided that the following conditions | |
13 | * are met: | |
14 | * 1. Redistributions of source code must retain the above copyright | |
15 | * notice, this list of conditions and the following disclaimer. | |
16 | * 2. Redistributions in binary form must reproduce the above copyright | |
17 | * notice, this list of conditions and the following disclaimer in the | |
18 | * documentation and/or other materials provided with the distribution. | |
19 | * 3. All advertising materials mentioning features or use of this software | |
20 | * must display the following acknowledgement: | |
21 | * This product includes software developed by the NetBSD | |
22 | * Foundation, Inc. and its contributors. | |
23 | * 4. Neither the name of The NetBSD Foundation nor the names of its | |
24 | * contributors may be used to endorse or promote products derived | |
25 | * from this software without specific prior written permission. | |
26 | * | |
27 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | |
28 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
29 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
30 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | |
31 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
37 | * POSSIBILITY OF SUCH DAMAGE. | |
38 | */ | |
39 | ||
40 | #include <sys/cdefs.h> | |
41 | #if defined(LIBC_SCCS) && !defined(lint) | |
42 | __RCSID("$NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $"); | |
43 | #endif /* LIBC_SCCS and not lint */ | |
44 | ||
45 | #include "namespace.h" | |
46 | ||
47 | #include <assert.h> | |
48 | #include <err.h> | |
49 | #include <errno.h> | |
50 | #include <getopt.h> | |
51 | #include <stdlib.h> | |
52 | #include <string.h> | |
53 | ||
54 | /* not part of the original file */ | |
55 | #ifndef _DIAGASSERT | |
56 | #define _DIAGASSERT(X) | |
57 | #endif | |
58 | ||
59 | #if HAVE_CONFIG_H && !HAVE_GETOPT_LONG && !HAVE_DECL_OPTIND | |
60 | #define REPLACE_GETOPT | |
61 | #endif | |
62 | ||
63 | #ifdef REPLACE_GETOPT | |
64 | #ifdef __weak_alias | |
65 | __weak_alias(getopt,_getopt) | |
66 | #endif | |
67 | int opterr = 1; /* if error message should be printed */ | |
68 | int optind = 1; /* index into parent argv vector */ | |
69 | int optopt = '?'; /* character checked for validity */ | |
70 | int optreset; /* reset getopt */ | |
71 | char *optarg; /* argument associated with option */ | |
72 | #elif HAVE_CONFIG_H && !HAVE_DECL_OPTRESET | |
73 | static int optreset; | |
74 | #endif | |
75 | ||
76 | #ifdef __weak_alias | |
77 | __weak_alias(getopt_long,_getopt_long) | |
78 | #endif | |
79 | ||
80 | #if !HAVE_GETOPT_LONG | |
81 | #define IGNORE_FIRST (*options == '-' || *options == '+') | |
82 | #define PRINT_ERROR ((opterr) && ((*options != ':') \ | |
83 | || (IGNORE_FIRST && options[1] != ':'))) | |
84 | #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) | |
85 | #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) | |
86 | /* XXX: GNU ignores PC if *options == '-' */ | |
87 | #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') | |
88 | ||
89 | /* return values */ | |
90 | #define BADCH (int)'?' | |
91 | #define BADARG ((IGNORE_FIRST && options[1] == ':') \ | |
92 | || (*options == ':') ? (int)':' : (int)'?') | |
93 | #define INORDER (int)1 | |
94 | ||
95 | #define EMSG "" | |
96 | ||
97 | static int getopt_internal(int, char * const *, const char *); | |
98 | static int gcd(int, int); | |
99 | static void permute_args(int, int, int, char * const *); | |
100 | ||
101 | static char *place = EMSG; /* option letter processing */ | |
102 | ||
103 | /* XXX: set optreset to 1 rather than these two */ | |
104 | static int nonopt_start = -1; /* first non option argument (for permute) */ | |
105 | static int nonopt_end = -1; /* first option after non options (for permute) */ | |
106 | ||
107 | /* Error messages */ | |
108 | static const char recargchar[] = "option requires an argument -- %c"; | |
109 | static const char recargstring[] = "option requires an argument -- %s"; | |
110 | static const char ambig[] = "ambiguous option -- %.*s"; | |
111 | static const char noarg[] = "option doesn't take an argument -- %.*s"; | |
112 | static const char illoptchar[] = "unknown option -- %c"; | |
113 | static const char illoptstring[] = "unknown option -- %s"; | |
114 | ||
115 | ||
116 | /* | |
117 | * Compute the greatest common divisor of a and b. | |
118 | */ | |
119 | static int | |
120 | gcd(a, b) | |
121 | int a; | |
122 | int b; | |
123 | { | |
124 | int c; | |
125 | ||
126 | c = a % b; | |
127 | while (c != 0) { | |
128 | a = b; | |
129 | b = c; | |
130 | c = a % b; | |
131 | } | |
132 | ||
133 | return b; | |
134 | } | |
135 | ||
136 | /* | |
137 | * Exchange the block from nonopt_start to nonopt_end with the block | |
138 | * from nonopt_end to opt_end (keeping the same order of arguments | |
139 | * in each block). | |
140 | */ | |
141 | static void | |
142 | permute_args(panonopt_start, panonopt_end, opt_end, nargv) | |
143 | int panonopt_start; | |
144 | int panonopt_end; | |
145 | int opt_end; | |
146 | char * const *nargv; | |
147 | { | |
148 | int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; | |
149 | char *swap; | |
150 | ||
151 | _DIAGASSERT(nargv != NULL); | |
152 | ||
153 | /* | |
154 | * compute lengths of blocks and number and size of cycles | |
155 | */ | |
156 | nnonopts = panonopt_end - panonopt_start; | |
157 | nopts = opt_end - panonopt_end; | |
158 | ncycle = gcd(nnonopts, nopts); | |
159 | cyclelen = (opt_end - panonopt_start) / ncycle; | |
160 | ||
161 | for (i = 0; i < ncycle; i++) { | |
162 | cstart = panonopt_end+i; | |
163 | pos = cstart; | |
164 | for (j = 0; j < cyclelen; j++) { | |
165 | if (pos >= panonopt_end) | |
166 | pos -= nnonopts; | |
167 | else | |
168 | pos += nopts; | |
169 | swap = nargv[pos]; | |
170 | /* LINTED const cast */ | |
171 | ((char **) nargv)[pos] = nargv[cstart]; | |
172 | /* LINTED const cast */ | |
173 | ((char **)nargv)[cstart] = swap; | |
174 | } | |
175 | } | |
176 | } | |
177 | ||
178 | /* | |
179 | * getopt_internal -- | |
180 | * Parse argc/argv argument vector. Called by user level routines. | |
181 | * Returns -2 if -- is found (can be long option or end of options marker). | |
182 | */ | |
183 | static int | |
184 | getopt_internal(nargc, nargv, options) | |
185 | int nargc; | |
186 | char * const *nargv; | |
187 | const char *options; | |
188 | { | |
189 | char *oli; /* option letter list index */ | |
190 | int optchar; | |
191 | ||
192 | _DIAGASSERT(nargv != NULL); | |
193 | _DIAGASSERT(options != NULL); | |
194 | ||
195 | optarg = NULL; | |
196 | ||
197 | /* | |
198 | * XXX Some programs (like rsyncd) expect to be able to | |
199 | * XXX re-initialize optind to 0 and have getopt_long(3) | |
200 | * XXX properly function again. Work around this braindamage. | |
201 | */ | |
202 | if (optind == 0) | |
203 | optind = 1; | |
204 | ||
205 | if (optreset) | |
206 | nonopt_start = nonopt_end = -1; | |
207 | start: | |
208 | if (optreset || !*place) { /* update scanning pointer */ | |
209 | optreset = 0; | |
210 | if (optind >= nargc) { /* end of argument vector */ | |
211 | place = EMSG; | |
212 | if (nonopt_end != -1) { | |
213 | /* do permutation, if we have to */ | |
214 | permute_args(nonopt_start, nonopt_end, | |
215 | optind, nargv); | |
216 | optind -= nonopt_end - nonopt_start; | |
217 | } | |
218 | else if (nonopt_start != -1) { | |
219 | /* | |
220 | * If we skipped non-options, set optind | |
221 | * to the first of them. | |
222 | */ | |
223 | optind = nonopt_start; | |
224 | } | |
225 | nonopt_start = nonopt_end = -1; | |
226 | return -1; | |
227 | } | |
228 | if ((*(place = nargv[optind]) != '-') | |
229 | || (place[1] == '\0')) { /* found non-option */ | |
230 | place = EMSG; | |
231 | if (IN_ORDER) { | |
232 | /* | |
233 | * GNU extension: | |
234 | * return non-option as argument to option 1 | |
235 | */ | |
236 | optarg = nargv[optind++]; | |
237 | return INORDER; | |
238 | } | |
239 | if (!PERMUTE) { | |
240 | /* | |
241 | * if no permutation wanted, stop parsing | |
242 | * at first non-option | |
243 | */ | |
244 | return -1; | |
245 | } | |
246 | /* do permutation */ | |
247 | if (nonopt_start == -1) | |
248 | nonopt_start = optind; | |
249 | else if (nonopt_end != -1) { | |
250 | permute_args(nonopt_start, nonopt_end, | |
251 | optind, nargv); | |
252 | nonopt_start = optind - | |
253 | (nonopt_end - nonopt_start); | |
254 | nonopt_end = -1; | |
255 | } | |
256 | optind++; | |
257 | /* process next argument */ | |
258 | goto start; | |
259 | } | |
260 | if (nonopt_start != -1 && nonopt_end == -1) | |
261 | nonopt_end = optind; | |
262 | if (place[1] && *++place == '-') { /* found "--" */ | |
263 | place++; | |
264 | return -2; | |
265 | } | |
266 | } | |
267 | if ((optchar = (int)*place++) == (int)':' || | |
268 | (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { | |
269 | /* option letter unknown or ':' */ | |
270 | if (!*place) | |
271 | ++optind; | |
272 | if (PRINT_ERROR) | |
273 | warnx(illoptchar, optchar); | |
274 | optopt = optchar; | |
275 | return BADCH; | |
276 | } | |
277 | if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ | |
278 | /* XXX: what if no long options provided (called by getopt)? */ | |
279 | if (*place) | |
280 | return -2; | |
281 | ||
282 | if (++optind >= nargc) { /* no arg */ | |
283 | place = EMSG; | |
284 | if (PRINT_ERROR) | |
285 | warnx(recargchar, optchar); | |
286 | optopt = optchar; | |
287 | return BADARG; | |
288 | } else /* white space */ | |
289 | place = nargv[optind]; | |
290 | /* | |
291 | * Handle -W arg the same as --arg (which causes getopt to | |
292 | * stop parsing). | |
293 | */ | |
294 | return -2; | |
295 | } | |
296 | if (*++oli != ':') { /* doesn't take argument */ | |
297 | if (!*place) | |
298 | ++optind; | |
299 | } else { /* takes (optional) argument */ | |
300 | optarg = NULL; | |
301 | if (*place) /* no white space */ | |
302 | optarg = place; | |
303 | /* XXX: disable test for :: if PC? (GNU doesn't) */ | |
304 | else if (oli[1] != ':') { /* arg not optional */ | |
305 | if (++optind >= nargc) { /* no arg */ | |
306 | place = EMSG; | |
307 | if (PRINT_ERROR) | |
308 | warnx(recargchar, optchar); | |
309 | optopt = optchar; | |
310 | return BADARG; | |
311 | } else | |
312 | optarg = nargv[optind]; | |
313 | } | |
314 | place = EMSG; | |
315 | ++optind; | |
316 | } | |
317 | /* dump back option letter */ | |
318 | return optchar; | |
319 | } | |
320 | ||
321 | #ifdef REPLACE_GETOPT | |
322 | /* | |
323 | * getopt -- | |
324 | * Parse argc/argv argument vector. | |
325 | * | |
326 | * [eventually this will replace the real getopt] | |
327 | */ | |
328 | int | |
329 | getopt(nargc, nargv, options) | |
330 | int nargc; | |
331 | char * const *nargv; | |
332 | const char *options; | |
333 | { | |
334 | int retval; | |
335 | ||
336 | _DIAGASSERT(nargv != NULL); | |
337 | _DIAGASSERT(options != NULL); | |
338 | ||
339 | if ((retval = getopt_internal(nargc, nargv, options)) == -2) { | |
340 | ++optind; | |
341 | /* | |
342 | * We found an option (--), so if we skipped non-options, | |
343 | * we have to permute. | |
344 | */ | |
345 | if (nonopt_end != -1) { | |
346 | permute_args(nonopt_start, nonopt_end, optind, | |
347 | nargv); | |
348 | optind -= nonopt_end - nonopt_start; | |
349 | } | |
350 | nonopt_start = nonopt_end = -1; | |
351 | retval = -1; | |
352 | } | |
353 | return retval; | |
354 | } | |
355 | #endif | |
356 | ||
357 | /* | |
358 | * getopt_long -- | |
359 | * Parse argc/argv argument vector. | |
360 | */ | |
361 | int | |
362 | getopt_long(nargc, nargv, options, long_options, idx) | |
363 | int nargc; | |
364 | char * const *nargv; | |
365 | const char *options; | |
366 | const struct option *long_options; | |
367 | int *idx; | |
368 | { | |
369 | int retval; | |
370 | ||
371 | _DIAGASSERT(nargv != NULL); | |
372 | _DIAGASSERT(options != NULL); | |
373 | _DIAGASSERT(long_options != NULL); | |
374 | /* idx may be NULL */ | |
375 | ||
376 | if ((retval = getopt_internal(nargc, nargv, options)) == -2) { | |
377 | char *current_argv, *has_equal; | |
378 | size_t current_argv_len; | |
379 | int i, match; | |
380 | ||
381 | current_argv = place; | |
382 | match = -1; | |
383 | ||
384 | optind++; | |
385 | place = EMSG; | |
386 | ||
387 | if (*current_argv == '\0') { /* found "--" */ | |
388 | /* | |
389 | * We found an option (--), so if we skipped | |
390 | * non-options, we have to permute. | |
391 | */ | |
392 | if (nonopt_end != -1) { | |
393 | permute_args(nonopt_start, nonopt_end, | |
394 | optind, nargv); | |
395 | optind -= nonopt_end - nonopt_start; | |
396 | } | |
397 | nonopt_start = nonopt_end = -1; | |
398 | return -1; | |
399 | } | |
400 | if ((has_equal = strchr(current_argv, '=')) != NULL) { | |
401 | /* argument found (--option=arg) */ | |
402 | current_argv_len = has_equal - current_argv; | |
403 | has_equal++; | |
404 | } else | |
405 | current_argv_len = strlen(current_argv); | |
406 | ||
407 | for (i = 0; long_options[i].name; i++) { | |
408 | /* find matching long option */ | |
409 | if (strncmp(current_argv, long_options[i].name, | |
410 | current_argv_len)) | |
411 | continue; | |
412 | ||
413 | if (strlen(long_options[i].name) == | |
414 | (unsigned)current_argv_len) { | |
415 | /* exact match */ | |
416 | match = i; | |
417 | break; | |
418 | } | |
419 | if (match == -1) /* partial match */ | |
420 | match = i; | |
421 | else { | |
422 | /* ambiguous abbreviation */ | |
423 | if (PRINT_ERROR) | |
424 | warnx(ambig, (int)current_argv_len, | |
425 | current_argv); | |
426 | optopt = 0; | |
427 | return BADCH; | |
428 | } | |
429 | } | |
430 | if (match != -1) { /* option found */ | |
431 | if (long_options[match].has_arg == no_argument | |
432 | && has_equal) { | |
433 | if (PRINT_ERROR) | |
434 | warnx(noarg, (int)current_argv_len, | |
435 | current_argv); | |
436 | /* | |
437 | * XXX: GNU sets optopt to val regardless of | |
438 | * flag | |
439 | */ | |
440 | if (long_options[match].flag == NULL) | |
441 | optopt = long_options[match].val; | |
442 | else | |
443 | optopt = 0; | |
444 | return BADARG; | |
445 | } | |
446 | if (long_options[match].has_arg == required_argument || | |
447 | long_options[match].has_arg == optional_argument) { | |
448 | if (has_equal) | |
449 | optarg = has_equal; | |
450 | else if (long_options[match].has_arg == | |
451 | required_argument) { | |
452 | /* | |
453 | * optional argument doesn't use | |
454 | * next nargv | |
455 | */ | |
456 | optarg = nargv[optind++]; | |
457 | } | |
458 | } | |
459 | if ((long_options[match].has_arg == required_argument) | |
460 | && (optarg == NULL)) { | |
461 | /* | |
462 | * Missing argument; leading ':' | |
463 | * indicates no error should be generated | |
464 | */ | |
465 | if (PRINT_ERROR) | |
466 | warnx(recargstring, current_argv); | |
467 | /* | |
468 | * XXX: GNU sets optopt to val regardless | |
469 | * of flag | |
470 | */ | |
471 | if (long_options[match].flag == NULL) | |
472 | optopt = long_options[match].val; | |
473 | else | |
474 | optopt = 0; | |
475 | --optind; | |
476 | return BADARG; | |
477 | } | |
478 | } else { /* unknown option */ | |
479 | if (PRINT_ERROR) | |
480 | warnx(illoptstring, current_argv); | |
481 | optopt = 0; | |
482 | return BADCH; | |
483 | } | |
484 | if (long_options[match].flag) { | |
485 | *long_options[match].flag = long_options[match].val; | |
486 | retval = 0; | |
487 | } else | |
488 | retval = long_options[match].val; | |
489 | if (idx) | |
490 | *idx = match; | |
491 | } | |
492 | return retval; | |
493 | } | |
494 | #endif /* !GETOPT_LONG */ |