summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/libc/stdlib/getopt_long.c429
1 files changed, 208 insertions, 221 deletions
diff --git a/src/lib/libc/stdlib/getopt_long.c b/src/lib/libc/stdlib/getopt_long.c
index 4fc1874367..130c1d8bfa 100644
--- a/src/lib/libc/stdlib/getopt_long.c
+++ b/src/lib/libc/stdlib/getopt_long.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $ */ 1/* $OpenBSD: getopt_long.c,v 1.3 2002/12/05 21:45:01 millert Exp $ */
2/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ 2/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
3 3
4/*- 4/*-
@@ -38,7 +38,7 @@
38 */ 38 */
39 39
40#if defined(LIBC_SCCS) && !defined(lint) 40#if defined(LIBC_SCCS) && !defined(lint)
41static char *rcsid = "$OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $"; 41static char *rcsid = "$OpenBSD: getopt_long.c,v 1.3 2002/12/05 21:45:01 millert Exp $";
42#endif /* LIBC_SCCS and not lint */ 42#endif /* LIBC_SCCS and not lint */
43 43
44#include <err.h> 44#include <err.h>
@@ -57,9 +57,9 @@ char *optarg; /* argument associated with option */
57 57
58#define PRINT_ERROR ((opterr) && (*options != ':')) 58#define PRINT_ERROR ((opterr) && (*options != ':'))
59 59
60#define FLAG_PERMUTE 0x01 60#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
61#define FLAG_ALLARGS 0x02 61#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
62#define FLAG_LONGONLY 0x04 62#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
63 63
64/* return values */ 64/* return values */
65#define BADCH (int)'?' 65#define BADCH (int)'?'
@@ -68,9 +68,10 @@ char *optarg; /* argument associated with option */
68 68
69#define EMSG "" 69#define EMSG ""
70 70
71static int getopt_internal(int, char * const *, const char *, int); 71static int getopt_internal(int, char * const *, const char *,
72static int getopt_long_internal(int, char * const *, const char *, 72 const struct option *, int *, int);
73 const struct option *, int *, int); 73static int parse_long_options(int, char * const *, const char *,
74 const struct option *, int *, int);
74static int gcd(int, int); 75static int gcd(int, int);
75static void permute_args(int, int, int, char * const *); 76static void permute_args(int, int, int, char * const *);
76 77
@@ -92,9 +93,7 @@ static const char illoptstring[] = "unknown option -- %s";
92 * Compute the greatest common divisor of a and b. 93 * Compute the greatest common divisor of a and b.
93 */ 94 */
94static int 95static int
95gcd(a, b) 96gcd(int a, int b)
96 int a;
97 int b;
98{ 97{
99 int c; 98 int c;
100 99
@@ -105,7 +104,7 @@ gcd(a, b)
105 c = a % b; 104 c = a % b;
106 } 105 }
107 106
108 return b; 107 return (b);
109} 108}
110 109
111/* 110/*
@@ -114,11 +113,8 @@ gcd(a, b)
114 * in each block). 113 * in each block).
115 */ 114 */
116static void 115static void
117permute_args(panonopt_start, panonopt_end, opt_end, nargv) 116permute_args(int panonopt_start, int panonopt_end, int opt_end,
118 int panonopt_start; 117 char * const *nargv)
119 int panonopt_end;
120 int opt_end;
121 char * const *nargv;
122{ 118{
123 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 119 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
124 char *swap; 120 char *swap;
@@ -149,23 +145,154 @@ permute_args(panonopt_start, panonopt_end, opt_end, nargv)
149} 145}
150 146
151/* 147/*
148 * parse_long_options --
149 * Parse long options in argc/argv argument vector.
150 * Returns -2 if long_only is set and the current option could be a short
151 * (single character) option instead.
152 */
153static int
154parse_long_options(int nargc, char * const *nargv, const char *options,
155 const struct option *long_options, int *idx, int long_only)
156{
157 char *current_argv, *has_equal;
158 size_t current_argv_len;
159 int i, match;
160
161 current_argv = place;
162 match = -1;
163
164 optind++;
165
166 if ((has_equal = strchr(current_argv, '=')) != NULL) {
167 /* argument found (--option=arg) */
168 current_argv_len = has_equal - current_argv;
169 has_equal++;
170 } else
171 current_argv_len = strlen(current_argv);
172
173 for (i = 0; long_options[i].name; i++) {
174 /* find matching long option */
175 if (strncmp(current_argv, long_options[i].name,
176 current_argv_len))
177 continue;
178
179 if (strlen(long_options[i].name) == current_argv_len) {
180 /* exact match */
181 match = i;
182 break;
183 }
184 /*
185 * Don't try a partial match of a short option when in
186 * long_only mode. Otherwise there is a potential conflict
187 * between partial matches and short options.
188 */
189 if (long_only && current_argv_len == 1)
190 continue;
191
192 if (match == -1) /* partial match */
193 match = i;
194 else {
195 /* ambiguous abbreviation */
196 if (PRINT_ERROR)
197 warnx(ambig, (int)current_argv_len,
198 current_argv);
199 optopt = 0;
200 return (BADCH);
201 }
202 }
203 if (match != -1) { /* option found */
204 if (long_options[match].has_arg == no_argument
205 && has_equal) {
206 if (PRINT_ERROR)
207 warnx(noarg, (int)current_argv_len,
208 current_argv);
209 /*
210 * XXX: GNU sets optopt to val regardless of flag
211 */
212 if (long_options[match].flag == NULL)
213 optopt = long_options[match].val;
214 else
215 optopt = 0;
216 return (BADARG);
217 }
218 if (long_options[match].has_arg == required_argument ||
219 long_options[match].has_arg == optional_argument) {
220 if (has_equal)
221 optarg = has_equal;
222 else if (long_options[match].has_arg ==
223 required_argument) {
224 /*
225 * optional argument doesn't use next nargv
226 */
227 optarg = nargv[optind++];
228 }
229 }
230 if ((long_options[match].has_arg == required_argument)
231 && (optarg == NULL)) {
232 /*
233 * Missing argument; leading ':' indicates no error
234 * should be generated.
235 */
236 if (PRINT_ERROR)
237 warnx(recargstring,
238 current_argv);
239 /*
240 * XXX: GNU sets optopt to val regardless of flag
241 */
242 if (long_options[match].flag == NULL)
243 optopt = long_options[match].val;
244 else
245 optopt = 0;
246 --optind;
247 return (BADARG);
248 }
249 } else { /* unknown option */
250 if (long_only) {
251 --optind;
252 return (-2);
253 }
254 if (PRINT_ERROR)
255 warnx(illoptstring, current_argv);
256 optopt = 0;
257 return (BADCH);
258 }
259 if (idx)
260 *idx = match;
261 if (long_options[match].flag) {
262 *long_options[match].flag = long_options[match].val;
263 return (0);
264 } else
265 return (long_options[match].val);
266}
267
268/*
152 * getopt_internal -- 269 * getopt_internal --
153 * Parse argc/argv argument vector. Called by user level routines. 270 * Parse argc/argv argument vector. Called by user level routines.
154 * Returns -2 if -- is found (can be long option or end of options marker).
155 */ 271 */
156static int 272static int
157getopt_internal(nargc, nargv, options, flags) 273getopt_internal(int nargc, char * const *nargv, const char *options,
158 int nargc; 274 const struct option *long_options, int *idx, int flags)
159 char * const *nargv;
160 const char *options;
161 int flags;
162{ 275{
163 char *oli; /* option letter list index */ 276 char *oli; /* option letter list index */
164 int optchar; 277 int optchar;
278 static int posixly_correct = -1;
165 279
166 optarg = NULL; 280 optarg = NULL;
167 281
168 /* 282 /*
283 * Disable GNU extensions if POSIXLY_CORRECT is set or options
284 * string begins with a '+'.
285 */
286 if (posixly_correct == -1)
287 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
288 if (posixly_correct || *options == '+')
289 flags &= ~FLAG_PERMUTE;
290 else if (*options == '-')
291 flags |= FLAG_ALLARGS;
292 if (*options == '+' || *options == '-')
293 options++;
294
295 /*
169 * XXX Some programs (like rsyncd) expect to be able to 296 * XXX Some programs (like rsyncd) expect to be able to
170 * XXX re-initialize optind to 0 and have getopt_long(3) 297 * XXX re-initialize optind to 0 and have getopt_long(3)
171 * XXX properly function again. Work around this braindamage. 298 * XXX properly function again. Work around this braindamage.
@@ -194,7 +321,7 @@ start:
194 optind = nonopt_start; 321 optind = nonopt_start;
195 } 322 }
196 nonopt_start = nonopt_end = -1; 323 nonopt_start = nonopt_end = -1;
197 return -1; 324 return (-1);
198 } 325 }
199 if ((*(place = nargv[optind]) != '-') 326 if ((*(place = nargv[optind]) != '-')
200 || (place[1] == '\0')) { /* found non-option */ 327 || (place[1] == '\0')) { /* found non-option */
@@ -205,14 +332,14 @@ start:
205 * return non-option as argument to option 1 332 * return non-option as argument to option 1
206 */ 333 */
207 optarg = nargv[optind++]; 334 optarg = nargv[optind++];
208 return INORDER; 335 return (INORDER);
209 } 336 }
210 if (!(flags & FLAG_PERMUTE)) { 337 if (!(flags & FLAG_PERMUTE)) {
211 /* 338 /*
212 * If no permutation wanted, stop parsing 339 * If no permutation wanted, stop parsing
213 * at first non-option. 340 * at first non-option.
214 */ 341 */
215 return -1; 342 return (-1);
216 } 343 }
217 /* do permutation */ 344 /* do permutation */
218 if (nonopt_start == -1) 345 if (nonopt_start == -1)
@@ -230,42 +357,64 @@ start:
230 } 357 }
231 if (nonopt_start != -1 && nonopt_end == -1) 358 if (nonopt_start != -1 && nonopt_end == -1)
232 nonopt_end = optind; 359 nonopt_end = optind;
233 if (place[1] && *++place == '-') { /* found "--" */ 360 if (strcmp(place, "--") == 0) {
234 place++; 361 optind++;
235 return -2; 362 place = EMSG;
363 /*
364 * We found an option (--), so if we skipped
365 * non-options, we have to permute.
366 */
367 if (nonopt_end != -1) {
368 permute_args(nonopt_start, nonopt_end,
369 optind, nargv);
370 optind -= nonopt_end - nonopt_start;
371 }
372 nonopt_start = nonopt_end = -1;
373 return (-1);
374 }
375 place++;
376
377 /* Check long options if we have any */
378 if (long_options != NULL) {
379 int long_only = 0;
380
381 if (*place == '-' ||
382 (long_only = (flags & FLAG_LONGONLY))) {
383 if (!long_only)
384 place++;
385 optchar = parse_long_options(nargc, nargv,
386 options, long_options, idx, long_only);
387 if (optchar != -2) {
388 place = EMSG;
389 return (optchar);
390 }
391 }
236 } 392 }
237 } 393 }
238 if ((optchar = (int)*place++) == (int)':' || 394 if ((optchar = (int)*place++) == (int)':' ||
239 (oli = strchr(options, optchar)) == NULL) { 395 (oli = strchr(options, optchar)) == NULL) {
240 /* could it be a long option with a single '-'? */
241 if (flags & FLAG_LONGONLY)
242 return -2;
243 /* option letter unknown or ':' */ 396 /* option letter unknown or ':' */
244 if (!*place) 397 if (!*place)
245 ++optind; 398 ++optind;
246 if (PRINT_ERROR) 399 if (PRINT_ERROR)
247 warnx(illoptchar, optchar); 400 warnx(illoptchar, optchar);
248 optopt = optchar; 401 optopt = optchar;
249 return BADCH; 402 return (BADCH);
250 } 403 }
251 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ 404 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
252 /* XXX: what if no long options provided (called by getopt)? */ 405 /* -W long-option */
253 if (*place)
254 return -2;
255
256 if (++optind >= nargc) { /* no arg */ 406 if (++optind >= nargc) { /* no arg */
257 place = EMSG; 407 place = EMSG;
258 if (PRINT_ERROR) 408 if (PRINT_ERROR)
259 warnx(recargchar, optchar); 409 warnx(recargchar, optchar);
260 optopt = optchar; 410 optopt = optchar;
261 return BADARG; 411 return (BADARG);
262 } else /* white space */ 412 } else /* white space */
263 place = nargv[optind]; 413 place = nargv[optind];
264 /* 414 optchar = parse_long_options(nargc, nargv, options,
265 * Handle -W arg the same as --arg (which causes getopt to 415 long_options, idx, 0);
266 * stop parsing). 416 place = EMSG;
267 */ 417 return (optchar);
268 return -2;
269 } 418 }
270 if (*++oli != ':') { /* doesn't take argument */ 419 if (*++oli != ':') { /* doesn't take argument */
271 if (!*place) 420 if (!*place)
@@ -281,7 +430,7 @@ start:
281 if (PRINT_ERROR) 430 if (PRINT_ERROR)
282 warnx(recargchar, optchar); 431 warnx(recargchar, optchar);
283 optopt = optchar; 432 optopt = optchar;
284 return BADARG; 433 return (BADARG);
285 } else 434 } else
286 optarg = nargv[optind]; 435 optarg = nargv[optind];
287 } 436 }
@@ -289,7 +438,7 @@ start:
289 ++optind; 438 ++optind;
290 } 439 }
291 /* dump back option letter */ 440 /* dump back option letter */
292 return optchar; 441 return (optchar);
293} 442}
294 443
295#ifdef REPLACE_GETOPT 444#ifdef REPLACE_GETOPT
@@ -300,183 +449,20 @@ start:
300 * [eventually this will replace the BSD getopt] 449 * [eventually this will replace the BSD getopt]
301 */ 450 */
302int 451int
303getopt(nargc, nargv, options) 452getopt(int nargc, char * const *nargv, const char *options)
304 int nargc;
305 char * const *nargv;
306 const char *options;
307{ 453{
308 int retval;
309
310 if ((retval = getopt_internal(nargc, nargv, options, 0)) == -2) {
311 ++optind;
312 /*
313 * We found an option (--), so if we skipped non-options,
314 * we have to permute.
315 */
316 if (nonopt_end != -1) {
317 permute_args(nonopt_start, nonopt_end, optind,
318 nargv);
319 optind -= nonopt_end - nonopt_start;
320 }
321 nonopt_start = nonopt_end = -1;
322 retval = -1;
323 }
324 return retval;
325}
326#endif /* REPLACE_GETOPT */
327
328/*
329 * getopt_long_internal --
330 * Parse argc/argv argument vector.
331 */
332static int
333getopt_long_internal(nargc, nargv, options, long_options, idx, flags)
334 int nargc;
335 char * const *nargv;
336 const char *options;
337 const struct option *long_options;
338 int *idx;
339 int flags;
340{
341 int retval;
342 454
343 /* 455 /*
344 * Disable GNU extensions if POSIXLY_CORRECT is set or options 456 * We dont' pass FLAG_PERMUTE to getopt_internal() since
345 * string begins with a '+'. 457 * the BSD getopt(3) (unlike GNU) has never done this.
458 *
459 * Furthermore, since many privileged programs call getopt()
460 * before dropping privileges it makes sense to keep things
461 * as simple (and bug-free) as possible.
346 */ 462 */
347 if (getenv("POSIXLY_CORRECT")) { 463 return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
348 if (*options == '+' || *options == '-')
349 options++;
350 } else {
351 if (*options == '+') {
352 options++;
353 } else {
354 flags |= FLAG_PERMUTE;
355 if (*options == '-') {
356 flags |= FLAG_ALLARGS;
357 options++;
358 }
359 }
360 }
361
362 if ((retval = getopt_internal(nargc, nargv, options, flags)) == -2) {
363 char *current_argv, *has_equal;
364 size_t current_argv_len;
365 int i, match;
366
367 current_argv = place;
368 match = -1;
369
370 optind++;
371 place = EMSG;
372
373 if (*current_argv == '\0') { /* found "--" */
374 /*
375 * We found an option (--), so if we skipped
376 * non-options, we have to permute.
377 */
378 if (nonopt_end != -1) {
379 permute_args(nonopt_start, nonopt_end,
380 optind, nargv);
381 optind -= nonopt_end - nonopt_start;
382 }
383 nonopt_start = nonopt_end = -1;
384 return -1;
385 }
386 if ((has_equal = strchr(current_argv, '=')) != NULL) {
387 /* argument found (--option=arg) */
388 current_argv_len = has_equal - current_argv;
389 has_equal++;
390 } else
391 current_argv_len = strlen(current_argv);
392
393 for (i = 0; long_options[i].name; i++) {
394 /* find matching long option */
395 if (strncmp(current_argv, long_options[i].name,
396 current_argv_len))
397 continue;
398
399 if (strlen(long_options[i].name) ==
400 (unsigned)current_argv_len) {
401 /* exact match */
402 match = i;
403 break;
404 }
405 if (match == -1) /* partial match */
406 match = i;
407 else {
408 /* ambiguous abbreviation */
409 if (PRINT_ERROR)
410 warnx(ambig, (int)current_argv_len,
411 current_argv);
412 optopt = 0;
413 return BADCH;
414 }
415 }
416 if (match != -1) { /* option found */
417 if (long_options[match].has_arg == no_argument
418 && has_equal) {
419 if (PRINT_ERROR)
420 warnx(noarg, (int)current_argv_len,
421 current_argv);
422 /*
423 * XXX: GNU sets optopt to val regardless of
424 * flag
425 */
426 if (long_options[match].flag == NULL)
427 optopt = long_options[match].val;
428 else
429 optopt = 0;
430 return BADARG;
431 }
432 if (long_options[match].has_arg == required_argument ||
433 long_options[match].has_arg == optional_argument) {
434 if (has_equal)
435 optarg = has_equal;
436 else if (long_options[match].has_arg ==
437 required_argument) {
438 /*
439 * optional argument doesn't use
440 * next nargv
441 */
442 optarg = nargv[optind++];
443 }
444 }
445 if ((long_options[match].has_arg == required_argument)
446 && (optarg == NULL)) {
447 /*
448 * Missing argument; leading ':'
449 * indicates no error should be generated
450 */
451 if (PRINT_ERROR)
452 warnx(recargstring, current_argv);
453 /*
454 * XXX: GNU sets optopt to val regardless
455 * of flag
456 */
457 if (long_options[match].flag == NULL)
458 optopt = long_options[match].val;
459 else
460 optopt = 0;
461 --optind;
462 return BADARG;
463 }
464 } else { /* unknown option */
465 if (PRINT_ERROR)
466 warnx(illoptstring, current_argv);
467 optopt = 0;
468 return BADCH;
469 }
470 if (long_options[match].flag) {
471 *long_options[match].flag = long_options[match].val;
472 retval = 0;
473 } else
474 retval = long_options[match].val;
475 if (idx)
476 *idx = match;
477 }
478 return retval;
479} 464}
465#endif /* REPLACE_GETOPT */
480 466
481/* 467/*
482 * getopt_long -- 468 * getopt_long --
@@ -491,7 +477,8 @@ getopt_long(nargc, nargv, options, long_options, idx)
491 int *idx; 477 int *idx;
492{ 478{
493 479
494 return getopt_long_internal(nargc, nargv, options, long_options, idx, 0); 480 return (getopt_internal(nargc, nargv, options, long_options, idx,
481 FLAG_PERMUTE));
495} 482}
496 483
497/* 484/*
@@ -507,6 +494,6 @@ getopt_long_only(nargc, nargv, options, long_options, idx)
507 int *idx; 494 int *idx;
508{ 495{
509 496
510 return getopt_long_internal(nargc, nargv, options, long_options, idx, 497 return (getopt_internal(nargc, nargv, options, long_options, idx,
511 FLAG_LONGONLY); 498 FLAG_PERMUTE|FLAG_LONGONLY));
512} 499}