getopt: add struct getopt_control and remove global variables

CC: Frodo Looijaard <frodo@frodo.looijaard.name>
Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2014-12-06 11:32:46 +00:00
parent 5f69b38c5e
commit 1dacc901ec
1 changed files with 82 additions and 80 deletions

View File

@ -76,12 +76,20 @@
/* The shells recognized. */
typedef enum { BASH, TCSH } shell_t;
struct getopt_control {
shell_t shell; /* the shell we generate output for */
char *optstr; /* getopt(3) optstring */
struct option *long_options; /* long options */
int long_options_length; /* length of options array */
int long_options_nr; /* number of used elements in array */
unsigned int
compatible:1, /* compatibility mode for 'difficult' programs */
quiet_errors:1, /* print errors */
quiet_output:1, /* print output */
quote:1; /* quote output */
};
/* Some global variables that tells us how to parse. */
static shell_t shell = BASH; /* The shell we generate output for. */
static int quiet_errors = 0; /* 0 is not quiet. */
static int quiet_output = 0; /* 0 is not quiet. */
static int quote = 1; /* 1 is do quote. */
enum { REALLOC_INCREMENT = 8 };
/* Allow changing which getopt is in use with function pointer */
int (*getopt_long_fp) (int argc, char *const *argv, const char *optstr,
@ -96,7 +104,7 @@ int (*getopt_long_fp) (int argc, char *const *argv, const char *optstr,
* exclamation marks within single quotes, and nukes whitespace. This
* function returns a pointer to a buffer that is overwritten by each call.
*/
static const char *normalize(const char *arg)
static const char *normalize(const struct getopt_control *ctl, const char *arg)
{
static char *BUFFER = NULL;
const char *argptr = arg;
@ -104,7 +112,7 @@ static const char *normalize(const char *arg)
free(BUFFER);
if (!quote) {
if (!ctl->quote) {
/* Just copy arg */
BUFFER = xmalloc(strlen(arg) + 1);
strcpy(BUFFER, arg);
@ -129,21 +137,21 @@ static const char *normalize(const char *arg)
*bufptr++ = '\\';
*bufptr++ = '\'';
*bufptr++ = '\'';
} else if (shell == TCSH && *argptr == '\\') {
} else if (ctl->shell == TCSH && *argptr == '\\') {
/* Backslash: replace it with: '\\' */
*bufptr++ = '\\';
*bufptr++ = '\\';
} else if (shell == TCSH && *argptr == '!') {
} else if (ctl->shell == TCSH && *argptr == '!') {
/* Exclamation mark: replace it with: \! */
*bufptr++ = '\'';
*bufptr++ = '\\';
*bufptr++ = '!';
*bufptr++ = '\'';
} else if (shell == TCSH && *argptr == '\n') {
} else if (ctl->shell == TCSH && *argptr == '\n') {
/* Newline: replace it with: \n */
*bufptr++ = '\\';
*bufptr++ = 'n';
} else if (shell == TCSH && isspace(*argptr)) {
} else if (ctl->shell == TCSH && isspace(*argptr)) {
/* Non-newline whitespace: replace it with \<ws> */
*bufptr++ = '\'';
*bufptr++ = '\\';
@ -166,44 +174,43 @@ static const char *normalize(const char *arg)
* optstr must contain the short options, and longopts the long options.
* Other settings are found in global variables.
*/
static int generate_output(char *argv[], int argc, const char *optstr,
const struct option *longopts)
static int generate_output(const struct getopt_control *ctl, char *argv[], int argc)
{
int exit_code = EXIT_SUCCESS; /* Assume everything will be OK */
int opt;
int longindex;
const char *charptr;
if (quiet_errors)
if (ctl->quiet_errors)
/* No error reporting from getopt(3) */
opterr = 0;
/* Reset getopt(3) */
optind = 0;
while ((opt =
(getopt_long_fp(argc, argv, optstr, longopts, &longindex)))
(getopt_long_fp(argc, argv, ctl->optstr, ctl->long_options, &longindex)))
!= EOF)
if (opt == '?' || opt == ':')
exit_code = GETOPT_EXIT_CODE;
else if (!quiet_output) {
else if (!ctl->quiet_output) {
if (opt == LONG_OPT) {
printf(" --%s", longopts[longindex].name);
if (longopts[longindex].has_arg)
printf(" %s", normalize(optarg ? optarg : ""));
printf(" --%s", ctl->long_options[longindex].name);
if (ctl->long_options[longindex].has_arg)
printf(" %s", normalize(ctl, optarg ? optarg : ""));
} else if (opt == NON_OPT)
printf(" %s", normalize(optarg ? optarg : ""));
printf(" %s", normalize(ctl, optarg ? optarg : ""));
else {
printf(" -%c", opt);
charptr = strchr(optstr, opt);
charptr = strchr(ctl->optstr, opt);
if (charptr != NULL && *++charptr == ':')
printf(" %s", normalize(optarg ? optarg : ""));
printf(" %s", normalize(ctl, optarg ? optarg : ""));
}
}
if (!quiet_output) {
if (!ctl->quiet_output) {
printf(" --");
while (optind < argc)
printf(" %s", normalize(argv[optind++]));
printf(" %s", normalize(ctl, argv[optind++]));
printf("\n");
}
return exit_code;
@ -222,48 +229,43 @@ static void __attribute__ ((__noreturn__)) parse_error(const char *message)
exit(PARAMETER_EXIT_CODE);
}
static struct option *long_options = NULL;
static int long_options_length = 0; /* Length of array */
static int long_options_nr = 0; /* Nr of used elements in array */
#define LONG_OPTIONS_INCR 10
#define init_longopt() add_longopt(NULL,0)
/* Register a long option. The contents of name is copied. */
static void add_longopt(const char *name, int has_arg)
static void add_longopt(struct getopt_control *ctl, const char *name, int has_arg)
{
char *tmp;
static int flag;
if (!name) {
/* init */
free(long_options);
long_options = NULL;
long_options_length = 0;
long_options_nr = 0;
free(ctl->long_options);
ctl->long_options = NULL;
ctl->long_options_length = 0;
ctl->long_options_nr = 0;
}
if (long_options_nr == long_options_length) {
long_options_length += LONG_OPTIONS_INCR;
long_options = xrealloc(long_options,
sizeof(struct option) *
long_options_length);
if (ctl->long_options_nr == ctl->long_options_length) {
ctl->long_options_length += REALLOC_INCREMENT;
ctl->long_options = xrealloc(ctl->long_options,
sizeof(struct option) *
ctl->long_options_length);
}
long_options[long_options_nr].name = NULL;
long_options[long_options_nr].has_arg = 0;
long_options[long_options_nr].flag = NULL;
long_options[long_options_nr].val = 0;
ctl->long_options[ctl->long_options_nr].name = NULL;
ctl->long_options[ctl->long_options_nr].has_arg = 0;
ctl->long_options[ctl->long_options_nr].flag = NULL;
ctl->long_options[ctl->long_options_nr].val = 0;
if (long_options_nr && name) {
if (ctl->long_options_nr && name) {
/* Not for init! */
long_options[long_options_nr - 1].has_arg = has_arg;
long_options[long_options_nr - 1].flag = &flag;
long_options[long_options_nr - 1].val = long_options_nr;
ctl->long_options[ctl->long_options_nr - 1].has_arg = has_arg;
ctl->long_options[ctl->long_options_nr - 1].flag = &flag;
ctl->long_options[ctl->long_options_nr - 1].val = ctl->long_options_nr;
tmp = xmalloc(strlen(name) + 1);
strcpy(tmp, name);
long_options[long_options_nr - 1].name = tmp;
ctl->long_options[ctl->long_options_nr - 1].name = tmp;
}
long_options_nr++;
ctl->long_options_nr++;
}
@ -271,7 +273,7 @@ static void add_longopt(const char *name, int has_arg)
* Register several long options. options is a string of long options,
* separated by commas or whitespace. This nukes options!
*/
static void add_long_options(char *options)
static void add_long_options(struct getopt_control *ctl, char *options)
{
int arg_opt;
char *tokptr = strtok(options, ", \t\n");
@ -291,22 +293,22 @@ static void add_long_options(char *options)
("empty long option after "
"-l or --long argument"));
}
add_longopt(tokptr, arg_opt);
add_longopt(ctl, tokptr, arg_opt);
}
tokptr = strtok(NULL, ", \t\n");
}
}
static void set_shell(const char *new_shell)
static void set_shell(struct getopt_control *ctl, const char *new_shell)
{
if (!strcmp(new_shell, "bash"))
shell = BASH;
ctl->shell = BASH;
else if (!strcmp(new_shell, "tcsh"))
shell = TCSH;
ctl->shell = TCSH;
else if (!strcmp(new_shell, "sh"))
shell = BASH;
ctl->shell = BASH;
else if (!strcmp(new_shell, "csh"))
shell = TCSH;
ctl->shell = TCSH;
else
parse_error(_
("unknown shell after -s or --shell argument"));
@ -340,10 +342,12 @@ static void __attribute__ ((__noreturn__)) print_help(void)
int main(int argc, char *argv[])
{
char *optstr = NULL;
struct getopt_control ctl = {
.shell = BASH,
.quote = 1
};
char *name = NULL;
int opt;
int compatible = 0;
/* Stop scanning as soon as a non-option argument is found! */
static const char *shortopts = "+ao:l:n:qQs:TuhV";
@ -367,14 +371,14 @@ int main(int argc, char *argv[])
textdomain(PACKAGE);
atexit(close_stdout);
init_longopt();
add_longopt(&ctl, NULL, 0); /* init */
getopt_long_fp = getopt_long;
if (getenv("GETOPT_COMPATIBLE"))
compatible = 1;
ctl.compatible = 1;
if (argc == 1) {
if (compatible) {
if (ctl.compatible) {
/*
* For some reason, the original getopt gave no
* error when there were no arguments.
@ -385,13 +389,12 @@ int main(int argc, char *argv[])
parse_error(_("missing optstring argument"));
}
if (argv[1][0] != '-' || compatible) {
quote = 0;
optstr = xmalloc(strlen(argv[1]) + 1);
strcpy(optstr, argv[1] + strspn(argv[1], "-+"));
if (argv[1][0] != '-' || ctl.compatible) {
ctl.quote = 0;
ctl.optstr = xmalloc(strlen(argv[1]) + 1);
strcpy(ctl.optstr, argv[1] + strspn(argv[1], "-+"));
argv[1] = argv[0];
return generate_output(argv + 1, argc - 1, optstr,
long_options);
return generate_output(&ctl, argv + 1, argc - 1);
}
while ((opt =
@ -403,12 +406,12 @@ int main(int argc, char *argv[])
case 'h':
print_help();
case 'o':
free(optstr);
optstr = xmalloc(strlen(optarg) + 1);
strcpy(optstr, optarg);
free(ctl.optstr);
ctl.optstr = xmalloc(strlen(optarg) + 1);
strcpy(ctl.optstr, optarg);
break;
case 'l':
add_long_options(optarg);
add_long_options(&ctl, optarg);
break;
case 'n':
free(name);
@ -416,18 +419,18 @@ int main(int argc, char *argv[])
strcpy(name, optarg);
break;
case 'q':
quiet_errors = 1;
ctl.quiet_errors = 1;
break;
case 'Q':
quiet_output = 1;
ctl.quiet_output = 1;
break;
case 's':
set_shell(optarg);
set_shell(&ctl, optarg);
break;
case 'T':
return TEST_EXIT_CODE;
case 'u':
quote = 0;
ctl.quote = 0;
break;
case 'V':
printf(UTIL_LINUX_VERSION);
@ -439,12 +442,12 @@ int main(int argc, char *argv[])
parse_error(_("internal error, contact the author."));
}
if (!optstr) {
if (!ctl.optstr) {
if (optind >= argc)
parse_error(_("missing optstring argument"));
else {
optstr = xmalloc(strlen(argv[optind]) + 1);
strcpy(optstr, argv[optind]);
ctl.optstr = xmalloc(strlen(argv[optind]) + 1);
strcpy(ctl.optstr, argv[optind]);
optind++;
}
}
@ -453,6 +456,5 @@ int main(int argc, char *argv[])
else
argv[optind - 1] = argv[0];
return generate_output(argv + optind - 1, argc-optind + 1,
optstr, long_options);
return generate_output(&ctl, argv + optind - 1, argc - optind + 1);
}