forked from mirrors/tftp-hpa-google
tftpd: switch to getopt_long()
Switch to using getopt_long(); include a version in case the platform lacks it. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
parent
77fbfeebee
commit
e4d3083006
6 changed files with 253 additions and 45 deletions
150
lib/getopt_long.c
Normal file
150
lib/getopt_long.c
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* getopt_long.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 "config.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 (*opt_end == '=') {
|
||||
if (lo->has_arg)
|
||||
optarg = (char *)opt_end+1;
|
||||
else
|
||||
return '?';
|
||||
} 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 '?';
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue