busybox/libbb/getopt32.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * universal getopt32 implementation for busybox
   4 *
   5 * Copyright (C) 2003-2005  Vladimir Oleynik  <dzo@simtreas.ru>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9#if ENABLE_LONG_OPTS
  10# include <getopt.h>
  11#endif
  12#include "libbb.h"
  13
  14//kbuild:lib-y += getopt32.o
  15
  16/*      Documentation
  17
  18uint32_t
  19getopt32(char **argv, const char *applet_opts, ...)
  20
  21        The command line options are passed as the applet_opts string.
  22
  23        If one of the given options is found, a flag value is added to
  24        the return value.
  25
  26        The flag value is determined by the position of the char in
  27        applet_opts string.  For example:
  28
  29        flags = getopt32(argv, "rnug");
  30
  31        "r" will set 1    (bit 0)
  32        "n" will set 2    (bit 1)
  33        "u" will set 4    (bit 2)
  34        "g" will set 8    (bit 3)
  35
  36        and so on.  You can also look at the return value as a bit
  37        field and each option sets one bit.
  38
  39        On exit, global variable optind is set so that if you
  40        will do argc -= optind; argv += optind; then
  41        argc will be equal to number of remaining non-option
  42        arguments, first one would be in argv[0], next in argv[1] and so on
  43        (options and their parameters will be moved into argv[]
  44        positions prior to argv[optind]).
  45
  46 "o:"   If one of the options requires an argument, then add a ":"
  47        after the char in applet_opts and provide a pointer to store
  48        the argument.  For example:
  49
  50        char *pointer_to_arg_for_a;
  51        char *pointer_to_arg_for_b;
  52        char *pointer_to_arg_for_c;
  53        char *pointer_to_arg_for_d;
  54
  55        flags = getopt32(argv, "a:b:c:d:",
  56                        &pointer_to_arg_for_a, &pointer_to_arg_for_b,
  57                        &pointer_to_arg_for_c, &pointer_to_arg_for_d);
  58
  59        The type of the pointer may be controlled by "o::" or "o+" in
  60        the external string opt_complementary (see below for more info).
  61
  62 "o::"  If option can have an *optional* argument, then add a "::"
  63        after its char in applet_opts and provide a pointer to store
  64        the argument.  Note that optional arguments _must_
  65        immediately follow the option: -oparam, not -o param.
  66
  67 "o:+"  This means that the parameter for this option is a nonnegative integer.
  68        It will be processed with xatoi_positive() - allowed range
  69        is 0..INT_MAX.
  70
  71        int param;  // "unsigned param;" will also work
  72        getopt32(argv, "p:+", &param);
  73
  74 "o:*"  This means that the option can occur multiple times. Each occurrence
  75        will be saved as a llist_t element instead of char*.
  76
  77        For example:
  78        The grep applet can have one or more "-e pattern" arguments.
  79        In this case you should use getopt32() as follows:
  80
  81        llist_t *patterns = NULL;
  82
  83        (this pointer must be initializated to NULL if the list is empty
  84        as required by llist_add_to_end(llist_t **old_head, char *new_item).)
  85
  86        getopt32(argv, "e:*", &patterns);
  87
  88        $ grep -e user -e root /etc/passwd
  89        root:x:0:0:root:/root:/bin/bash
  90        user:x:500:500::/home/user:/bin/bash
  91
  92 "^"    options string is "^optchars""\0""opt_complementary".
  93
  94 "!"    If the first character in the applet_opts string is a '!',
  95        report bad options, missing required options,
  96        inconsistent options with all-ones return value (instead of abort.
  97
  98 "+"    If the first character in the applet_opts string is a plus,
  99        then option processing will stop as soon as a non-option is
 100        encountered in the argv array.  Useful for applets like env
 101        which should not process arguments to subprograms:
 102        env -i ls -d /
 103        Here we want env to process just the '-i', not the '-d'.
 104
 105        (The order of multiple prefixes must be "^!+...")
 106
 107uint32_t
 108getopt32long(char **argv, const char *applet_opts, const char *logopts...)
 109
 110        This allows you to define long options:
 111
 112        static const char applet_longopts[] ALIGN1 =
 113                //"name\0"  has_arg     val
 114                "verbose\0" No_argument "v"
 115                ;
 116        opt = getopt32long(argv, applet_opts, applet_longopts, ...);
 117
 118        The last element (val) typically is set to
 119        matching short option from applet_opts. If there is no matching
 120        char in applet_opts, then:
 121        - return bit has next position after short options
 122        - if has_arg is not "No_argument", use ptr for arg also
 123        - opt_complementary affects it too
 124
 125        Note: a good applet will make long options configurable via the
 126        config process and not a required feature.  The current standard
 127        is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
 128
 129opt_complementary - option modifiers.
 130
 131 ":"    The colon (":") is used to separate groups of two or more chars
 132        and/or groups of chars and special characters (stating some
 133        conditions to be checked).
 134
 135 "abc"  If groups of two or more chars are specified, the first char
 136        is the main option and the other chars are secondary options.
 137        Their flags will be turned on if the main option is found even
 138        if they are not specified on the command line.  For example:
 139
 140        flags = getopt32(argv, "^abcd""\0""abc")
 141
 142        If getopt() finds "-a" on the command line, then
 143        getopt32's return value will be as if "-a -b -c" were
 144        found.
 145
 146 "ww"   Adjacent double options have a counter associated which indicates
 147        the number of occurrences of the option.
 148        For example the ps applet needs:
 149        if w is given once, GNU ps sets the width to 132,
 150        if w is given more than once, it is "unlimited"
 151
 152        int w_counter = 0; // must be initialized!
 153        getopt32(argv, "^w""\0""ww", &w_counter);
 154        if (w_counter)
 155                width = (w_counter == 1) ? 132 : INT_MAX;
 156        else
 157                get_terminal_width(...&width...);
 158
 159        w_counter is a pointer to an integer. It has to be passed to
 160        getopt32() after all other option argument sinks.
 161
 162        For example: accept multiple -v to indicate the level of verbosity
 163        and for each -b optarg, add optarg to my_b. Finally, if b is given,
 164        turn off c and vice versa:
 165
 166        llist_t *my_b = NULL;
 167        int verbose_level = 0;
 168        f = getopt32(argv, "^vb:*c"
 169                        "\0""vv:b-c:c-b"
 170                        , &my_b, &verbose_level);
 171        if (f & 2)       // -c after -b unsets -b flag
 172                while (my_b) dosomething_with(llist_pop(&my_b));
 173        if (my_b)        // but llist is stored if -b is specified
 174                free_llist(my_b);
 175        if (verbose_level) printf("verbose level is %d\n", verbose_level);
 176
 177Special characters:
 178
 179 "-N"   A dash as the first char in a opt_complementary group followed
 180        by a single digit (0-9) means that at least N non-option
 181        arguments must be present on the command line
 182
 183 "=N"   An equal sign as the first char in a opt_complementary group followed
 184        by a single digit (0-9) means that exactly N non-option
 185        arguments must be present on the command line
 186
 187 "?N"   A "?" as the first char in a opt_complementary group followed
 188        by a single digit (0-9) means that at most N arguments must be present
 189        on the command line.
 190
 191 "V-"   An option with dash before colon or end-of-line results in
 192        bb_show_usage() being called if this option is encountered.
 193        This is typically used to implement "print verbose usage message
 194        and exit" option.
 195
 196 "a-b"  A dash between two options causes the second of the two
 197        to be unset (and ignored) if it is given on the command line.
 198
 199        [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
 200
 201        For example:
 202        The du applet has the options "-s" and "-d depth".  If
 203        getopt32 finds -s, then -d is unset or if it finds -d
 204        then -s is unset.  (Note:  busybox implements the GNU
 205        "--max-depth" option as "-d".)  To obtain this behavior, you
 206        set opt_complementary to "s-d:d-s".  Only one flag value is
 207        added to getopt32's return value depending on the
 208        position of the options on the command line.  If one of the
 209        two options requires an argument pointer (":" in applet_opts
 210        as in "d:") optarg is set accordingly.
 211
 212        char *smax_print_depth;
 213
 214        opt = getopt32(argv, "^sd:x""\0""s-d:d-s:x-x", &smax_print_depth);
 215
 216        if (opt & 2)
 217                max_print_depth = atoi(smax_print_depth);
 218        if (opt & 4)
 219                printf("Detected odd -x usage\n");
 220
 221 "a--b" A double dash between two options, or between an option and a group
 222        of options, means that they are mutually exclusive.  Unlike
 223        the "-" case above, an error will be forced if the options
 224        are used together.
 225
 226        For example:
 227        The cut applet must have only one type of list specified, so
 228        -b, -c and -f are mutually exclusive and should raise an error
 229        if specified together.  In this case you must set
 230        opt_complementary to "b--cf:c--bf:f--bc".  If two of the
 231        mutually exclusive options are found, getopt32 will call
 232        bb_show_usage() and die.
 233
 234 "x--x" Variation of the above, it means that -x option should occur
 235        at most once.
 236
 237 "o+"   A plus after a char in opt_complementary means that the parameter
 238        for this option is a nonnegative integer. It will be processed
 239        with xatoi_positive() - allowed range is 0..INT_MAX.
 240
 241        int param;  // "unsigned param;" will also work
 242        getopt32(argv, "^p:""\0""p+", &param);
 243
 244 "o::"  A double colon after a char in opt_complementary means that the
 245        option can occur multiple times. Each occurrence will be saved as
 246        a llist_t element instead of char*.
 247
 248        For example:
 249        The grep applet can have one or more "-e pattern" arguments.
 250        In this case you should use getopt32() as follows:
 251
 252        llist_t *patterns = NULL;
 253
 254        (this pointer must be initializated to NULL if the list is empty
 255        as required by llist_add_to_end(llist_t **old_head, char *new_item).)
 256
 257        getopt32(argv, "^e:""\0""e::", &patterns);
 258
 259        $ grep -e user -e root /etc/passwd
 260        root:x:0:0:root:/root:/bin/bash
 261        user:x:500:500::/home/user:/bin/bash
 262
 263        "o+" and "o::" can be handled by "o:+" and "o:*" specifiers
 264        in option string (and it is preferred), but this does not work
 265        for "long options only" cases, such as tar --exclude=PATTERN,
 266        wget --header=HDR cases.
 267
 268 "a?b"  A "?" between an option and a group of options means that
 269        at least one of them is required to occur if the first option
 270        occurs in preceding command line arguments.
 271
 272        For example from "id" applet:
 273
 274        // Don't allow -n -r -rn -ug -rug -nug -rnug
 275        flags = getopt32(argv, "^rnug""\0""r?ug:n?ug:u--g:g--u");
 276
 277        This example allowed only:
 278        $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
 279
 280 "X"    A opt_complementary group with just a single letter means
 281        that this option is required. If more than one such group exists,
 282        at least one option is required to occur (not all of them).
 283        For example from "start-stop-daemon" applet:
 284
 285        // Don't allow -KS -SK, but -S or -K is required
 286        flags = getopt32(argv, "^KS...""\0""K:S:K--S:S--K");
 287
 288
 289        Don't forget to use ':'. For example, "?322-22-23X-x-a"
 290        is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" -
 291        max 3 args; count uses of '-2'; min 2 args; if there is
 292        a '-2' option then unset '-3', '-X' and '-a'; if there is
 293        a '-2' and after it a '-x' then error out.
 294        But it's far too obfuscated. Use ':' to separate groups.
 295*/
 296
 297/* Code here assumes that 'unsigned' is at least 32 bits wide */
 298
 299const char *const bb_argv_dash[] = { "-", NULL };
 300
 301enum {
 302        PARAM_STRING,
 303        PARAM_LIST,
 304        PARAM_INT,
 305};
 306
 307typedef struct {
 308        unsigned char opt_char;
 309        smallint param_type;
 310        unsigned switch_on;
 311        unsigned switch_off;
 312        unsigned incongruously;
 313        unsigned requires;
 314        void **optarg;  /* char**, llist_t** or int *. */
 315        int *counter;
 316} t_complementary;
 317
 318uint32_t option_mask32;
 319
 320#if ENABLE_LONG_OPTS
 321static const struct option bb_null_long_options[1] = {
 322        { 0, 0, 0, 0 }
 323};
 324#else
 325#define vgetopt32(argv,applet_opts,applet_long_options,p) \
 326        vgetopt32(argv,applet_opts,p)
 327#endif
 328
 329/* Please keep getopt32 free from xmalloc */
 330
 331static uint32_t
 332vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, va_list p)
 333{
 334        int argc;
 335        unsigned flags = 0;
 336        unsigned requires = 0;
 337        unsigned len;
 338        t_complementary complementary[33]; /* last stays zero-filled */
 339        char dont_die_flag;
 340        int c;
 341        const unsigned char *s;
 342        const char *opt_complementary;
 343        t_complementary *on_off;
 344#if ENABLE_LONG_OPTS
 345        const struct option *l_o;
 346        struct option *long_options = (struct option *) &bb_null_long_options;
 347#endif
 348        unsigned trigger;
 349        int min_arg = 0;
 350        int max_arg = -1;
 351        int spec_flgs = 0;
 352
 353#define SHOW_USAGE_IF_ERROR     1
 354
 355        on_off = complementary;
 356        memset(on_off, 0, sizeof(complementary));
 357
 358        len = strlen(applet_opts);
 359
 360        /* skip bbox extension */
 361        opt_complementary = NULL;
 362        if (applet_opts[0] == '^') {
 363                applet_opts++;
 364                /* point it past terminating NUL */
 365                opt_complementary = applet_opts + len;
 366        }
 367
 368        /* skip another bbox extension */
 369        dont_die_flag = applet_opts[0];
 370        if (dont_die_flag == '!')
 371                applet_opts++;
 372
 373        applet_opts = strcpy(alloca(len + 1), applet_opts);
 374
 375        /* skip GNU extension */
 376        s = (const unsigned char *)applet_opts;
 377        if (*s == '+' || *s == '-')
 378                s++;
 379        c = 0;
 380        while (*s) {
 381                if (c >= 32)
 382                        break;
 383                on_off->opt_char = *s;
 384                on_off->switch_on = (1U << c);
 385                if (*++s == ':') {
 386                        on_off->optarg = va_arg(p, void **);
 387                        if (s[1] == '+' || s[1] == '*') {
 388                                /* 'o:+' or 'o:*' */
 389                                on_off->param_type = (s[1] == '+') ?
 390                                        PARAM_INT : PARAM_LIST;
 391                                overlapping_strcpy((char*)s + 1, (char*)s + 2);
 392                        }
 393                        /* skip possible 'o::' (or 'o:+:' !) */
 394                        while (*++s == ':')
 395                                continue;
 396                }
 397                on_off++;
 398                c++;
 399        }
 400
 401#if ENABLE_LONG_OPTS
 402        if (applet_long_options) {
 403                const char *optstr;
 404                unsigned i, count;
 405
 406                count = 1;
 407                optstr = applet_long_options;
 408                while (optstr[0]) {
 409                        optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */
 410                        count++;
 411                }
 412                /* count == no. of longopts + 1 */
 413                long_options = alloca(count * sizeof(*long_options));
 414                memset(long_options, 0, count * sizeof(*long_options));
 415                i = 0;
 416                optstr = applet_long_options;
 417                while (--count) {
 418                        long_options[i].name = optstr;
 419                        optstr += strlen(optstr) + 1;
 420                        long_options[i].has_arg = (unsigned char)(*optstr++);
 421                        /* long_options[i].flag = NULL; */
 422                        long_options[i].val = (unsigned char)(*optstr++);
 423                        i++;
 424                }
 425                for (l_o = long_options; l_o->name; l_o++) {
 426                        if (l_o->flag)
 427                                continue;
 428                        for (on_off = complementary; on_off->opt_char; on_off++)
 429                                if (on_off->opt_char == l_o->val)
 430                                        goto next_long;
 431                        if (c >= 32)
 432                                break;
 433                        on_off->opt_char = l_o->val;
 434                        on_off->switch_on = (1U << c);
 435                        if (l_o->has_arg != no_argument)
 436                                on_off->optarg = va_arg(p, void **);
 437                        c++;
 438 next_long: ;
 439                }
 440        }
 441#endif /* ENABLE_LONG_OPTS */
 442
 443        s = (const unsigned char *)opt_complementary;
 444        if (s) for (; *s; s++) {
 445                t_complementary *pair;
 446                unsigned *pair_switch;
 447
 448                if (*s == ':')
 449                        continue;
 450                c = s[1];
 451                if (*s == '?') {
 452                        if (c < '0' || c > '9') {
 453                                spec_flgs |= SHOW_USAGE_IF_ERROR;
 454                        } else {
 455                                max_arg = c - '0';
 456                                s++;
 457                        }
 458                        continue;
 459                }
 460                if (*s == '-') {
 461                        if (c >= '0' && c <= '9') {
 462                                min_arg = c - '0';
 463                                s++;
 464                        }
 465                        continue;
 466                }
 467                if (*s == '=') {
 468                        min_arg = max_arg = c - '0';
 469                        s++;
 470                        continue;
 471                }
 472                for (on_off = complementary; on_off->opt_char; on_off++)
 473                        if (on_off->opt_char == *s)
 474                                goto found_opt;
 475                /* Without this, diagnostic of such bugs is not easy */
 476                bb_error_msg_and_die("NO OPT %c!", *s);
 477 found_opt:
 478                if (c == ':' && s[2] == ':') {
 479                        on_off->param_type = PARAM_LIST;
 480                        continue;
 481                }
 482                if (c == '+' && (s[2] == ':' || s[2] == '\0')) {
 483                        on_off->param_type = PARAM_INT;
 484                        s++;
 485                        continue;
 486                }
 487                if (c == ':' || c == '\0') {
 488                        requires |= on_off->switch_on;
 489                        continue;
 490                }
 491                if (c == '-' && (s[2] == ':' || s[2] == '\0')) {
 492                        flags |= on_off->switch_on;
 493                        on_off->incongruously |= on_off->switch_on;
 494                        s++;
 495                        continue;
 496                }
 497                if (c == *s) {
 498                        on_off->counter = va_arg(p, int *);
 499                        s++;
 500                }
 501                pair = on_off;
 502                pair_switch = &pair->switch_on;
 503                for (s++; *s && *s != ':'; s++) {
 504                        if (*s == '?') {
 505                                pair_switch = &pair->requires;
 506                        } else if (*s == '-') {
 507                                if (pair_switch == &pair->switch_off)
 508                                        pair_switch = &pair->incongruously;
 509                                else
 510                                        pair_switch = &pair->switch_off;
 511                        } else {
 512                                for (on_off = complementary; on_off->opt_char; on_off++)
 513                                        if (on_off->opt_char == *s) {
 514                                                *pair_switch |= on_off->switch_on;
 515                                                break;
 516                                        }
 517                        }
 518                }
 519                s--;
 520        }
 521
 522        /* In case getopt32 was already called:
 523         * reset libc getopt() internal state.
 524         * run_nofork_applet() does this, but we might end up here
 525         * also via gunzip_main() -> gzip_main(). Play safe.
 526         */
 527        GETOPT_RESET();
 528
 529        /* skip 0: some applets cheat: they do not actually HAVE argv[0] */
 530        argc = 1 + string_array_len(argv + 1);
 531
 532        /* Note: just "getopt() <= 0" will not work well for
 533         * "fake" short options, like this one:
 534         * wget $'-\203' "Test: test" http://kernel.org/
 535         * (supposed to act as --header, but doesn't) */
 536#if ENABLE_LONG_OPTS
 537        while ((c = getopt_long(argc, argv, applet_opts,
 538                        long_options, NULL)) != -1) {
 539#else
 540        while ((c = getopt(argc, argv, applet_opts)) != -1) {
 541#endif
 542                /* getopt prints "option requires an argument -- X"
 543                 * and returns '?' if an option has no arg, but one is reqd */
 544                c &= 0xff; /* fight libc's sign extension */
 545                for (on_off = complementary; on_off->opt_char != c; on_off++) {
 546                        /* c can be NUL if long opt has non-NULL ->flag,
 547                         * but we construct long opts so that flag
 548                         * is always NULL (see above) */
 549                        if (on_off->opt_char == '\0' /* && c != '\0' */) {
 550                                /* c is probably '?' - "bad option" */
 551                                goto error;
 552                        }
 553                }
 554                if (flags & on_off->incongruously)
 555                        goto error;
 556                trigger = on_off->switch_on & on_off->switch_off;
 557                flags &= ~(on_off->switch_off ^ trigger);
 558                flags |= on_off->switch_on ^ trigger;
 559                flags ^= trigger;
 560                if (on_off->counter)
 561                        (*(on_off->counter))++;
 562                if (optarg) {
 563                        if (on_off->param_type == PARAM_LIST) {
 564                                llist_add_to_end((llist_t **)(on_off->optarg), optarg);
 565                        } else if (on_off->param_type == PARAM_INT) {
 566//TODO: xatoi_positive indirectly pulls in printf machinery
 567                                *(unsigned*)(on_off->optarg) = xatoi_positive(optarg);
 568                        } else if (on_off->optarg) {
 569                                *(char **)(on_off->optarg) = optarg;
 570                        }
 571                }
 572        }
 573
 574        /* check depending requires for given options */
 575        for (on_off = complementary; on_off->opt_char; on_off++) {
 576                if (on_off->requires
 577                 && (flags & on_off->switch_on)
 578                 && (flags & on_off->requires) == 0
 579                ) {
 580                        goto error;
 581                }
 582        }
 583        if (requires && (flags & requires) == 0)
 584                goto error;
 585        argc -= optind;
 586        if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
 587                goto error;
 588
 589        option_mask32 = flags;
 590        return flags;
 591
 592 error:
 593        if (dont_die_flag != '!')
 594                bb_show_usage();
 595        return (int32_t)-1;
 596}
 597
 598uint32_t FAST_FUNC
 599getopt32(char **argv, const char *applet_opts, ...)
 600{
 601        uint32_t opt;
 602        va_list p;
 603
 604        va_start(p, applet_opts);
 605        opt = vgetopt32(argv, applet_opts, NULL, p);
 606        va_end(p);
 607        return opt;
 608}
 609
 610#if ENABLE_LONG_OPTS
 611uint32_t FAST_FUNC
 612getopt32long(char **argv, const char *applet_opts, const char *longopts, ...)
 613{
 614        uint32_t opt;
 615        va_list p;
 616
 617        va_start(p, longopts);
 618        opt = vgetopt32(argv, applet_opts, longopts, p);
 619        va_end(p);
 620        return opt;
 621}
 622#endif
 623