busybox/util-linux/getopt.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * getopt.c - Enhanced implementation of BSD getopt(1)
   4 * Copyright (c) 1997, 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>
   5 *
   6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   7 */
   8/*
   9 * Version 1.0-b4: Tue Sep 23 1997. First public release.
  10 * Version 1.0: Wed Nov 19 1997.
  11 *   Bumped up the version number to 1.0
  12 *   Fixed minor typo (CSH instead of TCSH)
  13 * Version 1.0.1: Tue Jun 3 1998
  14 *   Fixed sizeof instead of strlen bug
  15 *   Bumped up the version number to 1.0.1
  16 * Version 1.0.2: Thu Jun 11 1998 (not present)
  17 *   Fixed gcc-2.8.1 warnings
  18 *   Fixed --version/-V option (not present)
  19 * Version 1.0.5: Tue Jun 22 1999
  20 *   Make -u option work (not present)
  21 * Version 1.0.6: Tue Jun 27 2000
  22 *   No important changes
  23 * Version 1.1.0: Tue Jun 30 2000
  24 *   Added NLS support (partly written by Arkadiusz Mickiewicz
  25 *     <misiek@misiek.eu.org>)
  26 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
  27 *  Removed --version/-V and --help/-h
  28 *  Removed parse_error(), using bb_error_msg() from Busybox instead
  29 *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
  30 */
  31//config:config GETOPT
  32//config:       bool "getopt (5.8 kb)"
  33//config:       default y
  34//config:       help
  35//config:       The getopt utility is used to break up (parse) options in command
  36//config:       lines to make it easy to write complex shell scripts that also check
  37//config:       for legal (and illegal) options. If you want to write horribly
  38//config:       complex shell scripts, or use some horribly complex shell script
  39//config:       written by others, this utility may be for you. Most people will
  40//config:       wisely leave this disabled.
  41//config:
  42//config:config FEATURE_GETOPT_LONG
  43//config:       bool "Support -l LONGOPTs"
  44//config:       default y
  45//config:       depends on GETOPT && LONG_OPTS
  46//config:       help
  47//config:       Enable support for long options (option -l).
  48
  49//applet:IF_GETOPT(APPLET_NOEXEC(getopt, getopt, BB_DIR_BIN, BB_SUID_DROP, getopt))
  50
  51//kbuild:lib-$(CONFIG_GETOPT) += getopt.o
  52
  53//usage:#define getopt_trivial_usage
  54//usage:       "[OPTIONS] [--] OPTSTRING PARAMS"
  55//usage:#define getopt_full_usage "\n\n"
  56//usage:        IF_FEATURE_GETOPT_LONG(
  57//usage:       "        -a              Allow long options starting with single -\n"
  58//usage:       "        -l LOPT[,...]   Long options to recognize\n"
  59//usage:        )
  60//usage:       "        -n PROGNAME     The name under which errors are reported"
  61//usage:     "\n        -o OPTSTRING    Short options to recognize"
  62//usage:     "\n        -q              No error messages on unrecognized options"
  63//usage:     "\n        -Q              No normal output"
  64//usage:     "\n        -s SHELL        Set shell quoting conventions"
  65//usage:     "\n        -T              Version test (exits with 4)"
  66//usage:     "\n        -u              Don't quote output"
  67//usage:        IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */
  68//usage:     "\n"
  69//usage:     "\nExample:"
  70//usage:     "\n"
  71//usage:     "\nO=`getopt -l bb: -- ab:c:: \"$@\"` || exit 1"
  72//usage:     "\neval set -- \"$O\""
  73//usage:     "\nwhile true; do"
  74//usage:     "\n        case \"$1\" in"
  75//usage:     "\n        -a)     echo A; shift;;"
  76//usage:     "\n        -b|--bb) echo \"B:'$2'\"; shift 2;;"
  77//usage:     "\n        -c)     case \"$2\" in"
  78//usage:     "\n                \"\")   echo C; shift 2;;"
  79//usage:     "\n                *)      echo \"C:'$2'\"; shift 2;;"
  80//usage:     "\n                esac;;"
  81//usage:     "\n        --)     shift; break;;"
  82//usage:     "\n        *)      echo Error; exit 1;;"
  83//usage:     "\n        esac"
  84//usage:     "\ndone"
  85//usage:        )
  86//usage:
  87//usage:#define getopt_example_usage
  88//usage:       "$ cat getopt.test\n"
  89//usage:       "#!/bin/sh\n"
  90//usage:       "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n"
  91//usage:       "       -n 'example.busybox' -- \"$@\"`\n"
  92//usage:       "if [ $? != 0 ]; then exit 1; fi\n"
  93//usage:       "eval set -- \"$GETOPT\"\n"
  94//usage:       "while true; do\n"
  95//usage:       " case $1 in\n"
  96//usage:       "   -a|--a-long) echo \"Option a\"; shift;;\n"
  97//usage:       "   -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n"
  98//usage:       "   -c|--c-long)\n"
  99//usage:       "     case \"$2\" in\n"
 100//usage:       "       \"\") echo \"Option c, no argument\"; shift 2;;\n"
 101//usage:       "       *)  echo \"Option c, argument '$2'\"; shift 2;;\n"
 102//usage:       "     esac;;\n"
 103//usage:       "   --) shift; break;;\n"
 104//usage:       "   *) echo \"Internal error!\"; exit 1;;\n"
 105//usage:       " esac\n"
 106//usage:       "done\n"
 107
 108#if ENABLE_FEATURE_GETOPT_LONG
 109# include <getopt.h>
 110#endif
 111#include "libbb.h"
 112
 113/* NON_OPT is the code that is returned when a non-option is found in '+'
 114   mode */
 115enum {
 116        NON_OPT = 1,
 117#if ENABLE_FEATURE_GETOPT_LONG
 118/* LONG_OPT is the code that is returned when a long option is found. */
 119        LONG_OPT = 2
 120#endif
 121};
 122
 123/* For finding activated option flags. Must match getopt32 call! */
 124enum {
 125        OPT_o   = 0x1,  // -o
 126        OPT_n   = 0x2,  // -n
 127        OPT_q   = 0x4,  // -q
 128        OPT_Q   = 0x8,  // -Q
 129        OPT_s   = 0x10, // -s
 130        OPT_T   = 0x20, // -T
 131        OPT_u   = 0x40, // -u
 132#if ENABLE_FEATURE_GETOPT_LONG
 133        OPT_a   = 0x80, // -a
 134        OPT_l   = 0x100, // -l
 135#endif
 136        SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */
 137};
 138
 139/* 0 is getopt_long, 1 is getopt_long_only */
 140#define alternative  (option_mask32 & OPT_a)
 141
 142#define quiet_errors (option_mask32 & OPT_q)
 143#define quiet_output (option_mask32 & OPT_Q)
 144#define quote        (!(option_mask32 & OPT_u))
 145#define shell_TCSH   (option_mask32 & SHELL_IS_TCSH)
 146
 147/*
 148 * This function 'normalizes' a single argument: it puts single quotes around
 149 * it and escapes other special characters. If quote is false, it just
 150 * returns its argument.
 151 * Bash only needs special treatment for single quotes; tcsh also recognizes
 152 * exclamation marks within single quotes, and nukes whitespace.
 153 * This function returns a pointer to a buffer that is overwritten by
 154 * each call.
 155 */
 156static const char *normalize(const char *arg)
 157{
 158        char *bufptr;
 159#if ENABLE_FEATURE_CLEAN_UP
 160        static char *BUFFER = NULL;
 161        free(BUFFER);
 162#else
 163        char *BUFFER;
 164#endif
 165
 166        if (!quote) { /* Just copy arg */
 167                BUFFER = xstrdup(arg);
 168                return BUFFER;
 169        }
 170
 171        /* Each character in arg may take up to four characters in the result:
 172           For a quote we need a closing quote, a backslash, a quote and an
 173           opening quote! We need also the global opening and closing quote,
 174           and one extra character for '\0'. */
 175        BUFFER = xmalloc(strlen(arg)*4 + 3);
 176
 177        bufptr = BUFFER;
 178        *bufptr ++= '\'';
 179
 180        while (*arg) {
 181                if (*arg == '\'') {
 182                        /* Quote: replace it with: '\'' */
 183                        *bufptr ++= '\'';
 184                        *bufptr ++= '\\';
 185                        *bufptr ++= '\'';
 186                        *bufptr ++= '\'';
 187                } else if (shell_TCSH && *arg == '!') {
 188                        /* Exclamation mark: replace it with: \! */
 189                        *bufptr ++= '\'';
 190                        *bufptr ++= '\\';
 191                        *bufptr ++= '!';
 192                        *bufptr ++= '\'';
 193                } else if (shell_TCSH && *arg == '\n') {
 194                        /* Newline: replace it with: \n */
 195                        *bufptr ++= '\\';
 196                        *bufptr ++= 'n';
 197                } else if (shell_TCSH && isspace(*arg)) {
 198                        /* Non-newline whitespace: replace it with \<ws> */
 199                        *bufptr ++= '\'';
 200                        *bufptr ++= '\\';
 201                        *bufptr ++= *arg;
 202                        *bufptr ++= '\'';
 203                } else
 204                        /* Just copy */
 205                        *bufptr ++= *arg;
 206                arg++;
 207        }
 208        *bufptr ++= '\'';
 209        *bufptr ++= '\0';
 210        return BUFFER;
 211}
 212
 213/*
 214 * Generate the output. argv[0] is the program name (used for reporting errors).
 215 * argv[1..] contains the options to be parsed. argc must be the number of
 216 * elements in argv (ie. 1 if there are no options, only the program name),
 217 * optstr must contain the short options, and longopts the long options.
 218 * Other settings are found in global variables.
 219 */
 220#if !ENABLE_FEATURE_GETOPT_LONG
 221#define generate_output(argv,argc,optstr,longopts) \
 222        generate_output(argv,argc,optstr)
 223#endif
 224static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts)
 225{
 226        int exit_code = 0; /* We assume everything will be OK */
 227
 228        if (quiet_errors) /* No error reporting from getopt(3) */
 229                opterr = 0;
 230
 231        /* We used it already in main() in getopt32(),
 232         * we *must* reset getopt(3): */
 233        GETOPT_RESET();
 234
 235        while (1) {
 236#if ENABLE_FEATURE_GETOPT_LONG
 237                int longindex;
 238                int opt = alternative
 239                        ? getopt_long_only(argc, argv, optstr, longopts, &longindex)
 240                        : getopt_long(argc, argv, optstr, longopts, &longindex)
 241                ;
 242#else
 243                int opt = getopt(argc, argv, optstr);
 244#endif
 245                if (opt == -1)
 246                        break;
 247                if (opt == '?' || opt == ':' )
 248                        exit_code = 1;
 249                else if (!quiet_output) {
 250#if ENABLE_FEATURE_GETOPT_LONG
 251                        if (opt == LONG_OPT) {
 252                                printf(" --%s", longopts[longindex].name);
 253                                if (longopts[longindex].has_arg)
 254                                        printf(" %s",
 255                                                normalize(optarg ? optarg : ""));
 256                        } else
 257#endif
 258                        if (opt == NON_OPT)
 259                                printf(" %s", normalize(optarg));
 260                        else {
 261                                const char *charptr;
 262                                printf(" -%c", opt);
 263                                charptr = strchr(optstr, opt);
 264                                if (charptr && *++charptr == ':')
 265                                        printf(" %s",
 266                                                normalize(optarg ? optarg : ""));
 267                        }
 268                }
 269        }
 270
 271        if (!quiet_output) {
 272                unsigned idx;
 273                printf(" --");
 274                idx = optind;
 275                while (argv[idx])
 276                        printf(" %s", normalize(argv[idx++]));
 277                bb_putchar('\n');
 278        }
 279        return exit_code;
 280}
 281
 282#if ENABLE_FEATURE_GETOPT_LONG
 283/*
 284 * Register several long options. options is a string of long options,
 285 * separated by commas or whitespace.
 286 * This nukes options!
 287 */
 288static struct option *add_long_options(struct option *long_options, char *options)
 289{
 290        int long_nr = 0;
 291        int arg_opt, tlen;
 292        char *tokptr = strtok(options, ", \t\n");
 293
 294        if (long_options)
 295                while (long_options[long_nr].name)
 296                        long_nr++;
 297
 298        while (tokptr) {
 299                arg_opt = no_argument;
 300                tlen = strlen(tokptr);
 301                if (tlen) {
 302                        tlen--;
 303                        if (tokptr[tlen] == ':') {
 304                                arg_opt = required_argument;
 305                                if (tlen && tokptr[tlen-1] == ':') {
 306                                        tlen--;
 307                                        arg_opt = optional_argument;
 308                                }
 309                                tokptr[tlen] = '\0';
 310                                if (tlen == 0)
 311                                        bb_error_msg_and_die("empty long option specified");
 312                        }
 313                        long_options = xrealloc_vector(long_options, 4, long_nr);
 314                        long_options[long_nr].has_arg = arg_opt;
 315                        /*long_options[long_nr].flag = NULL; - xrealloc_vector did it */
 316                        long_options[long_nr].val = LONG_OPT;
 317                        long_options[long_nr].name = xstrdup(tokptr);
 318                        long_nr++;
 319                        /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */
 320                }
 321                tokptr = strtok(NULL, ", \t\n");
 322        }
 323        return long_options;
 324}
 325#endif
 326
 327static void set_shell(const char *new_shell)
 328{
 329        if (strcmp(new_shell, "bash") == 0 || strcmp(new_shell, "sh") == 0)
 330                return;
 331        if (strcmp(new_shell, "tcsh") == 0 || strcmp(new_shell, "csh") == 0)
 332                option_mask32 |= SHELL_IS_TCSH;
 333        else
 334                bb_error_msg("unknown shell '%s', assuming bash", new_shell);
 335}
 336
 337
 338/* Exit codes:
 339 *   0) No errors, successful operation.
 340 *   1) getopt(3) returned an error.
 341 *   2) A problem with parameter parsing for getopt(1).
 342 *   3) Internal error, out of memory
 343 *   4) Returned for -T
 344 */
 345
 346#if ENABLE_FEATURE_GETOPT_LONG
 347static const char getopt_longopts[] ALIGN1 =
 348        "options\0"      Required_argument "o"
 349        "longoptions\0"  Required_argument "l"
 350        "quiet\0"        No_argument       "q"
 351        "quiet-output\0" No_argument       "Q"
 352        "shell\0"        Required_argument "s"
 353        "test\0"         No_argument       "T"
 354        "unquoted\0"     No_argument       "u"
 355        "alternative\0"  No_argument       "a"
 356        "name\0"         Required_argument "n"
 357        ;
 358#endif
 359
 360int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 361int getopt_main(int argc, char **argv)
 362{
 363        int n;
 364        char *optstr = NULL;
 365        char *name = NULL;
 366        unsigned opt;
 367        const char *compatible;
 368        char *s_arg;
 369#if ENABLE_FEATURE_GETOPT_LONG
 370        struct option *long_options = NULL;
 371        llist_t *l_arg = NULL;
 372#endif
 373
 374        compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
 375
 376        if (!argv[1]) {
 377                if (compatible) {
 378                        /* For some reason, the original getopt gave no error
 379                         * when there were no arguments. */
 380                        puts(" --");
 381                        return 0;
 382                }
 383                bb_error_msg_and_die("missing optstring argument");
 384        }
 385
 386        if (argv[1][0] != '-' || compatible) {
 387                char *s = argv[1];
 388
 389                option_mask32 |= OPT_u; /* quoting off */
 390                s = xstrdup(s + strspn(s, "-+"));
 391                argv[1] = argv[0];
 392                return generate_output(argv+1, argc-1, s, long_options);
 393        }
 394
 395#if !ENABLE_FEATURE_GETOPT_LONG
 396        opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
 397#else
 398        opt = getopt32long(argv, "+o:n:qQs:Tual:*", getopt_longopts,
 399                                        &optstr, &name, &s_arg, &l_arg);
 400        /* Effectuate the read options for the applet itself */
 401        while (l_arg) {
 402                long_options = add_long_options(long_options, llist_pop(&l_arg));
 403        }
 404#endif
 405
 406        if (opt & OPT_s) {
 407                set_shell(s_arg);
 408        }
 409
 410        if (opt & OPT_T) {
 411                return 4;
 412        }
 413
 414        /* All options controlling the applet have now been parsed */
 415        n = optind - 1;
 416        if (!optstr) {
 417                optstr = argv[++n];
 418                if (!optstr)
 419                        bb_error_msg_and_die("missing optstring argument");
 420        }
 421
 422        argv[n] = name ? name : argv[0];
 423        return generate_output(argv + n, argc - n, optstr, long_options);
 424}
 425