diff options
Diffstat (limited to 'lib/args.c')
-rw-r--r-- | lib/args.c | 98 |
1 files changed, 58 insertions, 40 deletions
@@ -6,10 +6,50 @@ #include "toys.h" // Design goals: -// Don't use getopt() -// Don't permute original arguments. -// handle --long gracefully "(noshort)a(along)b(blong1)(blong2)" -// After each argument: +// Don't use getopt() out of libc. +// Don't permute original arguments (screwing up ps/top output). +// Integrated --long options "(noshort)a(along)b(blong1)(blong2)" + +/* This uses a getopt-like option string, but not getopt() itself. We call + * it the get_opt string. + * + * Each option in the get_opt string corresponds to a bit position in the + * return value. The rightmost argument is (1<<0), the next to last is (1<<1) + * and so on. If the option isn't seen in argv[], its bit remains 0. + * + * Options which have an argument fill in the corresponding slot in the global + * union "this" (see generated/globals.h), which it treats as an array of longs + * (note that sizeof(long)==sizeof(pointer) is guaranteed by LP64). + * + * You don't have to free the option strings, which point into the environment + * space. List objects should be freed by main() when command_main() returns. + * + * Example: + * Calling get_optflags() when toys.which->options="ab:c:d" and + * argv = ["command", "-b", "fruit", "-d", "walrus"] results in: + * + * Changes to struct toys: + * toys.optflags = 5 (I.E. 0101 so -b = 4 | -d = 1) + * toys.optargs[0] = "walrus" (leftover argument) + * toys.optargs[1] = NULL (end of list) + * toys.optc = 1 (there was 1 leftover argument) + * + * Changes to union this: + * this[0]=NULL (because -c didn't get an argument this time) + * this[1]="fruit" (argument to -b) + */ + +// Enabling TOYBOX_DEBUG in .config adds syntax checks to option string parsing +// which aren't needed in the final code (your option string is hardwired and +// should be correct when you ship), but are useful for development. + +// What you can put in a get_opt string: +// Any otherwise unused character (all letters, unprefixed numbers) specify +// an option that sets a flag. The bit value is the same as the binary digit +// if you string the option characters together in order. +// So in "abcdefgh" a = 128, h = 1 +// +// Suffixes specify that this option takes an argument (stored in GLOBALS): // Note that pointer and long are always the same size, even on 64 bit. // : plus a string argument, keep most recent if more than one // * plus a string argument, appended to a list @@ -17,19 +57,19 @@ // <LOW - die if less than LOW // >HIGH - die if greater than HIGH // =DEFAULT - value if not specified -// - plus a signed long argument defaulting to negative +// - plus a signed long argument defaulting to negative (say + for positive) // . plus a double precision floating point argument (with CFG_TOYBOX_FLOAT) -// Chop this out with USE_TOYBOX_FLOAT() around option string +// Chop this option out with USE_TOYBOX_FLOAT() in option string // Same <LOW>HIGH=DEFAULT as # // @ plus an occurrence counter (which is a long) // (longopt) -// | this is required. If more than one marked, only one required. -// ; long option's argument is optional, can only be supplied with --opt= +// | this is required. If more than one marked, only one required. +// ; long option's argument is optional (can only be supplied with --opt=) // ^ Stop parsing after encountering this argument -// " " (space char) the "plus an argument" must be separate +// " " (space char) the "plus an argument" must be separate // I.E. "-j 3" not "-j3". So "kill -stop" != "kill -s top" // -// at the beginning: +// At the beginning of the get_opt string (before any options): // ^ stop at first nonoption argument // <0 die if less than # leftover arguments (default 0) // >9 die if > # leftover arguments (default MAX_INT) @@ -49,38 +89,10 @@ // - and -- cannot be arguments. // -- force end of arguments // - is a synonym for stdin in file arguments -// -abc means -a -b -c - -/* This uses a getopt-like option string, but not getopt() itself. We call - * it the get_opt string. - * - * Each option in the get_opt string corresponds to a bit position in the - * return value. The rightmost argument is (1<<0), the next to last is (1<<1) - * and so on. If the option isn't seen in argv[], its bit remains 0. - * - * Options which have an argument fill in the corresponding slot in the global - * union "this" (see generated/globals.h), which it treats as an array of longs - * (note that sizeof(long)==sizeof(pointer) is guaranteed by LP64). - * - * You don't have to free the option strings, which point into the environment - * space. List objects should be freed by main() when command_main() returns. - * - * Example: - * Calling get_optflags() when toys.which->options="ab:c:d" and - * argv = ["command", "-b", "fruit", "-d", "walrus"] results in: - * - * Changes to struct toys: - * toys.optflags = 5 (-b=4 | -d=1) - * toys.optargs[0]="walrus" (leftover argument) - * toys.optargs[1]=NULL (end of list) - * toys.optc=1 (there was 1 leftover argument) - * - * Changes to union this: - * this[0]=NULL (because -c didn't get an argument this time) - * this[1]="fruit" (argument to -b) - */ +// -abcd means -a -b -c -d (but if -b takes an argument, then it's -a -b cd) // Linked list of all known options (option string parsed into this). +// Hangs off getoptflagstate, freed at end of option parsing. struct opts { struct opts *next; long *arg; // Pointer into union "this" to store arguments at. @@ -94,6 +106,10 @@ struct opts { } val[3]; // low, high, default - range of allowed values }; +// linked list of long options. (Hangs off getoptflagstate, free at end of +// option parsing, details about flag to set and global slot to fill out +// stored in related short option struct, but if opt->c = -1 the long option +// is "bare" (has no corresponding short option). struct longopts { struct longopts *next; struct opts *opt; @@ -325,6 +341,8 @@ void parse_optflaglist(struct getoptflagstate *gof) idx = stridx("-|!+", *++options); if (CFG_TOYBOX_DEBUG && idx == -1) error_exit("[ needs +-!"); + if (CFG_TOYBOX_DEBUG && (*options == ']' || !options)) + error_exit("empty []"); // Don't advance past ] but do process it once in loop. while (*(options++) != ']') { |