From 2208de30a37c02891803f11a48be634460c1b88e Mon Sep 17 00:00:00 2001 From: millert <> Date: Thu, 5 Dec 2002 21:45:01 +0000 Subject: [PATCH] Reorganize this so that getopt_only_only() works correctly. In order for getopt_only_only() to work we need to check for long options before short ones. I have merged getopt_internal and getopt_long_internal into a single function with the actual long args parsing broken out into a separate function. This also simplifies the flow of control. --- src/lib/libc/stdlib/getopt_long.c | 429 +++++++++++++++--------------- 1 file changed, 208 insertions(+), 221 deletions(-) diff --git a/src/lib/libc/stdlib/getopt_long.c b/src/lib/libc/stdlib/getopt_long.c index 4fc18743..130c1d8b 100644 --- a/src/lib/libc/stdlib/getopt_long.c +++ b/src/lib/libc/stdlib/getopt_long.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $ */ +/* $OpenBSD: getopt_long.c,v 1.3 2002/12/05 21:45:01 millert Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /*- @@ -38,7 +38,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $"; +static char *rcsid = "$OpenBSD: getopt_long.c,v 1.3 2002/12/05 21:45:01 millert Exp $"; #endif /* LIBC_SCCS and not lint */ #include @@ -57,9 +57,9 @@ char *optarg; /* argument associated with option */ #define PRINT_ERROR ((opterr) && (*options != ':')) -#define FLAG_PERMUTE 0x01 -#define FLAG_ALLARGS 0x02 -#define FLAG_LONGONLY 0x04 +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' @@ -68,9 +68,10 @@ char *optarg; /* argument associated with option */ #define EMSG "" -static int getopt_internal(int, char * const *, const char *, int); -static int getopt_long_internal(int, char * const *, const char *, - const struct option *, int *, int); +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(int, char * const *, const char *, + const struct option *, int *, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); @@ -92,9 +93,7 @@ static const char illoptstring[] = "unknown option -- %s"; * Compute the greatest common divisor of a and b. */ static int -gcd(a, b) - int a; - int b; +gcd(int a, int b) { int c; @@ -105,7 +104,7 @@ gcd(a, b) c = a % b; } - return b; + return (b); } /* @@ -114,11 +113,8 @@ gcd(a, b) * in each block). */ static void -permute_args(panonopt_start, panonopt_end, opt_end, nargv) - int panonopt_start; - int panonopt_end; - int opt_end; - char * const *nargv; +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; @@ -148,23 +144,154 @@ permute_args(panonopt_start, panonopt_end, opt_end, nargv) } } +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -2 if long_only is set and the current option could be a short + * (single character) option instead. + */ +static int +parse_long_options(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int long_only) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * Don't try a partial match of a short option when in + * long_only mode. Otherwise there is a potential conflict + * between partial matches and short options. + */ + if (long_only && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (long_only) { + --optind; + return (-2); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. - * Returns -2 if -- is found (can be long option or end of options marker). */ static int -getopt_internal(nargc, nargv, options, flags) - int nargc; - char * const *nargv; - const char *options; - int flags; +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar; + static int posixly_correct = -1; optarg = NULL; + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + /* * XXX Some programs (like rsyncd) expect to be able to * XXX re-initialize optind to 0 and have getopt_long(3) @@ -194,7 +321,7 @@ start: optind = nonopt_start; } nonopt_start = nonopt_end = -1; - return -1; + return (-1); } if ((*(place = nargv[optind]) != '-') || (place[1] == '\0')) { /* found non-option */ @@ -205,14 +332,14 @@ start: * return non-option as argument to option 1 */ optarg = nargv[optind++]; - return INORDER; + return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ - return -1; + return (-1); } /* do permutation */ if (nonopt_start == -1) @@ -230,42 +357,64 @@ start: } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; - if (place[1] && *++place == '-') { /* found "--" */ - place++; - return -2; + if (strcmp(place, "--") == 0) { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + place++; + + /* Check long options if we have any */ + if (long_options != NULL) { + int long_only = 0; + + if (*place == '-' || + (long_only = (flags & FLAG_LONGONLY))) { + if (!long_only) + place++; + optchar = parse_long_options(nargc, nargv, + options, long_options, idx, long_only); + if (optchar != -2) { + place = EMSG; + return (optchar); + } + } } } if ((optchar = (int)*place++) == (int)':' || (oli = strchr(options, optchar)) == NULL) { - /* could it be a long option with a single '-'? */ - if (flags & FLAG_LONGONLY) - return -2; /* option letter unknown or ':' */ if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; - return BADCH; + return (BADCH); } - if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ - /* XXX: what if no long options provided (called by getopt)? */ - if (*place) - return -2; - + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; - return BADARG; + return (BADARG); } else /* white space */ place = nargv[optind]; - /* - * Handle -W arg the same as --arg (which causes getopt to - * stop parsing). - */ - return -2; + optchar = parse_long_options(nargc, nargv, options, + long_options, idx, 0); + place = EMSG; + return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) @@ -281,7 +430,7 @@ start: if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; - return BADARG; + return (BADARG); } else optarg = nargv[optind]; } @@ -289,7 +438,7 @@ start: ++optind; } /* dump back option letter */ - return optchar; + return (optchar); } #ifdef REPLACE_GETOPT @@ -300,183 +449,20 @@ start: * [eventually this will replace the BSD getopt] */ int -getopt(nargc, nargv, options) - int nargc; - char * const *nargv; - const char *options; +getopt(int nargc, char * const *nargv, const char *options) { - int retval; - - if ((retval = getopt_internal(nargc, nargv, options, 0)) == -2) { - ++optind; - /* - * We found an option (--), so if we skipped non-options, - * we have to permute. - */ - if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, optind, - nargv); - optind -= nonopt_end - nonopt_start; - } - nonopt_start = nonopt_end = -1; - retval = -1; - } - return retval; -} -#endif /* REPLACE_GETOPT */ - -/* - * getopt_long_internal -- - * Parse argc/argv argument vector. - */ -static int -getopt_long_internal(nargc, nargv, options, long_options, idx, flags) - int nargc; - char * const *nargv; - const char *options; - const struct option *long_options; - int *idx; - int flags; -{ - int retval; /* - * Disable GNU extensions if POSIXLY_CORRECT is set or options - * string begins with a '+'. + * We dont' pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. */ - if (getenv("POSIXLY_CORRECT")) { - if (*options == '+' || *options == '-') - options++; - } else { - if (*options == '+') { - options++; - } else { - flags |= FLAG_PERMUTE; - if (*options == '-') { - flags |= FLAG_ALLARGS; - options++; - } - } - } - - if ((retval = getopt_internal(nargc, nargv, options, flags)) == -2) { - char *current_argv, *has_equal; - size_t current_argv_len; - int i, match; - - current_argv = place; - match = -1; - - optind++; - place = EMSG; - - if (*current_argv == '\0') { /* found "--" */ - /* - * We found an option (--), so if we skipped - * non-options, we have to permute. - */ - if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - nonopt_start = nonopt_end = -1; - return -1; - } - if ((has_equal = strchr(current_argv, '=')) != NULL) { - /* argument found (--option=arg) */ - current_argv_len = has_equal - current_argv; - has_equal++; - } else - current_argv_len = strlen(current_argv); - - for (i = 0; long_options[i].name; i++) { - /* find matching long option */ - if (strncmp(current_argv, long_options[i].name, - current_argv_len)) - continue; - - if (strlen(long_options[i].name) == - (unsigned)current_argv_len) { - /* exact match */ - match = i; - break; - } - if (match == -1) /* partial match */ - match = i; - else { - /* ambiguous abbreviation */ - if (PRINT_ERROR) - warnx(ambig, (int)current_argv_len, - current_argv); - optopt = 0; - return BADCH; - } - } - if (match != -1) { /* option found */ - if (long_options[match].has_arg == no_argument - && has_equal) { - if (PRINT_ERROR) - warnx(noarg, (int)current_argv_len, - current_argv); - /* - * XXX: GNU sets optopt to val regardless of - * flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - return BADARG; - } - if (long_options[match].has_arg == required_argument || - long_options[match].has_arg == optional_argument) { - if (has_equal) - optarg = has_equal; - else if (long_options[match].has_arg == - required_argument) { - /* - * optional argument doesn't use - * next nargv - */ - optarg = nargv[optind++]; - } - } - if ((long_options[match].has_arg == required_argument) - && (optarg == NULL)) { - /* - * Missing argument; leading ':' - * indicates no error should be generated - */ - if (PRINT_ERROR) - warnx(recargstring, current_argv); - /* - * XXX: GNU sets optopt to val regardless - * of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - --optind; - return BADARG; - } - } else { /* unknown option */ - if (PRINT_ERROR) - warnx(illoptstring, current_argv); - optopt = 0; - return BADCH; - } - if (long_options[match].flag) { - *long_options[match].flag = long_options[match].val; - retval = 0; - } else - retval = long_options[match].val; - if (idx) - *idx = match; - } - return retval; + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } +#endif /* REPLACE_GETOPT */ /* * getopt_long -- @@ -491,7 +477,8 @@ getopt_long(nargc, nargv, options, long_options, idx) int *idx; { - return getopt_long_internal(nargc, nargv, options, long_options, idx, 0); + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); } /* @@ -507,6 +494,6 @@ getopt_long_only(nargc, nargv, options, long_options, idx) int *idx; { - return getopt_long_internal(nargc, nargv, options, long_options, idx, - FLAG_LONGONLY); + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); }