1 /* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */
2 /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
3
4 /* SPDX-License-Identifier: LicenseRef-scancode-sudo AND BSD-4-Clause
5 *
6 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *
20 * Sponsored in part by the Defense Advanced Research Projects
21 * Agency (DARPA) and Air Force Research Laboratory, Air Force
22 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
23 */
24 /*-
25 * Copyright (c) 2000 The NetBSD Foundation, Inc.
26 * All rights reserved.
27 *
28 * This code is derived from software contributed to The NetBSD Foundation
29 * by Dieter Baron and Thomas Klausner.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the NetBSD
42 * Foundation, Inc. and its contributors.
43 * 4. Neither the name of The NetBSD Foundation nor the names of its
44 * contributors may be used to endorse or promote products derived
45 * from this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
48 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
49 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
50 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
51 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57 * POSSIBILITY OF SUCH DAMAGE.
58 */
59
60 #include "getopt_long.h"
61
62 #include <errno.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #ifndef _WIN32
67 #include <unistd.h>
68 #endif
69
70 #define PKGCONF_HACK_LOGICAL_OR_ALL_VALUES
71
72 int pkg_opterr = 1; /* if error message should be printed */
73 int pkg_optind = 1; /* index into parent argv vector */
74 int pkg_optopt = '?'; /* character checked for validity */
75 int pkg_optreset; /* reset getopt */
76 char *pkg_optarg; /* argument associated with option */
77
78 #define PRINT_ERROR ((pkg_opterr) && (*options != ':'))
79
80 #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
81 #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
82 #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
83
84 /* return values */
85 #define BADCH (int)'?'
86 #define BADARG ((*options == ':') ? (int)':' : (int)'?')
87 #define INORDER (int)1
88
89 /* add some padding to EMSG to avoid overrun */
90 #define EMSG "\0\0\0\0"
91
92 #ifdef GNU_COMPATIBLE
93 #define NO_PREFIX (-1)
94 #define D_PREFIX 0
95 #define DD_PREFIX 1
96 #define W_PREFIX 2
97 #endif
98
99 static int getopt_internal(int, char * const *, const char *,
100 const struct pkg_option *, int *, int);
101 static int parse_long_options(char * const *, const char *,
102 const struct pkg_option *, int *, int, int);
103 static int gcd(int, int);
104 static void permute_args(int, int, int, char * const *);
105
106 static char *place = EMSG; /* option letter processing */
107
108 /* XXX: set pkg_optreset to 1 rather than these two */
109 static int nonopt_start = -1; /* first non option argument (for permute) */
110 static int nonopt_end = -1; /* first option after non options (for permute) */
111
112 /* Error messages */
113 static const char recargchar[] = "option requires an argument -- %c";
114 static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
115 #ifdef GNU_COMPATIBLE
116 static int dash_prefix = NO_PREFIX;
117 static const char gnuoptchar[] = "invalid option -- %c";
118
119 static const char recargstring[] = "option `%s%s' requires an argument";
120 static const char ambig[] = "option `%s%.*s' is ambiguous";
121 static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
122 static const char illoptstring[] = "unrecognized option `%s%s'";
123 #else
124 static const char recargstring[] = "option requires an argument -- %s";
125 static const char ambig[] = "ambiguous option -- %.*s";
126 static const char noarg[] = "option doesn't take an argument -- %.*s";
127 static const char illoptstring[] = "unknown option -- %s";
128 #endif
129
130 /*
131 * Compute the greatest common divisor of a and b.
132 */
133 static int
gcd(int a,int b)134 gcd(int a, int b)
135 {
136 int c;
137
138 c = a % b;
139 while (c != 0) {
140 a = b;
141 b = c;
142 c = a % b;
143 }
144
145 return (b);
146 }
147
148 /*
149 * Exchange the block from nonopt_start to nonopt_end with the block
150 * from nonopt_end to opt_end (keeping the same order of arguments
151 * in each block).
152 */
153 static void
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)154 permute_args(int panonopt_start, int panonopt_end, int opt_end,
155 char * const *nargv)
156 {
157 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
158 char *swap;
159
160 /*
161 * compute lengths of blocks and number and size of cycles
162 */
163 nnonopts = panonopt_end - panonopt_start;
164 nopts = opt_end - panonopt_end;
165 ncycle = gcd(nnonopts, nopts);
166 cyclelen = (opt_end - panonopt_start) / ncycle;
167
168 for (i = 0; i < ncycle; i++) {
169 cstart = panonopt_end+i;
170 pos = cstart;
171 for (j = 0; j < cyclelen; j++) {
172 if (pos >= panonopt_end)
173 pos -= nnonopts;
174 else
175 pos += nopts;
176 swap = nargv[pos];
177 /* LINTED const cast */
178 ((char **) nargv)[pos] = nargv[cstart];
179 /* LINTED const cast */
180 ((char **)nargv)[cstart] = swap;
181 }
182 }
183 }
184
185 /*
186 * parse_long_options --
187 * Parse long options in argc/argv argument vector.
188 * Returns -1 if short_too is set and the option does not match long_options.
189 */
190 static int
parse_long_options(char * const * nargv,const char * options,const struct pkg_option * long_options,int * idx,int short_too,int flags)191 parse_long_options(char * const *nargv, const char *options,
192 const struct pkg_option *long_options, int *idx, int short_too, int flags)
193 {
194 char *current_argv, *has_equal;
195 #ifdef GNU_COMPATIBLE
196 char *current_dash;
197 #endif
198 size_t current_argv_len;
199 int i, match, exact_match, second_partial_match;
200
201 current_argv = place;
202 #ifdef GNU_COMPATIBLE
203 switch (dash_prefix) {
204 case D_PREFIX:
205 current_dash = "-";
206 break;
207 case DD_PREFIX:
208 current_dash = "--";
209 break;
210 case W_PREFIX:
211 current_dash = "-W ";
212 break;
213 default:
214 current_dash = "";
215 break;
216 }
217 #endif
218 match = -1;
219 exact_match = 0;
220 second_partial_match = 0;
221
222 pkg_optind++;
223
224 if ((has_equal = strchr(current_argv, '=')) != NULL) {
225 /* argument found (--option=arg) */
226 current_argv_len = has_equal - current_argv;
227 has_equal++;
228 } else
229 current_argv_len = strlen(current_argv);
230
231 for (i = 0; long_options[i].name; i++) {
232 /* find matching long option */
233 if (strncmp(current_argv, long_options[i].name,
234 current_argv_len))
235 continue;
236
237 if (strlen(long_options[i].name) == current_argv_len) {
238 /* exact match */
239 match = i;
240 exact_match = 1;
241 break;
242 }
243 /*
244 * If this is a known short option, don't allow
245 * a partial match of a single character.
246 */
247 if (short_too && current_argv_len == 1)
248 continue;
249
250 if (match == -1) /* first partial match */
251 match = i;
252 else if ((flags & FLAG_LONGONLY) ||
253 long_options[i].has_arg !=
254 long_options[match].has_arg ||
255 long_options[i].flag != long_options[match].flag ||
256 long_options[i].val != long_options[match].val)
257 second_partial_match = 1;
258 }
259 if (!exact_match && second_partial_match) {
260 /* ambiguous abbreviation */
261 if (PRINT_ERROR) {
262 fprintf(stderr, "pkgconf: ");
263 fprintf(stderr, ambig,
264 #ifdef GNU_COMPATIBLE
265 current_dash,
266 #endif
267 (int)current_argv_len,
268 current_argv);
269 fprintf(stderr, "\n");
270 }
271 pkg_optopt = 0;
272 return (BADCH);
273 }
274 if (match != -1) { /* option found */
275 if (long_options[match].has_arg == no_argument
276 && has_equal) {
277 if (PRINT_ERROR) {
278 fprintf(stderr, "pkgconf: ");
279 fprintf(stderr, noarg,
280 #ifdef GNU_COMPATIBLE
281 current_dash,
282 #endif
283 (int)current_argv_len,
284 current_argv);
285 fprintf(stderr, "\n");
286 }
287 /*
288 * XXX: GNU sets pkg_optopt to val regardless of flag
289 */
290 if (long_options[match].flag == NULL)
291 pkg_optopt = (int)long_options[match].val;
292 else
293 pkg_optopt = 0;
294 #ifdef GNU_COMPATIBLE
295 return (BADCH);
296 #else
297 return (BADARG);
298 #endif
299 }
300 if (long_options[match].has_arg == required_argument ||
301 long_options[match].has_arg == optional_argument) {
302 if (has_equal)
303 pkg_optarg = has_equal;
304 else if (long_options[match].has_arg ==
305 required_argument) {
306 /*
307 * optional argument doesn't use next nargv
308 */
309 pkg_optarg = nargv[pkg_optind++];
310 }
311 }
312 if ((long_options[match].has_arg == required_argument)
313 && (pkg_optarg == NULL)) {
314 /*
315 * Missing argument; leading ':' indicates no error
316 * should be generated.
317 */
318 if (PRINT_ERROR) {
319 fprintf(stderr, "pkgconf: ");
320 fprintf(stderr, recargstring,
321 #ifdef GNU_COMPATIBLE
322 current_dash,
323 #endif
324 current_argv);
325 fprintf(stderr, "\n");
326 }
327 /*
328 * XXX: GNU sets pkg_optopt to val regardless of flag
329 */
330 if (long_options[match].flag == NULL)
331 pkg_optopt = (int)long_options[match].val;
332 else
333 pkg_optopt = 0;
334 --pkg_optind;
335 return (BADARG);
336 }
337 } else { /* unknown option */
338 if (short_too) {
339 --pkg_optind;
340 return (-1);
341 }
342 if (PRINT_ERROR) {
343 fprintf(stderr, "pkgconf: ");
344 fprintf(stderr, illoptstring,
345 #ifdef GNU_COMPATIBLE
346 current_dash,
347 #endif
348 current_argv);
349 fprintf(stderr, "\n");
350 }
351 pkg_optopt = 0;
352 return (BADCH);
353 }
354 if (idx)
355 *idx = match;
356 if (long_options[match].flag) {
357 #ifdef PKGCONF_HACK_LOGICAL_OR_ALL_VALUES
358 *long_options[match].flag |= long_options[match].val;
359 #else
360 *long_options[match].flag = long_options[match].val;
361 #endif
362 return (0);
363 } else
364 return ((int)long_options[match].val);
365 }
366
367 /*
368 * getopt_internal --
369 * Parse argc/argv argument vector. Called by user level routines.
370 */
371 static int
getopt_internal(int nargc,char * const * nargv,const char * options,const struct pkg_option * long_options,int * idx,int flags)372 getopt_internal(int nargc, char * const *nargv, const char *options,
373 const struct pkg_option *long_options, int *idx, int flags)
374 {
375 char *oli; /* option letter list index */
376 int optchar, short_too;
377 int posixly_correct; /* no static, can be changed on the fly */
378
379 if (options == NULL)
380 return (-1);
381
382 /*
383 * Disable GNU extensions if POSIXLY_CORRECT is set or options
384 * string begins with a '+'.
385 */
386 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
387 #ifdef GNU_COMPATIBLE
388 if (*options == '-')
389 flags |= FLAG_ALLARGS;
390 else if (posixly_correct || *options == '+')
391 flags &= ~FLAG_PERMUTE;
392 #else
393 if (posixly_correct || *options == '+')
394 flags &= ~FLAG_PERMUTE;
395 else if (*options == '-')
396 flags |= FLAG_ALLARGS;
397 #endif
398
399 if (*options == '+' || *options == '-')
400 options++;
401
402 /*
403 * XXX Some GNU programs (like cvs) set pkg_optind to 0 instead of
404 * XXX using pkg_optreset. Work around this braindamage.
405 */
406 if (pkg_optind == 0)
407 pkg_optind = pkg_optreset = 1;
408
409 pkg_optarg = NULL;
410 if (pkg_optreset)
411 nonopt_start = nonopt_end = -1;
412 start:
413 if (pkg_optreset || !*place) { /* update scanning pointer */
414 pkg_optreset = 0;
415 if (pkg_optind >= nargc) { /* end of argument vector */
416 place = EMSG;
417 if (nonopt_end != -1) {
418 /* do permutation, if we have to */
419 permute_args(nonopt_start, nonopt_end,
420 pkg_optind, nargv);
421 pkg_optind -= nonopt_end - nonopt_start;
422 }
423 else if (nonopt_start != -1) {
424 /*
425 * If we skipped non-options, set pkg_optind
426 * to the first of them.
427 */
428 pkg_optind = nonopt_start;
429 }
430 nonopt_start = nonopt_end = -1;
431 return (-1);
432 }
433 if (*(place = nargv[pkg_optind]) != '-' ||
434 #ifdef GNU_COMPATIBLE
435 place[1] == '\0') {
436 #else
437 (place[1] == '\0' && strchr(options, '-') == NULL)) {
438 #endif
439 place = EMSG; /* found non-option */
440 if (flags & FLAG_ALLARGS) {
441 /*
442 * GNU extension:
443 * return non-option as argument to option 1
444 */
445 pkg_optarg = nargv[pkg_optind++];
446 return (INORDER);
447 }
448 if (!(flags & FLAG_PERMUTE)) {
449 /*
450 * If no permutation wanted, stop parsing
451 * at first non-option.
452 */
453 return (-1);
454 }
455 /* do permutation */
456 if (nonopt_start == -1)
457 nonopt_start = pkg_optind;
458 else if (nonopt_end != -1) {
459 permute_args(nonopt_start, nonopt_end,
460 pkg_optind, nargv);
461 nonopt_start = pkg_optind -
462 (nonopt_end - nonopt_start);
463 nonopt_end = -1;
464 }
465 pkg_optind++;
466 /* process next argument */
467 goto start;
468 }
469 if (nonopt_start != -1 && nonopt_end == -1)
470 nonopt_end = pkg_optind;
471
472 /*
473 * If we have "-" do nothing, if "--" we are done.
474 */
475 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
476 pkg_optind++;
477 place = EMSG;
478 /*
479 * We found an option (--), so if we skipped
480 * non-options, we have to permute.
481 */
482 if (nonopt_end != -1) {
483 permute_args(nonopt_start, nonopt_end,
484 pkg_optind, nargv);
485 pkg_optind -= nonopt_end - nonopt_start;
486 }
487 nonopt_start = nonopt_end = -1;
488 return (-1);
489 }
490 }
491
492 /*
493 * Check long options if:
494 * 1) we were passed some
495 * 2) the arg is not just "-"
496 * 3) either the arg starts with -- we are getopt_long_only()
497 */
498 if (long_options != NULL && place != nargv[pkg_optind] &&
499 (*place == '-' || (flags & FLAG_LONGONLY))) {
500 short_too = 0;
501 #ifdef GNU_COMPATIBLE
502 dash_prefix = D_PREFIX;
503 #endif
504 if (*place == '-') {
505 place++; /* --foo long option */
506 #ifdef GNU_COMPATIBLE
507 dash_prefix = DD_PREFIX;
508 #endif
509 } else if (*place != ':' && strchr(options, *place) != NULL)
510 short_too = 1; /* could be short option too */
511
512 optchar = parse_long_options(nargv, options, long_options,
513 idx, short_too, flags);
514 if (optchar != -1) {
515 place = EMSG;
516 return (optchar);
517 }
518 }
519
520 if ((optchar = (int)*place++) == (int)':' ||
521 (optchar == (int)'-' && *place != '\0') ||
522 (oli = strchr(options, optchar)) == NULL) {
523 /*
524 * If the user specified "-" and '-' isn't listed in
525 * options, return -1 (non-option) as per POSIX.
526 * Otherwise, it is an unknown option character (or ':').
527 */
528 if (optchar == (int)'-' && *place == '\0')
529 return (-1);
530 if (!*place)
531 ++pkg_optind;
532 #ifdef GNU_COMPATIBLE
533 if (PRINT_ERROR) {
534 fprintf(stderr, "pkgconf: ");
535 fprintf(stderr, posixly_correct ? illoptchar : gnuoptchar,
536 optchar);
537 fprintf(stderr, "\n");
538 }
539 #else
540 if (PRINT_ERROR) {
541 fprintf(stderr, "pkgconf: ");
542 fprintf(stderr, illoptchar, optchar);
543 fprintf(stderr, "\n");
544 }
545 #endif
546 pkg_optopt = optchar;
547 return (BADCH);
548 }
549 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
550 /* -W long-option */
551 if (*place) /* no space */
552 /* NOTHING */;
553 else if (++pkg_optind >= nargc) { /* no arg */
554 place = EMSG;
555 if (PRINT_ERROR) {
556 fprintf(stderr, "pkgconf: ");
557 fprintf(stderr, recargchar, optchar);
558 fprintf(stderr, "\n");
559 }
560 pkg_optopt = optchar;
561 return (BADARG);
562 } else /* white space */
563 place = nargv[pkg_optind];
564 #ifdef GNU_COMPATIBLE
565 dash_prefix = W_PREFIX;
566 #endif
567 optchar = parse_long_options(nargv, options, long_options,
568 idx, 0, flags);
569 place = EMSG;
570 return (optchar);
571 }
572 if (*++oli != ':') { /* doesn't take argument */
573 if (!*place)
574 ++pkg_optind;
575 } else { /* takes (optional) argument */
576 pkg_optarg = NULL;
577 if (*place) /* no white space */
578 pkg_optarg = place;
579 else if (oli[1] != ':') { /* arg not optional */
580 if (++pkg_optind >= nargc) { /* no arg */
581 place = EMSG;
582 if (PRINT_ERROR) {
583 fprintf(stderr, "pkgconf: ");
584 fprintf(stderr, recargchar, optchar);
585 fprintf(stderr, "\n");
586 }
587 pkg_optopt = optchar;
588 return (BADARG);
589 } else
590 pkg_optarg = nargv[pkg_optind];
591 }
592 place = EMSG;
593 ++pkg_optind;
594 }
595 /* dump back option letter */
596 return (optchar);
597 }
598
599 /*
600 * getopt --
601 * Parse argc/argv argument vector.
602 *
603 * [eventually this will replace the BSD getopt]
604 */
605 int
606 pkg_getopt(int nargc, char * const *nargv, const char *options)
607 {
608
609 /*
610 * We don't pass FLAG_PERMUTE to getopt_internal() since
611 * the BSD getopt(3) (unlike GNU) has never done this.
612 *
613 * Furthermore, since many privileged programs call getopt()
614 * before dropping privileges it makes sense to keep things
615 * as simple (and bug-free) as possible.
616 */
617 return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
618 }
619
620 /*
621 * getopt_long --
622 * Parse argc/argv argument vector.
623 */
624 int
625 pkg_getopt_long(int nargc, char * const *nargv, const char *options,
626 const struct pkg_option *long_options, int *idx)
627 {
628
629 return (getopt_internal(nargc, nargv, options, long_options, idx,
630 FLAG_PERMUTE));
631 }
632
633 /*
634 * getopt_long_only --
635 * Parse argc/argv argument vector.
636 */
637 int
638 pkg_getopt_long_only(int nargc, char * const *nargv, const char *options,
639 const struct pkg_option *long_options, int *idx)
640 {
641
642 return (getopt_internal(nargc, nargv, options, long_options, idx,
643 FLAG_PERMUTE|FLAG_LONGONLY));
644 }
645