From 9c146a91d3c1ae3b561be39b792bbd45447b904c Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sat, 7 Apr 2007 10:25:04 +0000 Subject: [PATCH] getopt: use getopt32 for option parsing - inspired by patch by Mats Erik Andersson 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 --- util-linux/getopt.c | 353 ++++++++++++++++++++------------------------ 1 file changed, 158 insertions(+), 195 deletions(-) diff --git a/util-linux/getopt.c b/util-linux/getopt.c index 74e7235ea..fefa02206 100644 --- a/util-linux/getopt.c +++ b/util-linux/getopt.c @@ -42,25 +42,29 @@ enum { LONG_OPT = 2 }; -/* The shells recognized. */ -typedef enum {BASH,TCSH} shell_t; +/* For finding activated option flags. Must match getopt32 call! */ +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. */ -static shell_t shell=BASH; /* The shell we generate output for. */ -static int quiet_errors; /* 0 is not quiet. */ -static int quiet_output; /* 0 is not quiet. */ -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); - +#define quiet_errors (option_mask32 & OPT_q) +#define quiet_output (option_mask32 & OPT_Q) +#define quote (!(option_mask32 & OPT_u)) +#define shell_TCSH (option_mask32 & SHELL_IS_TCSH) /* * 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 * 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; - +#if ENABLE_FEATURE_CLEAN_UP + static char *BUFFER = NULL; free(BUFFER); +#else + char *BUFFER; +#endif if (!quote) { /* Just copy arg */ - BUFFER=xstrdup(arg); + BUFFER = xstrdup(arg); 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 opening quote! We need also the global opening and closing quote, and one extra character for '\0'. */ - BUFFER=xmalloc(strlen(arg)*4+3); + BUFFER = xmalloc(strlen(arg)*4 + 3); - bufptr=BUFFER; - *bufptr++='\''; + bufptr = BUFFER; + *bufptr ++= '\''; - while (*argptr) { - if (*argptr == '\'') { + while (*arg) { + if (*arg == '\'') { /* Quote: replace it with: '\'' */ - *bufptr++='\''; - *bufptr++='\\'; - *bufptr++='\''; - *bufptr++='\''; - } else if (shell==TCSH && *argptr=='!') { + *bufptr ++= '\''; + *bufptr ++= '\\'; + *bufptr ++= '\''; + *bufptr ++= '\''; + } else if (shell_TCSH && *arg == '!') { /* Exclamation mark: replace it with: \! */ - *bufptr++='\''; - *bufptr++='\\'; - *bufptr++='!'; - *bufptr++='\''; - } else if (shell==TCSH && *argptr=='\n') { + *bufptr ++= '\''; + *bufptr ++= '\\'; + *bufptr ++= '!'; + *bufptr ++= '\''; + } else if (shell_TCSH && *arg == '\n') { /* Newline: replace it with: \n */ - *bufptr++='\\'; - *bufptr++='n'; - } else if (shell==TCSH && isspace(*argptr)) { + *bufptr ++= '\\'; + *bufptr ++= 'n'; + } else if (shell_TCSH && isspace(*arg)) { /* Non-newline whitespace: replace it with \ */ - *bufptr++='\''; - *bufptr++='\\'; - *bufptr++=*argptr; - *bufptr++='\''; + *bufptr ++= '\''; + *bufptr ++= '\\'; + *bufptr ++= *arg; + *bufptr ++= '\''; } else /* Just copy */ - *bufptr++=*argptr; - argptr++; + *bufptr ++= *arg; + arg++; } - *bufptr++='\''; - *bufptr++='\0'; + *bufptr ++= '\''; + *bufptr ++= '\0'; return BUFFER; } @@ -133,132 +139,102 @@ const char *normalize(const char *arg) * optstr must contain the short options, and longopts the long options. * 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) { int exit_code = 0; /* We assume everything will be OK */ - int opt; + unsigned opt; int longindex; const char *charptr; if (quiet_errors) /* No error reporting from getopt(3) */ - opterr=0; - optind=0; /* Reset getopt(3) */ + opterr = 0; + optind = 0; /* Reset getopt(3) */ - while ((opt = (alternative? - getopt_long_only(argc,argv,optstr,longopts,&longindex): + while ((opt = (alternative ? + getopt_long_only(argc,argv,optstr,longopts,&longindex) : getopt_long(argc,argv,optstr,longopts,&longindex))) != EOF) if (opt == '?' || opt == ':' ) exit_code = 1; else if (!quiet_output) { if (opt == LONG_OPT) { - printf(" --%s",longopts[longindex].name); + printf(" --%s", longopts[longindex].name); if (longopts[longindex].has_arg) printf(" %s", - normalize(optarg?optarg:"")); + normalize(optarg ? optarg : "")); } else if (opt == NON_OPT) - printf(" %s",normalize(optarg)); + printf(" %s", normalize(optarg)); else { - printf(" -%c",opt); + printf(" -%c", opt); charptr = strchr(optstr,opt); if (charptr != NULL && *++charptr == ':') printf(" %s", - normalize(optarg?optarg:"")); + normalize(optarg ? optarg : "")); } } - if (! quiet_output) { + if (!quiet_output) { printf(" --"); while (optind < argc) - printf(" %s",normalize(argv[optind++])); + printf(" %s", normalize(argv[optind++])); puts(""); } 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, * separated by commas or whitespace. * 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; - 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) { - arg_opt=no_argument; - tlen=strlen(tokptr); - if (tlen > 0) { - if (tokptr[tlen-1] == ':') { - if (tlen > 1 && tokptr[tlen-2] == ':') { - tokptr[tlen-2]='\0'; - tlen -= 2; - arg_opt=optional_argument; - } else { - tokptr[tlen-1]='\0'; - tlen -= 1; - arg_opt=required_argument; + arg_opt = no_argument; + tlen = strlen(tokptr); + if (tlen) { + tlen--; + if (tokptr[tlen] == ':') { + arg_opt = required_argument; + if (tlen && tokptr[tlen-1] == ':') { + tlen--; + arg_opt = optional_argument; } + tokptr[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")) - shell=BASH; - else if (!strcmp(new_shell,"tcsh")) - shell=TCSH; - else if (!strcmp(new_shell,"sh")) - shell=BASH; - else if (!strcmp(new_shell,"csh")) - shell=TCSH; + if (!strcmp(new_shell,"bash") || !strcmp(new_shell,"sh")) + return; + if (!strcmp(new_shell,"tcsh") || !strcmp(new_shell,"csh")) + option_mask32 |= SHELL_IS_TCSH; 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 */ -static const struct option longopts[]= -{ - {"options",required_argument,NULL,'o'}, - {"longoptions",required_argument,NULL,'l'}, - {"quiet",no_argument,NULL,'q'}, - {"quiet-output",no_argument,NULL,'Q'}, - {"shell",required_argument,NULL,'s'}, - {"test",no_argument,NULL,'T'}, - {"unquoted",no_argument,NULL,'u'}, - {"alternative",no_argument,NULL,'a'}, - {"name",required_argument,NULL,'n'}, - {NULL,0,NULL,0} +#if ENABLE_GETOPT_LONG +static const struct option longopts[] = { + { "options", required_argument, NULL, 'o' }, + { "longoptions", required_argument, NULL, 'l' }, + { "quiet", no_argument, NULL, 'q' }, + { "quiet-output", no_argument, NULL, 'Q' }, + { "shell", required_argument, NULL, 's' }, + { "test", no_argument, NULL, 'T' }, + { "unquoted", no_argument, NULL, 'u' }, + { "alternative", no_argument, NULL, 'a' }, + { "name", required_argument, NULL, 'n' }, + { NULL, 0, NULL, 0 } }; +#endif -/* Stop scanning as soon as a non-option argument is found! */ -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[]); +int getopt_main(int argc, char *argv[]) { - const char *optstr = NULL; + struct option *long_options = NULL; + char *optstr = NULL; char *name = NULL; - int opt; - int compatible=0; + unsigned opt; + const char *compatible; + char *s_arg; +#if ENABLE_GETOPT_LONG + llist_t *l_arg = NULL; +#endif - init_longopt(); - - if (getenv("GETOPT_COMPATIBLE")) - compatible=1; + compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */ if (argc == 1) { if (compatible) { @@ -307,60 +282,48 @@ int getopt_main(int argc, char **argv) when there were no arguments. */ printf(" --\n"); return 0; - } else - bb_error_msg_and_die("missing optstring argument"); + } + bb_error_msg_and_die("missing optstring argument"); } if (argv[1][0] != '-' || compatible) { char *s; - quote=0; - s=xmalloc(strlen(argv[1])+1); - strcpy(s,argv[1]+strspn(argv[1],"-+")); - argv[1]=argv[0]; - return generate_output(argv+1,argc-1,s,long_options); + option_mask32 |= OPT_u; /* quoting off */ + s = xstrdup(argv[1] + strspn(argv[1], "-+")); + argv[1] = argv[0]; + return generate_output(argv+1, argc-1, s, long_options); } - while ((opt = getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF) - switch (opt) { - case 'a': - alternative=1; - break; - case 'o': - optstr = optarg; - break; - case 'l': - add_long_options(optarg); - break; - case 'n': - name = optarg; - 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 !ENABLE_GETOPT_LONG + opt = getopt32(argc, argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg); +#else + applet_long_options = longopts; + opt_complementary = "?:l::"; + opt = getopt32(argc, argv, "+o:n:qQs:Tual:", + &optstr, &name, &s_arg, &l_arg); + /* Effectuate the read options for the applet itself */ + while (l_arg) { + long_options = add_long_options(long_options, l_arg->data); + l_arg = l_arg->link; + } +#endif + 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 (optind >= argc) bb_error_msg_and_die("missing optstring argument"); - else optstr=argv[optind++]; + optstr = argv[optind++]; } - if (name) - argv[optind-1]=name; - else - argv[optind-1]=argv[0]; - return generate_output(argv+optind-1,argc-optind+1,optstr,long_options); + + argv[optind-1] = name ? name : argv[0]; + return generate_output(argv+optind-1, argc-optind+1, optstr, long_options); }