X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=util-linux%2Fgetopt.c;h=1666d3d301ac86b4a165a5defd20abb6a65a7412;hb=498cec202adbf69a7a72af5e204260682d614183;hp=032d0dc6be5146827ed0ae7174271113783d4e45;hpb=d4004ee6a933eaf3d3843624d8c63e922db8d7dd;p=oweals%2Fbusybox.git diff --git a/util-linux/getopt.c b/util-linux/getopt.c index 032d0dc6b..1666d3d30 100644 --- a/util-linux/getopt.c +++ b/util-linux/getopt.c @@ -1,22 +1,10 @@ +/* vi: set sw=4 ts=4: */ /* * getopt.c - Enhanced implementation of BSD getopt(1) - * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard + * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ - /* * Version 1.0-b4: Tue Sep 23 1997. First public release. * Version 1.0: Wed Nov 19 1997. @@ -33,49 +21,128 @@ * Version 1.0.6: Tue Jun 27 2000 * No important changes * Version 1.1.0: Tue Jun 30 2000 - * Added NLS support (partly written by Arkadiusz Mikiewicz + * Added NLS support (partly written by Arkadiusz Mickiewicz * ) * Ported to Busybox - Alfred M. Szmidt - * Removed --version/-V and --help/-h in + * Removed --version/-V and --help/-h * Removed parse_error(), using bb_error_msg() from Busybox instead * Replaced our_malloc with xmalloc and our_realloc with xrealloc - * */ - -#include -#include -#include -#include -#include -#include - -#include "busybox.h" +//config:config GETOPT +//config: bool "getopt (5.8 kb)" +//config: default y +//config: help +//config: The getopt utility is used to break up (parse) options in command +//config: lines to make it easy to write complex shell scripts that also check +//config: for legal (and illegal) options. If you want to write horribly +//config: complex shell scripts, or use some horribly complex shell script +//config: written by others, this utility may be for you. Most people will +//config: wisely leave this disabled. +//config: +//config:config FEATURE_GETOPT_LONG +//config: bool "Support -l LONGOPTs" +//config: default y +//config: depends on GETOPT && LONG_OPTS +//config: help +//config: Enable support for long options (option -l). + +//applet:IF_GETOPT(APPLET_NOEXEC(getopt, getopt, BB_DIR_BIN, BB_SUID_DROP, getopt)) + +//kbuild:lib-$(CONFIG_GETOPT) += getopt.o + +//usage:#define getopt_trivial_usage +//usage: "[OPTIONS] [--] OPTSTRING PARAMS" +//usage:#define getopt_full_usage "\n\n" +//usage: IF_FEATURE_GETOPT_LONG( +//usage: " -a Allow long options starting with single -\n" +//usage: " -l LOPT[,...] Long options to recognize\n" +//usage: ) +//usage: " -n PROGNAME The name under which errors are reported" +//usage: "\n -o OPTSTRING Short options to recognize" +//usage: "\n -q No error messages on unrecognized options" +//usage: "\n -Q No normal output" +//usage: "\n -s SHELL Set shell quoting conventions" +//usage: "\n -T Version test (exits with 4)" +//usage: "\n -u Don't quote output" +//usage: IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */ +//usage: "\n" +//usage: "\nExample:" +//usage: "\n" +//usage: "\nO=`getopt -l bb: -- ab:c:: \"$@\"` || exit 1" +//usage: "\neval set -- \"$O\"" +//usage: "\nwhile true; do" +//usage: "\n case \"$1\" in" +//usage: "\n -a) echo A; shift;;" +//usage: "\n -b|--bb) echo \"B:'$2'\"; shift 2;;" +//usage: "\n -c) case \"$2\" in" +//usage: "\n \"\") echo C; shift 2;;" +//usage: "\n *) echo \"C:'$2'\"; shift 2;;" +//usage: "\n esac;;" +//usage: "\n --) shift; break;;" +//usage: "\n *) echo Error; exit 1;;" +//usage: "\n esac" +//usage: "\ndone" +//usage: ) +//usage: +//usage:#define getopt_example_usage +//usage: "$ cat getopt.test\n" +//usage: "#!/bin/sh\n" +//usage: "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" +//usage: " -n 'example.busybox' -- \"$@\"`\n" +//usage: "if [ $? != 0 ]; then exit 1; fi\n" +//usage: "eval set -- \"$GETOPT\"\n" +//usage: "while true; do\n" +//usage: " case $1 in\n" +//usage: " -a|--a-long) echo \"Option a\"; shift;;\n" +//usage: " -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n" +//usage: " -c|--c-long)\n" +//usage: " case \"$2\" in\n" +//usage: " \"\") echo \"Option c, no argument\"; shift 2;;\n" +//usage: " *) echo \"Option c, argument '$2'\"; shift 2;;\n" +//usage: " esac;;\n" +//usage: " --) shift; break;;\n" +//usage: " *) echo \"Internal error!\"; exit 1;;\n" +//usage: " esac\n" +//usage: "done\n" + +#if ENABLE_FEATURE_GETOPT_LONG +# include +#endif +#include "libbb.h" /* NON_OPT is the code that is returned when a non-option is found in '+' mode */ -static const int NON_OPT = 1; +enum { + NON_OPT = 1, +#if ENABLE_FEATURE_GETOPT_LONG /* LONG_OPT is the code that is returned when a long option is found. */ -static const int LONG_OPT = 2; - -/* The shells recognized. */ -typedef enum {BASH,TCSH} shell_t; - + LONG_OPT = 2 +#endif +}; -/* 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. */ -static int alternative=0; /* 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_FEATURE_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 @@ -86,59 +153,61 @@ 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; - - free(BUFFER); - - if (!quote) { /* Just copy arg */ - BUFFER=bb_xstrdup(arg); - return BUFFER; - } - - /* Each character in arg may take up to four characters in the result: - 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); - - bufptr=BUFFER; - *bufptr++='\''; - - while (*argptr) { - if (*argptr == '\'') { - /* Quote: replace it with: '\'' */ - *bufptr++='\''; - *bufptr++='\\'; - *bufptr++='\''; - *bufptr++='\''; - } else if (shell==TCSH && *argptr=='!') { - /* Exclamation mark: replace it with: \! */ - *bufptr++='\''; - *bufptr++='\\'; - *bufptr++='!'; - *bufptr++='\''; - } else if (shell==TCSH && *argptr=='\n') { - /* Newline: replace it with: \n */ - *bufptr++='\\'; - *bufptr++='n'; - } else if (shell==TCSH && isspace(*argptr)) { - /* Non-newline whitespace: replace it with \ */ - *bufptr++='\''; - *bufptr++='\\'; - *bufptr++=*argptr; - *bufptr++='\''; - } else - /* Just copy */ - *bufptr++=*argptr; - argptr++; - } - *bufptr++='\''; - *bufptr++='\0'; - return BUFFER; + 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); + return BUFFER; + } + + /* Each character in arg may take up to four characters in the result: + 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); + + bufptr = BUFFER; + *bufptr ++= '\''; + + while (*arg) { + if (*arg == '\'') { + /* Quote: replace it with: '\'' */ + *bufptr ++= '\''; + *bufptr ++= '\\'; + *bufptr ++= '\''; + *bufptr ++= '\''; + } else if (shell_TCSH && *arg == '!') { + /* Exclamation mark: replace it with: \! */ + *bufptr ++= '\''; + *bufptr ++= '\\'; + *bufptr ++= '!'; + *bufptr ++= '\''; + } else if (shell_TCSH && *arg == '\n') { + /* Newline: replace it with: \n */ + *bufptr ++= '\\'; + *bufptr ++= 'n'; + } else if (shell_TCSH && isspace(*arg)) { + /* Non-newline whitespace: replace it with \ */ + *bufptr ++= '\''; + *bufptr ++= '\\'; + *bufptr ++= *arg; + *bufptr ++= '\''; + } else + /* Just copy */ + *bufptr ++= *arg; + arg++; + } + *bufptr ++= '\''; + *bufptr ++= '\0'; + return BUFFER; } /* @@ -148,132 +217,121 @@ 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, - const struct option *longopts) -{ - int exit_code = 0; /* We assume everything will be OK */ - int opt; - int longindex; - const char *charptr; - - if (quiet_errors) /* No error reporting from getopt(3) */ - opterr=0; - optind=0; /* Reset getopt(3) */ - - 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); - if (longopts[longindex].has_arg) - printf(" %s", - normalize(optarg?optarg:"")); - } else if (opt == NON_OPT) - printf(" %s",normalize(optarg)); - else { - printf(" -%c",opt); - charptr = strchr(optstr,opt); - if (charptr != NULL && *++charptr == ':') - printf(" %s", - normalize(optarg?optarg:"")); - } - } - - if (! quiet_output) { - printf(" --"); - while (optind < argc) - printf(" %s",normalize(argv[optind++])); - printf("\n"); - } - return 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 */ -static const int 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 !ENABLE_FEATURE_GETOPT_LONG +#define generate_output(argv,argc,optstr,longopts) \ + generate_output(argv,argc,optstr) +#endif +static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts) { - 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=bb_xstrdup(name); - } - long_options_nr++; + int exit_code = 0; /* We assume everything will be OK */ + + if (quiet_errors) /* No error reporting from getopt(3) */ + opterr = 0; + + /* We used it already in main() in getopt32(), + * we *must* reset getopt(3): */ + GETOPT_RESET(); + + while (1) { +#if ENABLE_FEATURE_GETOPT_LONG + int longindex; + int opt = alternative + ? getopt_long_only(argc, argv, optstr, longopts, &longindex) + : getopt_long(argc, argv, optstr, longopts, &longindex) + ; +#else + int opt = getopt(argc, argv, optstr); +#endif + if (opt == -1) + break; + if (opt == '?' || opt == ':' ) + exit_code = 1; + else if (!quiet_output) { +#if ENABLE_FEATURE_GETOPT_LONG + if (opt == LONG_OPT) { + printf(" --%s", longopts[longindex].name); + if (longopts[longindex].has_arg) + printf(" %s", + normalize(optarg ? optarg : "")); + } else +#endif + if (opt == NON_OPT) + printf(" %s", normalize(optarg)); + else { + const char *charptr; + printf(" -%c", opt); + charptr = strchr(optstr, opt); + if (charptr && *++charptr == ':') + printf(" %s", + normalize(optarg ? optarg : "")); + } + } + } + + if (!quiet_output) { + unsigned idx; + printf(" --"); + idx = optind; + while (argv[idx]) + printf(" %s", normalize(argv[idx++])); + bb_putchar('\n'); + } + return exit_code; } - +#if ENABLE_FEATURE_GETOPT_LONG /* * 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 arg_opt, tlen; - char *tokptr=strtok(options,", \t\n"); - 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; - } - if (tlen == 0) - bb_error_msg("empty long option after -l or --long argument"); - } - add_longopt(tokptr,arg_opt); - } - tokptr=strtok(NULL,", \t\n"); - } + int long_nr = 0; + int arg_opt, tlen; + 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) { + 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_and_die("empty long option specified"); + } + long_options = xrealloc_vector(long_options, 4, long_nr); + long_options[long_nr].has_arg = arg_opt; + /*long_options[long_nr].flag = NULL; - xrealloc_vector did it */ + 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])); - xrealloc_vector did it */ + } + tokptr = strtok(NULL, ", \t\n"); + } + return long_options; } +#endif -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; - else - bb_error_msg("unknown shell after -s or --shell argument"); + if (strcmp(new_shell, "bash") == 0 || strcmp(new_shell, "sh") == 0) + return; + if (strcmp(new_shell, "tcsh") == 0 || strcmp(new_shell, "csh") == 0) + option_mask32 |= SHELL_IS_TCSH; + else + bb_error_msg("unknown shell '%s', assuming bash", new_shell); } @@ -285,107 +343,82 @@ void set_shell(const char *new_shell) * 4) Returned for -T */ -static struct option longopts[]= +#if ENABLE_FEATURE_GETOPT_LONG +static const char getopt_longopts[] ALIGN1 = + "options\0" Required_argument "o" + "longoptions\0" Required_argument "l" + "quiet\0" No_argument "q" + "quiet-output\0" No_argument "Q" + "shell\0" Required_argument "s" + "test\0" No_argument "T" + "unquoted\0" No_argument "u" + "alternative\0" No_argument "a" + "name\0" Required_argument "n" + ; +#endif + +int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int getopt_main(int argc, char **argv) { - {"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} -}; - -/* 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[]) -{ - const char *optstr = NULL; - const char *name = NULL; - int opt; - int compatible=0; - - init_longopt(); - - if (getenv("GETOPT_COMPATIBLE")) - compatible=1; - - if (argc == 1) { - if (compatible) { - /* For some reason, the original getopt gave no error - when there were no arguments. */ - printf(" --\n"); - return 0; - } else - bb_error_msg_and_die("missing optstring argument"); - } - - if (argv[1][0] != '-' || compatible) { - quote=0; - optstr=xmalloc(strlen(argv[1])+1); - strcpy(optstr,argv[1]+strspn(argv[1],"-+")); - argv[1]=argv[0]; - return (generate_output(argv+1,argc-1,optstr,long_options)); - } - - while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF) - switch (opt) { - case 'a': - alternative=1; - break; - case 'o': - free(optstr); - optstr = optarg; - break; - case 'l': - add_long_options(optarg); - break; - case 'n': - free(name); - 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 (!optstr) { - if (optind >= argc) - bb_error_msg_and_die("missing optstring argument"); - else { - optstr=bb_xstrdup(argv[optind]); - 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)); + int n; + char *optstr = NULL; + char *name = NULL; + unsigned opt; + const char *compatible; + char *s_arg; +#if ENABLE_FEATURE_GETOPT_LONG + struct option *long_options = NULL; + llist_t *l_arg = NULL; +#endif + + compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */ + + if (!argv[1]) { + if (compatible) { + /* For some reason, the original getopt gave no error + * when there were no arguments. */ + puts(" --"); + return 0; + } + bb_error_msg_and_die("missing optstring argument"); + } + + if (argv[1][0] != '-' || compatible) { + char *s = argv[1]; + + option_mask32 |= OPT_u; /* quoting off */ + s = xstrdup(s + strspn(s, "-+")); + argv[1] = argv[0]; + return generate_output(argv+1, argc-1, s, long_options); + } + +#if !ENABLE_FEATURE_GETOPT_LONG + opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg); +#else + opt = getopt32long(argv, "+o:n:qQs:Tual:*", getopt_longopts, + &optstr, &name, &s_arg, &l_arg); + /* Effectuate the read options for the applet itself */ + while (l_arg) { + long_options = add_long_options(long_options, llist_pop(&l_arg)); + } +#endif + + if (opt & OPT_s) { + set_shell(s_arg); + } + + if (opt & OPT_T) { + return 4; + } + + /* All options controlling the applet have now been parsed */ + n = optind - 1; + if (!optstr) { + optstr = argv[++n]; + if (!optstr) + bb_error_msg_and_die("missing optstring argument"); + } + + argv[n] = name ? name : argv[0]; + return generate_output(argv + n, argc - n, optstr, long_options); } - -/* - Local Variables: - c-file-style: "linux" - c-basic-offset: 4 - tab-width: 4 - End: -*/