diff options
author | H. Peter Anvin <hpa@zytor.com> | 2006-08-23 20:08:32 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2006-08-23 20:08:32 -0700 |
commit | 93f023c6bbb6991b88427db98e065d1b92e1bad8 (patch) | |
tree | dcf83e1f414138280c455fe5b070742014899ef7 | |
parent | 548b94db18508c01874ed1b18cf3bb0044f6b884 (diff) | |
download | klibc-93f023c6bbb6991b88427db98e065d1b92e1bad8.tar.gz |
[klibc] Add getopt_long()klibc-1.4.23
Enough utilities want getopt_long(), so give it to them; at least
a reasonably simple variant.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | usr/klibc/Kbuild | 2 | ||||
-rw-r--r-- | usr/klibc/getopt_long.c | 150 |
2 files changed, 151 insertions, 1 deletions
diff --git a/usr/klibc/Kbuild b/usr/klibc/Kbuild index e6b96b6ebec4e..906caaed7e287 100644 --- a/usr/klibc/Kbuild +++ b/usr/klibc/Kbuild @@ -41,7 +41,7 @@ klib-y := vsnprintf.o snprintf.o vsprintf.o sprintf.o \ seteuid.o setegid.o \ getenv.o setenv.o putenv.o __put_env.o unsetenv.o \ clearenv.o nullenv.o \ - getopt.o readdir.o remove.o \ + getopt.o getopt_long.o readdir.o remove.o \ syslog.o closelog.o pty.o getpt.o isatty.o reboot.o \ time.o utime.o llseek.o nice.o getpriority.o \ qsort.o bsearch.o \ diff --git a/usr/klibc/getopt_long.c b/usr/klibc/getopt_long.c new file mode 100644 index 0000000000000..f7b8bdc495da9 --- /dev/null +++ b/usr/klibc/getopt_long.c @@ -0,0 +1,150 @@ +/* + * getopt.c + * + * getopt_long(), or at least a common subset thereof: + * + * - Option reordering is not supported + * - -W foo is not supported + * - First optstring character "-" not supported. + */ + +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> + +char *optarg; +int optind, opterr, optopt; +static struct getopt_private_state { + const char *optptr; + const char *last_optstring; + char *const *last_argv; +} pvt; + +static inline const char *option_matches(const char *arg_str, + const char *opt_name) +{ + while (*arg_str != '\0' && *arg_str != '=') { + if (*arg_str++ != *opt_name++) + return NULL; + } + + if (*opt_name) + return NULL; + + return arg_str; +} + +int getopt_long(int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longindex) +{ + const char *carg; + const char *osptr; + int opt; + + /* getopt() relies on a number of different global state + variables, which can make this really confusing if there is + more than one use of getopt() in the same program. This + attempts to detect that situation by detecting if the + "optstring" or "argv" argument have changed since last time + we were called; if so, reinitialize the query state. */ + + if (optstring != pvt.last_optstring || argv != pvt.last_argv || + optind < 1 || optind > argc) { + /* optind doesn't match the current query */ + pvt.last_optstring = optstring; + pvt.last_argv = argv; + optind = 1; + pvt.optptr = NULL; + } + + carg = argv[optind]; + + /* First, eliminate all non-option cases */ + + if (!carg || carg[0] != '-' || !carg[1]) { + return -1; + } + + if (carg[1] == '-') { + const struct option *lo; + const char *opt_end = NULL; + + optind++; + + /* Either it's a long option, or it's -- */ + if (!carg[2]) { + /* It's -- */ + return -1; + } + + for (lo = longopts; lo->name; lo++) { + if ((opt_end = option_matches(carg+2, lo->name))) + break; + } + if (opt_end) + return '?'; + + if (longindex) + *longindex = lo-longopts; + + if (lo->has_arg && *opt_end == '=') { + optarg = (char *)opt_end+1; + } else if (lo->has_arg == 1) { + if (!(optarg = argv[optind])) + return '?'; + optind++; + } + + if (lo->flag) { + *lo->flag = lo->val; + return 0; + } else { + return lo->val; + } + } + + if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) { + /* Someone frobbed optind, change to new opt. */ + pvt.optptr = carg + 1; + } + + opt = *pvt.optptr++; + + if (opt != ':' && (osptr = strchr(optstring, opt))) { + if (osptr[1] == ':') { + if (*pvt.optptr) { + /* Argument-taking option with attached + argument */ + optarg = (char *)pvt.optptr; + optind++; + } else { + /* Argument-taking option with non-attached + argument */ + if (argv[optind + 1]) { + optarg = (char *)argv[optind+1]; + optind += 2; + } else { + /* Missing argument */ + optind++; + return (optstring[0] == ':') + ? ':' : '?'; + } + } + return opt; + } else { + /* Non-argument-taking option */ + /* pvt.optptr will remember the exact position to + resume at */ + if (!*pvt.optptr) + optind++; + return opt; + } + } else { + /* Unknown option */ + optopt = opt; + if (!*pvt.optptr) + optind++; + return '?'; + } +} |