getopt: use getopt32 for option parsing - inspired by patch by Mats Erik Andersson
No binary (build failed)
—
(log yok)
| @@ -42,25 +42,29 @@ enum { | |
| LONG_OPT = 2 | |
| }; | |
| - | /* The shells recognized. */ |
| - | typedef enum {BASH,TCSH} shell_t; |
| - | |
| - | |
| - | /* 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 */ |
| + | /* 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 */ |
| + | }; |
| - | /* 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); |
| + | /* 0 is getopt_long, 1 is getopt_long_only */ |
| + | #define alternative (option_mask32 & OPT_a) |
| + | #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 \<ws> */ | |
| - | *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); |
| } | |