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        char *BUFFER;
 160
 161        if (!quote) { /* Just return arg */
 162                return arg;
 163        }
 164
 165        /* Each character in arg may take up to four characters in the result:
 166           For a quote we need a closing quote, a backslash, a quote and an
 167           opening quote! We need also the global opening and closing quote,
 168           and one extra character for '\0'. */
 169        BUFFER = auto_string(xmalloc(strlen(arg)*4 + 3));
 170
 171        bufptr = BUFFER;
 172        *bufptr ++= '\'';
 173
 174        while (*arg) {
 175                if (shell_TCSH && *arg == '\n') {
 176                        /* Newline: replace it with: \n */
 177                        *bufptr++ = '\\';
 178                        *bufptr++ = 'n';
 179                } else
 180                if ((shell_TCSH && (*arg == '!' || isspace(*arg)))
 181                 || *arg == '\''
 182                ) {
 183                        /* Quote exclamation marks, non-NL whitespace and quotes */
 184                        *bufptr++ = '\'';
 185                        *bufptr++ = '\\';
 186                        *bufptr++ = *arg;
 187                        *bufptr++ = '\'';
 188                } else {
 189                        /* Just copy */
 190                        *bufptr ++= *arg;
 191                }
 192                arg++;
 193        }
 194        *bufptr++ = '\'';
 195        *bufptr++ = '\0';
 196        return BUFFER;
 197}
 198
 199/*
 200 * Generate the output. argv[0] is the program name (used for reporting errors).
 201 * argv[1..] contains the options to be parsed. argc must be the number of
 202 * elements in argv (ie. 1 if there are no options, only the program name),
 203 * optstr must contain the short options, and longopts the long options.
 204 * Other settings are found in global variables.
 205 */
 206#if !ENABLE_FEATURE_GETOPT_LONG
 207#define generate_output(argv,argc,optstr,longopts) \
 208        generate_output(argv,argc,optstr)
 209#endif
 210static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts)
 211{
 212        int exit_code = 0; /* We assume everything will be OK */
 213
 214        if (quiet_errors) /* No error reporting from getopt(3) */
 215                opterr = 0;
 216
 217        /* We used it already in main() in getopt32(),
 218         * we *must* reset getopt(3): */
 219        GETOPT_RESET();
 220
 221        while (1) {
 222#if ENABLE_FEATURE_GETOPT_LONG
 223                int longindex;
 224                int opt = alternative
 225                        ? getopt_long_only(argc, argv, optstr, longopts, &longindex)
 226                        : getopt_long(argc, argv, optstr, longopts, &longindex)
 227                ;
 228#else
 229                int opt = getopt(argc, argv, optstr);
 230#endif
 231                if (opt == -1)
 232                        break;
 233                if (opt == '?' || opt == ':' )
 234                        exit_code = 1;
 235                else if (!quiet_output) {
 236#if ENABLE_FEATURE_GETOPT_LONG
 237                        if (opt == LONG_OPT) {
 238                                printf(" --%s", longopts[longindex].name);
 239                                if (longopts[longindex].has_arg)
 240                                        printf(" %s",
 241                                                normalize(optarg ? optarg : ""));
 242                        } else
 243#endif
 244                        if (opt == NON_OPT)
 245                                printf(" %s", normalize(optarg));
 246                        else {
 247                                const char *charptr;
 248                                printf(" -%c", opt);
 249                                charptr = strchr(optstr, opt);
 250                                if (charptr && *++charptr == ':')
 251                                        printf(" %s",
 252                                                normalize(optarg ? optarg : ""));
 253                        }
 254                }
 255        }
 256
 257        if (!quiet_output) {
 258                unsigned idx;
 259                printf(" --");
 260                idx = optind;
 261                while (argv[idx])
 262                        printf(" %s", normalize(argv[idx++]));
 263                bb_putchar('\n');
 264        }
 265        return exit_code;
 266}
 267
 268#if ENABLE_FEATURE_GETOPT_LONG
 269/*
 270 * Register several long options. options is a string of long options,
 271 * separated by commas or whitespace.
 272 * This nukes options!
 273 */
 274static struct option *add_long_options(struct option *long_options, char *options)
 275{
 276        int long_nr = 0;
 277        int arg_opt, tlen;
 278        char *tokptr;
 279
 280        if (long_options)
 281                while (long_options[long_nr].name)
 282                        long_nr++;
 283
 284        tokptr = strtok_r(options, ", \t\n", &options);
 285        while (tokptr) {
 286                arg_opt = no_argument;
 287                tlen = strlen(tokptr);
 288                if (tlen) {
 289                        tlen--;
 290                        if (tokptr[tlen] == ':') {
 291                                arg_opt = required_argument;
 292                                if (tlen && tokptr[tlen-1] == ':') {
 293                                        tlen--;
 294                                        arg_opt = optional_argument;
 295                                }
 296                                tokptr[tlen] = '\0';
 297                                if (tlen == 0)
 298                                        bb_simple_error_msg_and_die("empty long option specified");
 299                        }
 300                        long_options = xrealloc_vector(long_options, 4, long_nr);
 301                        long_options[long_nr].has_arg = arg_opt;
 302                        /*long_options[long_nr].flag = NULL; - xrealloc_vector did it */
 303                        long_options[long_nr].val = LONG_OPT;
 304                        long_options[long_nr].name = xstrdup(tokptr);
 305                        long_nr++;
 306                        /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */
 307                }
 308                tokptr = strtok_r(NULL, ", \t\n", &options);
 309        }
 310        return long_options;
 311}
 312#endif
 313
 314static void set_shell(const char *new_shell)
 315{
 316        switch (index_in_strings("bash\0sh\0tcsh\0csh\0", new_shell)) {
 317        case 0:
 318        case 1:
 319                break;
 320        case 2:
 321        case 3:
 322                option_mask32 |= SHELL_IS_TCSH;
 323                break;
 324        default:
 325                bb_error_msg("unknown shell '%s', assuming bash", new_shell);
 326                break;
 327        }
 328}
 329
 330
 331/* Exit codes:
 332 *   0) No errors, successful operation.
 333 *   1) getopt(3) returned an error.
 334 *   2) A problem with parameter parsing for getopt(1).
 335 *   3) Internal error, out of memory
 336 *   4) Returned for -T
 337 */
 338
 339#if ENABLE_FEATURE_GETOPT_LONG
 340static const char getopt_longopts[] ALIGN1 =
 341        "options\0"      Required_argument "o"
 342        "longoptions\0"  Required_argument "l"
 343        "quiet\0"        No_argument       "q"
 344        "quiet-output\0" No_argument       "Q"
 345        "shell\0"        Required_argument "s"
 346        "test\0"         No_argument       "T"
 347        "unquoted\0"     No_argument       "u"
 348        "alternative\0"  No_argument       "a"
 349        "name\0"         Required_argument "n"
 350        ;
 351#endif
 352
 353int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 354int getopt_main(int argc, char **argv)
 355{
 356        int n;
 357        char *optstr = NULL;
 358        char *name = NULL;
 359        unsigned opt;
 360        const char *compatible;
 361        char *s_arg;
 362#if ENABLE_FEATURE_GETOPT_LONG
 363        struct option *long_options = NULL;
 364        llist_t *l_arg = NULL;
 365#endif
 366
 367        compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
 368
 369        if (!argv[1]) {
 370                if (compatible) {
 371                        /* For some reason, the original getopt gave no error
 372                         * when there were no arguments. */
 373                        puts(" --");
 374                        return 0;
 375                }
 376                bb_simple_error_msg_and_die("missing optstring argument");
 377        }
 378
 379        if (argv[1][0] != '-' || compatible) {
 380                char *s = argv[1];
 381
 382                option_mask32 |= OPT_u; /* quoting off */
 383                s = xstrdup(s + strspn(s, "-+"));
 384                argv[1] = argv[0];
 385                return generate_output(argv+1, argc-1, s, long_options);
 386        }
 387
 388#if !ENABLE_FEATURE_GETOPT_LONG
 389        opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
 390#else
 391        opt = getopt32long(argv, "+o:n:qQs:Tual:*", getopt_longopts,
 392                                        &optstr, &name, &s_arg, &l_arg);
 393        /* Effectuate the read options for the applet itself */
 394        while (l_arg) {
 395                long_options = add_long_options(long_options, llist_pop(&l_arg));
 396        }
 397#endif
 398
 399        if (opt & OPT_s) {
 400                set_shell(s_arg);
 401        }
 402
 403        if (opt & OPT_T) {
 404                return 4;
 405        }
 406
 407        /* All options controlling the applet have now been parsed */
 408        n = optind - 1;
 409        if (!optstr) {
 410                optstr = argv[++n];
 411                if (!optstr)
 412                        bb_simple_error_msg_and_die("missing optstring argument");
 413        }
 414
 415        argv[n] = name ? name : argv[0];
 416        return generate_output(argv + n, argc - n, optstr, long_options);
 417}
 418