diff options
author | Christopher Faylor <me@cgf.cx> | 2001-09-19 01:07:11 +0000 |
---|---|---|
committer | Christopher Faylor <me@cgf.cx> | 2001-09-19 01:07:11 +0000 |
commit | 5733509e25f40d3640c1648dcb89ae5234d097e8 (patch) | |
tree | 704e399cd6af206dcfc77186601ea90aae9fdcfd /winsup/cygwin/lib | |
parent | 439defa2300878b2b87145e754ece9bc1d07ad26 (diff) | |
download | newlib-5733509e25f40d3640c1648dcb89ae5234d097e8.zip newlib-5733509e25f40d3640c1648dcb89ae5234d097e8.tar.gz newlib-5733509e25f40d3640c1648dcb89ae5234d097e8.tar.bz2 |
* cygwin.din (__argv): Export.
(__argc): Ditto.
(__progname): Ditto.
* include/getopt.h (getopt_long): constify arguments.
* lib/getopt.c: Import new file from NetBSD.
Diffstat (limited to 'winsup/cygwin/lib')
-rw-r--r-- | winsup/cygwin/lib/getopt.c | 696 |
1 files changed, 399 insertions, 297 deletions
diff --git a/winsup/cygwin/lib/getopt.c b/winsup/cygwin/lib/getopt.c index da7ecfc..7049f80 100644 --- a/winsup/cygwin/lib/getopt.c +++ b/winsup/cygwin/lib/getopt.c @@ -1,6 +1,11 @@ -/* - * Copyright (c) 1987, 1993, 1994, 1996 - * The Regents of the University of California. All rights reserved. +/* $NetBSD: getopt_long.c,v 1.12 2001/04/24 09:07:43 joda Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -12,380 +17,477 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> +#include <sys/cdefs.h> + +/*#include "namespace.h"*/ + +#include <assert.h> +#include <errno.h> +/*#include <err.h>*/ #include <stdlib.h> #include <string.h> -#include "getopt.h" +#include <getopt.h> +#include <stdarg.h> +#include <stdio.h> -int opterr = 1; /* if error message should be printed */ -int optind = 1; /* index into parent argv vector */ -int optopt; /* character checked for validity */ -int optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ +#define REPLACE_GETOPT -static char * __progname (char *); -int getopt_internal (int, char * const *, const char *); +#define _DIAGASSERT(x) do {} while (0) -static char * __progname(nargv0) - char * nargv0; -{ - char * tmp = strrchr(nargv0, '/'); - if (tmp) tmp++; else tmp = nargv0; - return(tmp); -} +#ifdef REPLACE_GETOPT +#ifdef __weak_alias +__weak_alias(getopt,_getopt) +#endif +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#ifdef __weak_alias +__weak_alias(getopt_long,_getopt_long) +#endif + + +#define IGNORE_FIRST (*options == '-' || *options == '+') +#define PRINT_ERROR ((opterr) && ((*options != ':') \ + || (IGNORE_FIRST && options[1] != ':'))) +#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) +#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) +/* XXX: GNU ignores PC if *options == '-' */ +#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') +/* return values */ #define BADCH (int)'?' -#define BADARG (int)':' +#define BADARG ((IGNORE_FIRST && options[1] == ':') \ + || (*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + #define EMSG "" +static int getopt_internal __P((int, char * const *, const char *)); +static int gcd __P((int, int)); +static void permute_args __P((int, int, int, char * const *)); + +static const char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +extern char __declspec(dllimport) *__progname; + +static void +_vwarnx(const char *fmt, va_list ap) +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); +} + +static void +warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + _vwarnx(fmt, ap); + va_end(ap); +} + /* - * getopt -- - * Parse argc/argv argument vector. + * Compute the greatest common divisor of a and b. */ -int -getopt_internal(nargc, nargv, ostr) - int nargc; +static int +gcd(a, b) + int a; + int b; +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return b; +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(nonopt_start, nonopt_end, opt_end, nargv) + int nonopt_start; + int nonopt_end; + int opt_end; char * const *nargv; - const char *ostr; { - static const char *place = EMSG; /* option letter processing */ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + _DIAGASSERT(nargv != NULL); + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = nonopt_end - nonopt_start; + nopts = opt_end - nonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - nonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = nonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= nonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * 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(int nargc, char *const * nargv, const char *options) +{ char *oli; /* option letter list index */ + int optchar; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + + optarg = NULL; + /* + * XXX Some programs (like rsyncd) expect to be able to + * XXX re-initialize optind to 0 and have getopt_long(3) + * XXX properly function again. Work around this braindamage. + */ + if (optind == 0) + optind = 1; + + if (optreset) + nonopt_start = nonopt_end = -1; +start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { + if (optind >= nargc) { /* end of argument vector */ place = EMSG; - return (-1); + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return -1; } - if (place[1] && *++place == '-') { /* found "--" */ - /* ++optind; */ + if ((*(place = nargv[optind]) != '-') + || (place[1] == '\0')) { /* found non-option */ place = EMSG; - return (-2); + if (IN_ORDER) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return INORDER; + } + if (!PERMUTE) { + /* + * if no permutation wanted, stop parsing + * at first non-option + */ + return -1; + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = strchr(ostr, optopt))) { - /* - * if the user didn't specify '-' as an option, - * assume it means -1. - */ - if (optopt == (int)'-') - return (-1); + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + if (place[1] && *++place == '-') { /* found "--" */ + place++; + return -2; + } + } + if ((optchar = (int)*place++) == (int)':' || + (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { + /* option letter unknown or ':' */ if (!*place) ++optind; - if (opterr && *ostr != ':') - (void)fprintf(stderr, - "%s: illegal option -- %c\n", __progname(nargv[0]), optopt); - return (BADCH); + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return BADCH; } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; + if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ + /* XXX: what if no long options provided (called by getopt)? */ + if (*place) + return -2; + + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return BADARG; + } else /* white space */ + place = nargv[optind]; + /* + * Handle -W arg the same as --arg (which causes getopt to + * stop parsing). + */ + return -2; + } + if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; - } else { /* need an argument */ + } else { /* takes (optional) argument */ + optarg = NULL; if (*place) /* no white space */ - optarg = (char *)place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if ((opterr) && (*ostr != ':')) - (void)fprintf(stderr, - "%s: option requires an argument -- %c\n", - __progname(nargv[0]), optopt); - return (BADARG); - } else /* white space */ - optarg = nargv[optind]; + optarg = (char *) place; + /* XXX: disable test for :: if PC? (GNU doesn't) */ + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return BADARG; + } else + optarg = nargv[optind]; + } place = EMSG; ++optind; } - return (optopt); /* dump back option letter */ + /* dump back option letter */ + return optchar; } +#ifdef REPLACE_GETOPT /* * getopt -- * Parse argc/argv argument vector. + * + * [eventually this will replace the real getopt] */ int -getopt(nargc, nargv, ostr) - int nargc; - char * const *nargv; - const char *ostr; +getopt(int nargc, char * const *nargv, const char *options) { int retval; - if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) { - retval = -1; + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + + if ((retval = getopt_internal(nargc, nargv, options)) == -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); + return retval; } +#endif /* * getopt_long -- * Parse argc/argv argument vector. */ int -getopt_long(nargc, nargv, options, long_options, index) - int nargc; - char ** nargv; - char * options; - struct option * long_options; - int * index; +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) { int retval; + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + _DIAGASSERT(long_options != NULL); + /* idx may be NULL */ + if ((retval = getopt_internal(nargc, nargv, options)) == -2) { - char *current_argv = nargv[optind++] + 2, *has_equal; - int i, match = -1; + 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') { - return(-1); + 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, '='))) { + 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++) { - if (strncmp(current_argv, long_options[i].name, current_argv_len)) + /* 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) { + if (strlen(long_options[i].name) == + (unsigned)current_argv_len) { + /* exact match */ match = i; break; } - if (match == -1) + 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) { - if (long_options[match].has_arg) { + 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 + 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 == 1) && (optarg == NULL)) { - /* Missing option, leading : indecates no error */ - if ((opterr) && (*options != ':')) - (void)fprintf(stderr, - "%s: option requires an argument -- %s\n", - __progname(nargv[0]), current_argv); - return (BADARG); + 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 { /* No matching argument */ - if ((opterr) && (*options != ':')) - (void)fprintf(stderr, - "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv); - return (BADCH); + } 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 + } else retval = long_options[match].val; - if (index) - *index = match; - } - return(retval); -} -/*****************************************************************/ - - - - - - -#include <stdio.h> -#include "getopt.h" - -/* Stuff for getopt */ -static struct option long_options[] = { - { (char *)"simple", 0, NULL, 's' }, - { (char *)"t", 0, NULL, 't' }, - { (char *)"u", 1, NULL, 'u' }, - { (char *)"v", 0, NULL, 'v' }, - /* Do not reorder the following */ - { (char *)"yy", 0, NULL, 'Y' }, - { (char *)"y", 0, NULL, 'y' }, - { (char *)"zz", 0, NULL, 'z' }, - { (char *)"zzz", 0, NULL, 'Z' }, - { NULL, 0, NULL, 0 } -}; -extern char * optarg; -extern int optreset; -extern int optind; - -int test_getopt_long(args, expected_result) - char ** args, * expected_result; -{ - char actual_result[256]; - int count, pass, i; - - pass = 0; - optind = 1; - optreset = 1; - for (count = 0; args[count]; count++); - while ((i = getopt_long(count, args, (char *)"ab:", long_options, NULL)) != EOF) { - switch(i) { - case 'u': - if (strcmp(optarg, "bogus")) { - printf("--u option does not have bogus optarg.\n"); - return(1); - } - case 'Y': - case 's': - case 't': - case 'v': - case 'y': - case 'z': - actual_result[pass++] = i; - break; - default: - actual_result[pass++] = '?'; - break; - } - } - - actual_result[pass] = '\0'; - return(strcmp(actual_result, expected_result)); - -} - -#if 0 -int usage(value) - int value; -{ - printf("test_getopt [-d]\n"); - exit(value); -} -#endif - -#if 0 - -/* - * Static arglists for individual tests - * This is ugly and maybe I should just use a variable arglist - */ -const char *argv1[] = { "Test simple", "--s", NULL }; -const char *argv2[] = { "Test multiple", "--s", "--t", NULL }; -const char *argv3[] = { "Test optarg with space", "--u", "bogus", NULL }; -const char *argv4[] = { "Test optarg with equal", "--u=bogus", NULL }; -const char *argv5[] = { "Test complex", "--s", "--t", "--u", "bogus", "--v", NULL }; -const char *argv6[] = { "Test exact", "--y", NULL }; -const char *argv7[] = { "Test abbr", "--z", NULL }; -const char *argv8[] = { "Test simple termination", "--z", "foo", "--z", NULL }; -const char *argv9[] = { "Test -- termination", "--z", "--", "--z", NULL }; - -int debug = 0; -int main(argc, argv) - int argc; - char ** argv; -{ - int i; - - /* Of course if getopt() has a bug this won't work */ - while ((i = getopt(argc, argv, "d")) != EOF) { - switch(i) { - case 'd': - debug++; - break; - default: - usage(1); - break; - } - } - - /* Test getopt_long() */ - { - if (test_getopt_long(argv1, "s")) { - printf("Test simple failed.\n"); - exit(1); - } - } - - /* Test multiple arguments */ - { - if (test_getopt_long(argv2, "st")) { - printf("Test multiple failed.\n"); - exit(1); - } - } - - /* Test optarg with space */ - { - if (test_getopt_long(argv3, "u")) { - printf("Test optarg with space failed.\n"); - exit(1); - } - } - - /* Test optarg with equal */ - { - if (test_getopt_long(argv4, "u")) { - printf("Test optarg with equal failed.\n"); - exit(1); - } - } - - /* Test complex */ - { - if (test_getopt_long(argv5, "stuv")) { - printf("Test complex failed.\n"); - exit(1); - } - } - - /* Test that exact matches override abbr matches */ - { - if (test_getopt_long(argv6, "y")) { - printf("Test exact failed.\n"); - exit(1); - } - } - - /* Test that abbr matches are first match. */ - { - if (test_getopt_long(argv7, "z")) { - printf("Test abbr failed.\n"); - exit(1); - } - } - - /* Test that option termination succeeds */ - { - if (test_getopt_long(argv8, "z")) { - printf("Test simple termination failed.\n"); - exit(1); - } + if (idx) + *idx = match; } - - /* Test that "--" termination succeeds */ - { - if (test_getopt_long(argv9, "z")) { - printf("Test -- termination failed.\n"); - exit(1); - } - } - exit(0); + return retval; } -#endif |