getopt: use getopt32 for option parsing - inspired by patch by

Mats Erik Andersson <mats.andersson64@comhem.se>
function                                             old     new   delta
getopt_main                                          809     810      +1
static.BUFFER                                          4       -      -4
shell                                                  8       4      -4
quote                                                  4       -      -4
quiet_output                                           4       -      -4
quiet_errors                                           4       -      -4
long_options_nr                                        4       -      -4
long_options_length                                    4       -      -4
long_options                                         388     384      -4
alternative                                            4       -      -4
shortopts                                             15       -     -15
normalize                                            243     220     -23
.rodata                                           131832  131800     -32
add_longopt                                          200       -    -200
------------------------------------------------------------------------------
(add/remove: 0/9 grow/shrink: 1/4 up/down: 1/-306)           Total: -305 bytes
This commit is contained in:
Denis Vlasenko 2007-04-07 10:25:04 +00:00
parent fdf63a30fe
commit 9c146a91d3

View file

@ -42,25 +42,29 @@ enum {
LONG_OPT = 2 LONG_OPT = 2
}; };
/* The shells recognized. */ /* For finding activated option flags. Must match getopt32 call! */
typedef enum {BASH,TCSH} shell_t; enum {
OPT_o = 0x1, // -o
OPT_n = 0x2, // -n
OPT_q = 0x4, // -q
OPT_Q = 0x8, // -Q
OPT_s = 0x10, // -s
OPT_T = 0x20, // -T
OPT_u = 0x40, // -u
#if ENABLE_GETOPT_LONG
OPT_a = 0x80, // -a
OPT_l = 0x100, // -l
#endif
SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */
};
/* 0 is getopt_long, 1 is getopt_long_only */
#define alternative (option_mask32 & OPT_a)
/* Some global variables that tells us how to parse. */ #define quiet_errors (option_mask32 & OPT_q)
static shell_t shell=BASH; /* The shell we generate output for. */ #define quiet_output (option_mask32 & OPT_Q)
static int quiet_errors; /* 0 is not quiet. */ #define quote (!(option_mask32 & OPT_u))
static int quiet_output; /* 0 is not quiet. */ #define shell_TCSH (option_mask32 & SHELL_IS_TCSH)
static int quote=1; /* 1 is do quote. */
static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */
/* Function prototypes */
static const char *normalize(const char *arg);
static int generate_output(char **argv,int argc,const char *optstr,
const struct option *longopts);
static void add_long_options(char *options);
static void add_longopt(const char *name,int has_arg);
static void set_shell(const char *new_shell);
/* /*
* This function 'normalizes' a single argument: it puts single quotes around * This function 'normalizes' a single argument: it puts single quotes around
@ -71,16 +75,18 @@ static void set_shell(const char *new_shell);
* This function returns a pointer to a buffer that is overwritten by * This function returns a pointer to a buffer that is overwritten by
* each call. * each call.
*/ */
const char *normalize(const char *arg) static const char *normalize(const char *arg)
{ {
static char *BUFFER=NULL;
const char *argptr=arg;
char *bufptr; char *bufptr;
#if ENABLE_FEATURE_CLEAN_UP
static char *BUFFER = NULL;
free(BUFFER); free(BUFFER);
#else
char *BUFFER;
#endif
if (!quote) { /* Just copy arg */ if (!quote) { /* Just copy arg */
BUFFER=xstrdup(arg); BUFFER = xstrdup(arg);
return BUFFER; return BUFFER;
} }
@ -88,41 +94,41 @@ const char *normalize(const char *arg)
For a quote we need a closing quote, a backslash, a quote and an For a quote we need a closing quote, a backslash, a quote and an
opening quote! We need also the global opening and closing quote, opening quote! We need also the global opening and closing quote,
and one extra character for '\0'. */ and one extra character for '\0'. */
BUFFER=xmalloc(strlen(arg)*4+3); BUFFER = xmalloc(strlen(arg)*4 + 3);
bufptr=BUFFER; bufptr = BUFFER;
*bufptr++='\''; *bufptr ++= '\'';
while (*argptr) { while (*arg) {
if (*argptr == '\'') { if (*arg == '\'') {
/* Quote: replace it with: '\'' */ /* Quote: replace it with: '\'' */
*bufptr++='\''; *bufptr ++= '\'';
*bufptr++='\\'; *bufptr ++= '\\';
*bufptr++='\''; *bufptr ++= '\'';
*bufptr++='\''; *bufptr ++= '\'';
} else if (shell==TCSH && *argptr=='!') { } else if (shell_TCSH && *arg == '!') {
/* Exclamation mark: replace it with: \! */ /* Exclamation mark: replace it with: \! */
*bufptr++='\''; *bufptr ++= '\'';
*bufptr++='\\'; *bufptr ++= '\\';
*bufptr++='!'; *bufptr ++= '!';
*bufptr++='\''; *bufptr ++= '\'';
} else if (shell==TCSH && *argptr=='\n') { } else if (shell_TCSH && *arg == '\n') {
/* Newline: replace it with: \n */ /* Newline: replace it with: \n */
*bufptr++='\\'; *bufptr ++= '\\';
*bufptr++='n'; *bufptr ++= 'n';
} else if (shell==TCSH && isspace(*argptr)) { } else if (shell_TCSH && isspace(*arg)) {
/* Non-newline whitespace: replace it with \<ws> */ /* Non-newline whitespace: replace it with \<ws> */
*bufptr++='\''; *bufptr ++= '\'';
*bufptr++='\\'; *bufptr ++= '\\';
*bufptr++=*argptr; *bufptr ++= *arg;
*bufptr++='\''; *bufptr ++= '\'';
} else } else
/* Just copy */ /* Just copy */
*bufptr++=*argptr; *bufptr ++= *arg;
argptr++; arg++;
} }
*bufptr++='\''; *bufptr ++= '\'';
*bufptr++='\0'; *bufptr ++= '\0';
return BUFFER; return BUFFER;
} }
@ -133,132 +139,102 @@ const char *normalize(const char *arg)
* optstr must contain the short options, and longopts the long options. * optstr must contain the short options, and longopts the long options.
* Other settings are found in global variables. * Other settings are found in global variables.
*/ */
int generate_output(char **argv,int argc,const char *optstr, static int generate_output(char * argv[],int argc,const char *optstr,
const struct option *longopts) const struct option *longopts)
{ {
int exit_code = 0; /* We assume everything will be OK */ int exit_code = 0; /* We assume everything will be OK */
int opt; unsigned opt;
int longindex; int longindex;
const char *charptr; const char *charptr;
if (quiet_errors) /* No error reporting from getopt(3) */ if (quiet_errors) /* No error reporting from getopt(3) */
opterr=0; opterr = 0;
optind=0; /* Reset getopt(3) */ optind = 0; /* Reset getopt(3) */
while ((opt = (alternative? while ((opt = (alternative ?
getopt_long_only(argc,argv,optstr,longopts,&longindex): getopt_long_only(argc,argv,optstr,longopts,&longindex) :
getopt_long(argc,argv,optstr,longopts,&longindex))) getopt_long(argc,argv,optstr,longopts,&longindex)))
!= EOF) != EOF)
if (opt == '?' || opt == ':' ) if (opt == '?' || opt == ':' )
exit_code = 1; exit_code = 1;
else if (!quiet_output) { else if (!quiet_output) {
if (opt == LONG_OPT) { if (opt == LONG_OPT) {
printf(" --%s",longopts[longindex].name); printf(" --%s", longopts[longindex].name);
if (longopts[longindex].has_arg) if (longopts[longindex].has_arg)
printf(" %s", printf(" %s",
normalize(optarg?optarg:"")); normalize(optarg ? optarg : ""));
} else if (opt == NON_OPT) } else if (opt == NON_OPT)
printf(" %s",normalize(optarg)); printf(" %s", normalize(optarg));
else { else {
printf(" -%c",opt); printf(" -%c", opt);
charptr = strchr(optstr,opt); charptr = strchr(optstr,opt);
if (charptr != NULL && *++charptr == ':') if (charptr != NULL && *++charptr == ':')
printf(" %s", printf(" %s",
normalize(optarg?optarg:"")); normalize(optarg ? optarg : ""));
} }
} }
if (! quiet_output) { if (!quiet_output) {
printf(" --"); printf(" --");
while (optind < argc) while (optind < argc)
printf(" %s",normalize(argv[optind++])); printf(" %s", normalize(argv[optind++]));
puts(""); puts("");
} }
return exit_code; return exit_code;
} }
static struct option *long_options;
static int long_options_length; /* Length of array */
static int long_options_nr; /* Nr of used elements in array */
enum { LONG_OPTIONS_INCR = 10 };
#define init_longopt() add_longopt(NULL,0)
/* Register a long option. The contents of name is copied. */
void add_longopt(const char *name, int has_arg)
{
if (!name) { /* init */
free(long_options);
long_options=NULL;
long_options_length=0;
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);
}
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;
if (long_options_nr) { /* Not for init! */
long_options[long_options_nr-1].has_arg=has_arg;
long_options[long_options_nr-1].flag=NULL;
long_options[long_options_nr-1].val=LONG_OPT;
long_options[long_options_nr-1].name=xstrdup(name);
}
long_options_nr++;
}
/* /*
* Register several long options. options is a string of long options, * Register several long options. options is a string of long options,
* separated by commas or whitespace. * separated by commas or whitespace.
* This nukes options! * This nukes options!
*/ */
void add_long_options(char *options) static struct option *add_long_options(struct option *long_options, char *options)
{ {
int long_nr = 0;
int arg_opt, tlen; int arg_opt, tlen;
char *tokptr=strtok(options,", \t\n"); char *tokptr = strtok(options, ", \t\n");
if (long_options)
while (long_options[long_nr].name)
long_nr++;
while (tokptr) { while (tokptr) {
arg_opt=no_argument; arg_opt = no_argument;
tlen=strlen(tokptr); tlen = strlen(tokptr);
if (tlen > 0) { if (tlen) {
if (tokptr[tlen-1] == ':') { tlen--;
if (tlen > 1 && tokptr[tlen-2] == ':') { if (tokptr[tlen] == ':') {
tokptr[tlen-2]='\0'; arg_opt = required_argument;
tlen -= 2; if (tlen && tokptr[tlen-1] == ':') {
arg_opt=optional_argument; tlen--;
} else { arg_opt = optional_argument;
tokptr[tlen-1]='\0';
tlen -= 1;
arg_opt=required_argument;
} }
tokptr[tlen] = '\0';
if (tlen == 0) if (tlen == 0)
bb_error_msg("empty long option after -l or --long argument"); bb_error_msg_and_die("empty long option specified");
} }
add_longopt(tokptr,arg_opt); long_options = xrealloc(long_options,
sizeof(long_options[0]) * (long_nr+2));
long_options[long_nr].has_arg = arg_opt;
long_options[long_nr].flag = NULL;
long_options[long_nr].val = LONG_OPT;
long_options[long_nr].name = xstrdup(tokptr);
long_nr++;
memset(&long_options[long_nr], 0, sizeof(long_options[0]));
} }
tokptr=strtok(NULL,", \t\n"); tokptr = strtok(NULL, ", \t\n");
} }
return long_options;
} }
void set_shell(const char *new_shell) static void set_shell(const char *new_shell)
{ {
if (!strcmp(new_shell,"bash")) if (!strcmp(new_shell,"bash") || !strcmp(new_shell,"sh"))
shell=BASH; return;
else if (!strcmp(new_shell,"tcsh")) if (!strcmp(new_shell,"tcsh") || !strcmp(new_shell,"csh"))
shell=TCSH; option_mask32 |= SHELL_IS_TCSH;
else if (!strcmp(new_shell,"sh"))
shell=BASH;
else if (!strcmp(new_shell,"csh"))
shell=TCSH;
else else
bb_error_msg("unknown shell after -s or --shell argument"); bb_error_msg("unknown shell '%s', assuming bash", new_shell);
} }
@ -270,36 +246,35 @@ void set_shell(const char *new_shell)
* 4) Returned for -T * 4) Returned for -T
*/ */
static const struct option longopts[]= #if ENABLE_GETOPT_LONG
{ static const struct option longopts[] = {
{"options",required_argument,NULL,'o'}, { "options", required_argument, NULL, 'o' },
{"longoptions",required_argument,NULL,'l'}, { "longoptions", required_argument, NULL, 'l' },
{"quiet",no_argument,NULL,'q'}, { "quiet", no_argument, NULL, 'q' },
{"quiet-output",no_argument,NULL,'Q'}, { "quiet-output", no_argument, NULL, 'Q' },
{"shell",required_argument,NULL,'s'}, { "shell", required_argument, NULL, 's' },
{"test",no_argument,NULL,'T'}, { "test", no_argument, NULL, 'T' },
{"unquoted",no_argument,NULL,'u'}, { "unquoted", no_argument, NULL, 'u' },
{"alternative",no_argument,NULL,'a'}, { "alternative", no_argument, NULL, 'a' },
{"name",required_argument,NULL,'n'}, { "name", required_argument, NULL, 'n' },
{NULL,0,NULL,0} { NULL, 0, NULL, 0 }
}; };
#endif
/* Stop scanning as soon as a non-option argument is found! */ int getopt_main(int argc, char *argv[]);
static const char shortopts[]="+ao:l:n:qQs:Tu"; int getopt_main(int argc, char *argv[])
int getopt_main(int argc, char **argv);
int getopt_main(int argc, char **argv)
{ {
const char *optstr = NULL; struct option *long_options = NULL;
char *optstr = NULL;
char *name = NULL; char *name = NULL;
int opt; unsigned opt;
int compatible=0; const char *compatible;
char *s_arg;
#if ENABLE_GETOPT_LONG
llist_t *l_arg = NULL;
#endif
init_longopt(); compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
if (getenv("GETOPT_COMPATIBLE"))
compatible=1;
if (argc == 1) { if (argc == 1) {
if (compatible) { if (compatible) {
@ -307,60 +282,48 @@ int getopt_main(int argc, char **argv)
when there were no arguments. */ when there were no arguments. */
printf(" --\n"); printf(" --\n");
return 0; return 0;
} else }
bb_error_msg_and_die("missing optstring argument"); bb_error_msg_and_die("missing optstring argument");
} }
if (argv[1][0] != '-' || compatible) { if (argv[1][0] != '-' || compatible) {
char *s; char *s;
quote=0; option_mask32 |= OPT_u; /* quoting off */
s=xmalloc(strlen(argv[1])+1); s = xstrdup(argv[1] + strspn(argv[1], "-+"));
strcpy(s,argv[1]+strspn(argv[1],"-+")); argv[1] = argv[0];
argv[1]=argv[0]; return generate_output(argv+1, argc-1, s, long_options);
return generate_output(argv+1,argc-1,s,long_options);
} }
while ((opt = getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF) #if !ENABLE_GETOPT_LONG
switch (opt) { opt = getopt32(argc, argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
case 'a': #else
alternative=1; applet_long_options = longopts;
break; opt_complementary = "?:l::";
case 'o': opt = getopt32(argc, argv, "+o:n:qQs:Tual:",
optstr = optarg; &optstr, &name, &s_arg, &l_arg);
break; /* Effectuate the read options for the applet itself */
case 'l': while (l_arg) {
add_long_options(optarg); long_options = add_long_options(long_options, l_arg->data);
break; l_arg = l_arg->link;
case 'n': }
name = optarg; #endif
break;
case 'q':
quiet_errors=1;
break;
case 'Q':
quiet_output=1;
break;
case 's':
set_shell(optarg);
break;
case 'T':
return 4;
case 'u':
quote=0;
break;
default:
bb_show_usage();
}
if (opt & OPT_s) {
set_shell(s_arg);
}
if (opt & OPT_T) {
return 4;
}
/* All options controlling the applet have now been parsed */
if (!optstr) { if (!optstr) {
if (optind >= argc) if (optind >= argc)
bb_error_msg_and_die("missing optstring argument"); bb_error_msg_and_die("missing optstring argument");
else optstr=argv[optind++]; optstr = argv[optind++];
} }
if (name)
argv[optind-1]=name; argv[optind-1] = name ? name : argv[0];
else return generate_output(argv+optind-1, argc-optind+1, optstr, long_options);
argv[optind-1]=argv[0];
return generate_output(argv+optind-1,argc-optind+1,optstr,long_options);
} }