10f66f451Sopenharmony_ci/* args.c - Command line argument parsing. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2006 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci */ 50f66f451Sopenharmony_ci 60f66f451Sopenharmony_ci// NOTE: If option parsing segfaults, switch on TOYBOX_DEBUG in menuconfig to 70f66f451Sopenharmony_ci// add syntax checks to option string parsing which aren't needed in the final 80f66f451Sopenharmony_ci// code (since get_opt string is hardwired and should be correct when you ship) 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ci#include "toys.h" 110f66f451Sopenharmony_ci 120f66f451Sopenharmony_ci// Design goals: 130f66f451Sopenharmony_ci// Don't use getopt() out of libc. 140f66f451Sopenharmony_ci// Don't permute original arguments (screwing up ps/top output). 150f66f451Sopenharmony_ci// Integrated --long options "(noshort)a(along)b(blong1)(blong2)" 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ci/* This uses a getopt-like option string, but not getopt() itself. We call 180f66f451Sopenharmony_ci * it the get_opt string. 190f66f451Sopenharmony_ci * 200f66f451Sopenharmony_ci * Each option in the get_opt string corresponds to a bit position in the 210f66f451Sopenharmony_ci * return value. The rightmost argument is (1<<0), the next to last is (1<<1) 220f66f451Sopenharmony_ci * and so on. If the option isn't seen in argv[], its bit remains 0. 230f66f451Sopenharmony_ci * 240f66f451Sopenharmony_ci * Options which have an argument fill in the corresponding slot in the global 250f66f451Sopenharmony_ci * union "this" (see generated/globals.h), which it treats as an array of longs 260f66f451Sopenharmony_ci * (note that sizeof(long)==sizeof(pointer) is guaranteed by LP64). 270f66f451Sopenharmony_ci * 280f66f451Sopenharmony_ci * You don't have to free the option strings, which point into the environment 290f66f451Sopenharmony_ci * space. List objects should be freed by main() when command_main() returns. 300f66f451Sopenharmony_ci * 310f66f451Sopenharmony_ci * Example: 320f66f451Sopenharmony_ci * Calling get_optflags() when toys.which->options="ab:c:d" and 330f66f451Sopenharmony_ci * argv = ["command", "-b", "fruit", "-d", "walrus"] results in: 340f66f451Sopenharmony_ci * 350f66f451Sopenharmony_ci * Changes to struct toys: 360f66f451Sopenharmony_ci * toys.optflags = 5 (I.E. 0101 so -b = 4 | -d = 1) 370f66f451Sopenharmony_ci * toys.optargs[0] = "walrus" (leftover argument) 380f66f451Sopenharmony_ci * toys.optargs[1] = NULL (end of list) 390f66f451Sopenharmony_ci * toys.optc = 1 (there was 1 leftover argument) 400f66f451Sopenharmony_ci * 410f66f451Sopenharmony_ci * Changes to union this: 420f66f451Sopenharmony_ci * this[0]=NULL (because -c didn't get an argument this time) 430f66f451Sopenharmony_ci * this[1]="fruit" (argument to -b) 440f66f451Sopenharmony_ci */ 450f66f451Sopenharmony_ci 460f66f451Sopenharmony_ci// What you can put in a get_opt string: 470f66f451Sopenharmony_ci// Any otherwise unused character (all letters, unprefixed numbers) specify 480f66f451Sopenharmony_ci// an option that sets a flag. The bit value is the same as the binary digit 490f66f451Sopenharmony_ci// if you string the option characters together in order. 500f66f451Sopenharmony_ci// So in "abcdefgh" a = 128, h = 1 510f66f451Sopenharmony_ci// 520f66f451Sopenharmony_ci// Suffixes specify that this option takes an argument (stored in GLOBALS): 530f66f451Sopenharmony_ci// Note that pointer and long are always the same size, even on 64 bit. 540f66f451Sopenharmony_ci// : string argument, keep most recent if more than one 550f66f451Sopenharmony_ci// * string argument, appended to a struct arg_list linked list. 560f66f451Sopenharmony_ci// # signed long argument 570f66f451Sopenharmony_ci// <LOW - die if less than LOW 580f66f451Sopenharmony_ci// >HIGH - die if greater than HIGH 590f66f451Sopenharmony_ci// =DEFAULT - value if not specified 600f66f451Sopenharmony_ci// - signed long argument defaulting to negative (say + for positive) 610f66f451Sopenharmony_ci// . double precision floating point argument (with CFG_TOYBOX_FLOAT) 620f66f451Sopenharmony_ci// Chop this option out with USE_TOYBOX_FLOAT() in option string 630f66f451Sopenharmony_ci// Same <LOW>HIGH=DEFAULT as # 640f66f451Sopenharmony_ci// @ occurrence counter (which is a long) 650f66f451Sopenharmony_ci// % time offset in milliseconds with optional s/m/h/d suffix 660f66f451Sopenharmony_ci// (longopt) 670f66f451Sopenharmony_ci// | this is required. If more than one marked, only one required. 680f66f451Sopenharmony_ci// ; long option's argument is optional (can only be supplied with --opt=) 690f66f451Sopenharmony_ci// ^ Stop parsing after encountering this argument 700f66f451Sopenharmony_ci// " " (space char) the "plus an argument" must be separate 710f66f451Sopenharmony_ci// I.E. "-j 3" not "-j3". So "kill -stop" != "kill -s top" 720f66f451Sopenharmony_ci// 730f66f451Sopenharmony_ci// At the beginning of the get_opt string (before any options): 740f66f451Sopenharmony_ci// ^ stop at first nonoption argument 750f66f451Sopenharmony_ci// <0 die if less than # leftover arguments (default 0) 760f66f451Sopenharmony_ci// >9 die if > # leftover arguments (default MAX_INT) 770f66f451Sopenharmony_ci// ? Allow unknown arguments (pass them through to command). 780f66f451Sopenharmony_ci// & first arg has imaginary dash (ala tar/ps/ar) which sets FLAGS_NODASH 790f66f451Sopenharmony_ci// 800f66f451Sopenharmony_ci// At the end: [groups] of previously seen options 810f66f451Sopenharmony_ci// - Only one in group (switch off) [-abc] means -ab=-b, -ba=-a, -abc=-c 820f66f451Sopenharmony_ci// + Synonyms (switch on all) [+abc] means -ab=-abc, -c=-abc 830f66f451Sopenharmony_ci// ! More than one in group is error [!abc] means -ab calls error_exit() 840f66f451Sopenharmony_ci// primarily useful if you can switch things back off again. 850f66f451Sopenharmony_ci// 860f66f451Sopenharmony_ci// You may use octal escapes with the high bit (127) set to use a control 870f66f451Sopenharmony_ci// character as an option flag. For example, \300 would be the option -@ 880f66f451Sopenharmony_ci 890f66f451Sopenharmony_ci// Notes from getopt man page 900f66f451Sopenharmony_ci// - and -- cannot be arguments. 910f66f451Sopenharmony_ci// -- force end of arguments 920f66f451Sopenharmony_ci// - is a synonym for stdin in file arguments 930f66f451Sopenharmony_ci// -abcd means -a -b -c -d (but if -b takes an argument, then it's -a -b cd) 940f66f451Sopenharmony_ci 950f66f451Sopenharmony_ci// Linked list of all known options (option string parsed into this). 960f66f451Sopenharmony_ci// Hangs off getoptflagstate, freed at end of option parsing. 970f66f451Sopenharmony_cistruct opts { 980f66f451Sopenharmony_ci struct opts *next; 990f66f451Sopenharmony_ci long *arg; // Pointer into union "this" to store arguments at. 1000f66f451Sopenharmony_ci int c; // Argument character to match 1010f66f451Sopenharmony_ci int flags; // |=1, ^=2, " "=4, ;=8 1020f66f451Sopenharmony_ci unsigned long long dex[3]; // bits to disable/enable/exclude in toys.optflags 1030f66f451Sopenharmony_ci char type; // Type of arguments to store union "this" 1040f66f451Sopenharmony_ci union { 1050f66f451Sopenharmony_ci long l; 1060f66f451Sopenharmony_ci FLOAT f; 1070f66f451Sopenharmony_ci } val[3]; // low, high, default - range of allowed values 1080f66f451Sopenharmony_ci}; 1090f66f451Sopenharmony_ci 1100f66f451Sopenharmony_ci// linked list of long options. (Hangs off getoptflagstate, free at end of 1110f66f451Sopenharmony_ci// option parsing, details about flag to set and global slot to fill out 1120f66f451Sopenharmony_ci// stored in related short option struct, but if opt->c = -1 the long option 1130f66f451Sopenharmony_ci// is "bare" (has no corresponding short option). 1140f66f451Sopenharmony_cistruct longopts { 1150f66f451Sopenharmony_ci struct longopts *next; 1160f66f451Sopenharmony_ci struct opts *opt; 1170f66f451Sopenharmony_ci char *str; 1180f66f451Sopenharmony_ci int len; 1190f66f451Sopenharmony_ci}; 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_ci// State during argument parsing. 1220f66f451Sopenharmony_cistruct getoptflagstate 1230f66f451Sopenharmony_ci{ 1240f66f451Sopenharmony_ci int argc, minargs, maxargs; 1250f66f451Sopenharmony_ci char *arg; 1260f66f451Sopenharmony_ci struct opts *opts; 1270f66f451Sopenharmony_ci struct longopts *longopts; 1280f66f451Sopenharmony_ci int noerror, nodash_now, stopearly; 1290f66f451Sopenharmony_ci unsigned excludes, requires; 1300f66f451Sopenharmony_ci}; 1310f66f451Sopenharmony_ci 1320f66f451Sopenharmony_ci// Use getoptflagstate to parse one command line option from argv 1330f66f451Sopenharmony_cistatic int gotflag(struct getoptflagstate *gof, struct opts *opt) 1340f66f451Sopenharmony_ci{ 1350f66f451Sopenharmony_ci int type; 1360f66f451Sopenharmony_ci 1370f66f451Sopenharmony_ci // Did we recognize this option? 1380f66f451Sopenharmony_ci if (!opt) { 1390f66f451Sopenharmony_ci if (gof->noerror) return 1; 1400f66f451Sopenharmony_ci help_exit("Unknown option '%s'", gof->arg); 1410f66f451Sopenharmony_ci } 1420f66f451Sopenharmony_ci 1430f66f451Sopenharmony_ci // Might enabling this switch off something else? 1440f66f451Sopenharmony_ci if (toys.optflags & opt->dex[0]) { 1450f66f451Sopenharmony_ci struct opts *clr; 1460f66f451Sopenharmony_ci unsigned long long i = 1; 1470f66f451Sopenharmony_ci 1480f66f451Sopenharmony_ci // Forget saved argument for flag we switch back off 1490f66f451Sopenharmony_ci for (clr=gof->opts, i=1; clr; clr = clr->next, i<<=1) 1500f66f451Sopenharmony_ci if (clr->arg && (i & toys.optflags & opt->dex[0])) *clr->arg = 0; 1510f66f451Sopenharmony_ci toys.optflags &= ~opt->dex[0]; 1520f66f451Sopenharmony_ci } 1530f66f451Sopenharmony_ci 1540f66f451Sopenharmony_ci // Set flags 1550f66f451Sopenharmony_ci toys.optflags |= opt->dex[1]; 1560f66f451Sopenharmony_ci gof->excludes |= opt->dex[2]; 1570f66f451Sopenharmony_ci if (opt->flags&2) gof->stopearly=2; 1580f66f451Sopenharmony_ci 1590f66f451Sopenharmony_ci if (toys.optflags & gof->excludes) { 1600f66f451Sopenharmony_ci struct opts *bad; 1610f66f451Sopenharmony_ci unsigned i = 1; 1620f66f451Sopenharmony_ci 1630f66f451Sopenharmony_ci for (bad=gof->opts, i=1; bad ;bad = bad->next, i<<=1) { 1640f66f451Sopenharmony_ci if (opt == bad || !(i & toys.optflags)) continue; 1650f66f451Sopenharmony_ci if (toys.optflags & bad->dex[2]) break; 1660f66f451Sopenharmony_ci } 1670f66f451Sopenharmony_ci if (bad) help_exit("No '%c' with '%c'", opt->c, bad->c); 1680f66f451Sopenharmony_ci } 1690f66f451Sopenharmony_ci 1700f66f451Sopenharmony_ci // Does this option take an argument? 1710f66f451Sopenharmony_ci if (!gof->arg) { 1720f66f451Sopenharmony_ci if (opt->flags & 8) return 0; 1730f66f451Sopenharmony_ci gof->arg = ""; 1740f66f451Sopenharmony_ci } else gof->arg++; 1750f66f451Sopenharmony_ci type = opt->type; 1760f66f451Sopenharmony_ci 1770f66f451Sopenharmony_ci if (type == '@') ++*(opt->arg); 1780f66f451Sopenharmony_ci else if (type) { 1790f66f451Sopenharmony_ci char *arg = gof->arg; 1800f66f451Sopenharmony_ci 1810f66f451Sopenharmony_ci // Handle "-xblah" and "-x blah", but also a third case: "abxc blah" 1820f66f451Sopenharmony_ci // to make "tar xCjfv blah1 blah2 thingy" work like 1830f66f451Sopenharmony_ci // "tar -x -C blah1 -j -f blah2 -v thingy" 1840f66f451Sopenharmony_ci 1850f66f451Sopenharmony_ci if (gof->nodash_now || (!arg[0] && !(opt->flags & 8))) 1860f66f451Sopenharmony_ci arg = toys.argv[++gof->argc]; 1870f66f451Sopenharmony_ci if (!arg) { 1880f66f451Sopenharmony_ci char *s = "Missing argument to "; 1890f66f451Sopenharmony_ci struct longopts *lo; 1900f66f451Sopenharmony_ci 1910f66f451Sopenharmony_ci if (opt->c != -1) help_exit("%s-%c", s, opt->c); 1920f66f451Sopenharmony_ci 1930f66f451Sopenharmony_ci for (lo = gof->longopts; lo->opt != opt; lo = lo->next); 1940f66f451Sopenharmony_ci help_exit("%s--%.*s", s, lo->len, lo->str); 1950f66f451Sopenharmony_ci } 1960f66f451Sopenharmony_ci 1970f66f451Sopenharmony_ci if (type == ':') *(opt->arg) = (long)arg; 1980f66f451Sopenharmony_ci else if (type == '*') { 1990f66f451Sopenharmony_ci struct arg_list **list; 2000f66f451Sopenharmony_ci 2010f66f451Sopenharmony_ci list = (struct arg_list **)opt->arg; 2020f66f451Sopenharmony_ci while (*list) list=&((*list)->next); 2030f66f451Sopenharmony_ci *list = xzalloc(sizeof(struct arg_list)); 2040f66f451Sopenharmony_ci (*list)->arg = arg; 2050f66f451Sopenharmony_ci } else if (type == '#' || type == '-') { 2060f66f451Sopenharmony_ci long l = atolx(arg); 2070f66f451Sopenharmony_ci if (type == '-' && !ispunct(*arg)) l*=-1; 2080f66f451Sopenharmony_ci if (l < opt->val[0].l) help_exit("-%c < %ld", opt->c, opt->val[0].l); 2090f66f451Sopenharmony_ci if (l > opt->val[1].l) help_exit("-%c > %ld", opt->c, opt->val[1].l); 2100f66f451Sopenharmony_ci 2110f66f451Sopenharmony_ci *(opt->arg) = l; 2120f66f451Sopenharmony_ci } else if (CFG_TOYBOX_FLOAT && type == '.') { 2130f66f451Sopenharmony_ci FLOAT *f = (FLOAT *)(opt->arg); 2140f66f451Sopenharmony_ci 2150f66f451Sopenharmony_ci *f = strtod(arg, &arg); 2160f66f451Sopenharmony_ci if (opt->val[0].l != LONG_MIN && *f < opt->val[0].f) 2170f66f451Sopenharmony_ci help_exit("-%c < %lf", opt->c, (double)opt->val[0].f); 2180f66f451Sopenharmony_ci if (opt->val[1].l != LONG_MAX && *f > opt->val[1].f) 2190f66f451Sopenharmony_ci help_exit("-%c > %lf", opt->c, (double)opt->val[1].f); 2200f66f451Sopenharmony_ci } else if (type=='%') *(opt->arg) = xparsemillitime(arg); 2210f66f451Sopenharmony_ci 2220f66f451Sopenharmony_ci if (!gof->nodash_now) gof->arg = ""; 2230f66f451Sopenharmony_ci } 2240f66f451Sopenharmony_ci 2250f66f451Sopenharmony_ci return 0; 2260f66f451Sopenharmony_ci} 2270f66f451Sopenharmony_ci 2280f66f451Sopenharmony_ci// Parse this command's options string into struct getoptflagstate, which 2290f66f451Sopenharmony_ci// includes a struct opts linked list in reverse order (I.E. right-to-left) 2300f66f451Sopenharmony_civoid parse_optflaglist(struct getoptflagstate *gof) 2310f66f451Sopenharmony_ci{ 2320f66f451Sopenharmony_ci char *options = toys.which->options; 2330f66f451Sopenharmony_ci long *nextarg = (long *)&this; 2340f66f451Sopenharmony_ci struct opts *new = 0; 2350f66f451Sopenharmony_ci int idx; 2360f66f451Sopenharmony_ci 2370f66f451Sopenharmony_ci // Parse option format string 2380f66f451Sopenharmony_ci memset(gof, 0, sizeof(struct getoptflagstate)); 2390f66f451Sopenharmony_ci gof->maxargs = INT_MAX; 2400f66f451Sopenharmony_ci if (!options) return; 2410f66f451Sopenharmony_ci 2420f66f451Sopenharmony_ci // Parse leading special behavior indicators 2430f66f451Sopenharmony_ci for (;;) { 2440f66f451Sopenharmony_ci if (*options == '^') gof->stopearly++; 2450f66f451Sopenharmony_ci else if (*options == '<') gof->minargs=*(++options)-'0'; 2460f66f451Sopenharmony_ci else if (*options == '>') gof->maxargs=*(++options)-'0'; 2470f66f451Sopenharmony_ci else if (*options == '?') gof->noerror++; 2480f66f451Sopenharmony_ci else if (*options == '&') gof->nodash_now = 1; 2490f66f451Sopenharmony_ci else break; 2500f66f451Sopenharmony_ci options++; 2510f66f451Sopenharmony_ci } 2520f66f451Sopenharmony_ci 2530f66f451Sopenharmony_ci // Parse option string into a linked list of options with attributes. 2540f66f451Sopenharmony_ci 2550f66f451Sopenharmony_ci if (!*options) gof->stopearly++; 2560f66f451Sopenharmony_ci while (*options) { 2570f66f451Sopenharmony_ci char *temp; 2580f66f451Sopenharmony_ci 2590f66f451Sopenharmony_ci // Option groups come after all options are defined 2600f66f451Sopenharmony_ci if (*options == '[') break; 2610f66f451Sopenharmony_ci 2620f66f451Sopenharmony_ci // Allocate a new list entry when necessary 2630f66f451Sopenharmony_ci if (!new) { 2640f66f451Sopenharmony_ci new = xzalloc(sizeof(struct opts)); 2650f66f451Sopenharmony_ci new->next = gof->opts; 2660f66f451Sopenharmony_ci gof->opts = new; 2670f66f451Sopenharmony_ci new->val[0].l = LONG_MIN; 2680f66f451Sopenharmony_ci new->val[1].l = LONG_MAX; 2690f66f451Sopenharmony_ci } 2700f66f451Sopenharmony_ci // Each option must start with "(" or an option character. (Bare 2710f66f451Sopenharmony_ci // longopts only come at the start of the string.) 2720f66f451Sopenharmony_ci if (*options == '(' && new->c != -1) { 2730f66f451Sopenharmony_ci char *end; 2740f66f451Sopenharmony_ci struct longopts *lo; 2750f66f451Sopenharmony_ci 2760f66f451Sopenharmony_ci // Find the end of the longopt 2770f66f451Sopenharmony_ci for (end = ++options; *end && *end != ')'; end++); 2780f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && !*end) error_exit("(longopt) didn't end"); 2790f66f451Sopenharmony_ci 2800f66f451Sopenharmony_ci // init a new struct longopts 2810f66f451Sopenharmony_ci lo = xmalloc(sizeof(struct longopts)); 2820f66f451Sopenharmony_ci lo->next = gof->longopts; 2830f66f451Sopenharmony_ci lo->opt = new; 2840f66f451Sopenharmony_ci lo->str = options; 2850f66f451Sopenharmony_ci lo->len = end-options; 2860f66f451Sopenharmony_ci gof->longopts = lo; 2870f66f451Sopenharmony_ci options = ++end; 2880f66f451Sopenharmony_ci 2890f66f451Sopenharmony_ci // Mark this struct opt as used, even when no short opt. 2900f66f451Sopenharmony_ci if (!new->c) new->c = -1; 2910f66f451Sopenharmony_ci 2920f66f451Sopenharmony_ci continue; 2930f66f451Sopenharmony_ci 2940f66f451Sopenharmony_ci // If this is the start of a new option that wasn't a longopt, 2950f66f451Sopenharmony_ci 2960f66f451Sopenharmony_ci } else if (strchr(":*#@.-%", *options)) { 2970f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && new->type) 2980f66f451Sopenharmony_ci error_exit("multiple types %c:%c%c", new->c, new->type, *options); 2990f66f451Sopenharmony_ci new->type = *options; 3000f66f451Sopenharmony_ci } else if (-1 != (idx = stridx("|^ ;", *options))) new->flags |= 1<<idx; 3010f66f451Sopenharmony_ci // bounds checking 3020f66f451Sopenharmony_ci else if (-1 != (idx = stridx("<>=", *options))) { 3030f66f451Sopenharmony_ci if (new->type == '#' || new->type == '%') { 3040f66f451Sopenharmony_ci long l = strtol(++options, &temp, 10); 3050f66f451Sopenharmony_ci if (temp != options) new->val[idx].l = l; 3060f66f451Sopenharmony_ci } else if (CFG_TOYBOX_FLOAT && new->type == '.') { 3070f66f451Sopenharmony_ci FLOAT f = strtod(++options, &temp); 3080f66f451Sopenharmony_ci if (temp != options) new->val[idx].f = f; 3090f66f451Sopenharmony_ci } else error_exit("<>= only after .#%%"); 3100f66f451Sopenharmony_ci options = --temp; 3110f66f451Sopenharmony_ci 3120f66f451Sopenharmony_ci // At this point, we've hit the end of the previous option. The 3130f66f451Sopenharmony_ci // current character is the start of a new option. If we've already 3140f66f451Sopenharmony_ci // assigned an option to this struct, loop to allocate a new one. 3150f66f451Sopenharmony_ci // (It'll get back here afterwards and fall through to next else.) 3160f66f451Sopenharmony_ci } else if (new->c) { 3170f66f451Sopenharmony_ci new = 0; 3180f66f451Sopenharmony_ci continue; 3190f66f451Sopenharmony_ci 3200f66f451Sopenharmony_ci // Claim this option, loop to see what's after it. 3210f66f451Sopenharmony_ci } else new->c = 127&*options; 3220f66f451Sopenharmony_ci 3230f66f451Sopenharmony_ci options++; 3240f66f451Sopenharmony_ci } 3250f66f451Sopenharmony_ci 3260f66f451Sopenharmony_ci // Initialize enable/disable/exclude masks and pointers to store arguments. 3270f66f451Sopenharmony_ci // (This goes right to left so we need the whole list before we can start.) 3280f66f451Sopenharmony_ci idx = 0; 3290f66f451Sopenharmony_ci for (new = gof->opts; new; new = new->next) { 3300f66f451Sopenharmony_ci unsigned long long u = 1L<<idx++; 3310f66f451Sopenharmony_ci 3320f66f451Sopenharmony_ci if (new->c == 1) new->c = 0; 3330f66f451Sopenharmony_ci new->dex[1] = u; 3340f66f451Sopenharmony_ci if (new->flags & 1) gof->requires |= u; 3350f66f451Sopenharmony_ci if (new->type) { 3360f66f451Sopenharmony_ci new->arg = (void *)nextarg; 3370f66f451Sopenharmony_ci *(nextarg++) = new->val[2].l; 3380f66f451Sopenharmony_ci } 3390f66f451Sopenharmony_ci } 3400f66f451Sopenharmony_ci 3410f66f451Sopenharmony_ci // Parse trailing group indicators 3420f66f451Sopenharmony_ci while (*options) { 3430f66f451Sopenharmony_ci unsigned bits = 0; 3440f66f451Sopenharmony_ci 3450f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && *options != '[') error_exit("trailing %s", options); 3460f66f451Sopenharmony_ci 3470f66f451Sopenharmony_ci idx = stridx("-+!", *++options); 3480f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && idx == -1) error_exit("[ needs +-!"); 3490f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && (options[1] == ']' || !options[1])) 3500f66f451Sopenharmony_ci error_exit("empty []"); 3510f66f451Sopenharmony_ci 3520f66f451Sopenharmony_ci // Don't advance past ] but do process it once in loop. 3530f66f451Sopenharmony_ci while (*options++ != ']') { 3540f66f451Sopenharmony_ci struct opts *opt; 3550f66f451Sopenharmony_ci int i; 3560f66f451Sopenharmony_ci 3570f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && !*options) error_exit("[ without ]"); 3580f66f451Sopenharmony_ci // Find this option flag (in previously parsed struct opt) 3590f66f451Sopenharmony_ci for (i=0, opt = gof->opts; ; i++, opt = opt->next) { 3600f66f451Sopenharmony_ci if (*options == ']') { 3610f66f451Sopenharmony_ci if (!opt) break; 3620f66f451Sopenharmony_ci if (bits&(1<<i)) opt->dex[idx] |= bits&~(1<<i); 3630f66f451Sopenharmony_ci } else { 3640f66f451Sopenharmony_ci if (*options==1) break; 3650f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && !opt) 3660f66f451Sopenharmony_ci error_exit("[] unknown target %c", *options); 3670f66f451Sopenharmony_ci if (opt->c == *options) { 3680f66f451Sopenharmony_ci bits |= 1<<i; 3690f66f451Sopenharmony_ci break; 3700f66f451Sopenharmony_ci } 3710f66f451Sopenharmony_ci } 3720f66f451Sopenharmony_ci } 3730f66f451Sopenharmony_ci } 3740f66f451Sopenharmony_ci } 3750f66f451Sopenharmony_ci} 3760f66f451Sopenharmony_ci 3770f66f451Sopenharmony_ci// Fill out toys.optflags, toys.optargs, and this[] from toys.argv 3780f66f451Sopenharmony_ci 3790f66f451Sopenharmony_civoid get_optflags(void) 3800f66f451Sopenharmony_ci{ 3810f66f451Sopenharmony_ci struct getoptflagstate gof; 3820f66f451Sopenharmony_ci struct opts *catch; 3830f66f451Sopenharmony_ci unsigned long long saveflags; 3840f66f451Sopenharmony_ci char *letters[]={"s",""}; 3850f66f451Sopenharmony_ci 3860f66f451Sopenharmony_ci // Option parsing is a two stage process: parse the option string into 3870f66f451Sopenharmony_ci // a struct opts list, then use that list to process argv[]; 3880f66f451Sopenharmony_ci 3890f66f451Sopenharmony_ci toys.exitval = toys.which->flags >> 24; 3900f66f451Sopenharmony_ci 3910f66f451Sopenharmony_ci // Allocate memory for optargs 3920f66f451Sopenharmony_ci saveflags = 0; 3930f66f451Sopenharmony_ci while (toys.argv[saveflags++]); 3940f66f451Sopenharmony_ci toys.optargs = xzalloc(sizeof(char *)*saveflags); 3950f66f451Sopenharmony_ci 3960f66f451Sopenharmony_ci parse_optflaglist(&gof); 3970f66f451Sopenharmony_ci 3980f66f451Sopenharmony_ci if (toys.argv[1] && toys.argv[1][0] == '-') gof.nodash_now = 0; 3990f66f451Sopenharmony_ci 4000f66f451Sopenharmony_ci // Iterate through command line arguments, skipping argv[0] 4010f66f451Sopenharmony_ci for (gof.argc=1; toys.argv[gof.argc]; gof.argc++) { 4020f66f451Sopenharmony_ci gof.arg = toys.argv[gof.argc]; 4030f66f451Sopenharmony_ci catch = NULL; 4040f66f451Sopenharmony_ci 4050f66f451Sopenharmony_ci // Parse this argument 4060f66f451Sopenharmony_ci if (gof.stopearly>1) goto notflag; 4070f66f451Sopenharmony_ci 4080f66f451Sopenharmony_ci if (gof.argc>1 || *gof.arg=='-') gof.nodash_now = 0; 4090f66f451Sopenharmony_ci 4100f66f451Sopenharmony_ci // Various things with dashes 4110f66f451Sopenharmony_ci if (*gof.arg == '-') { 4120f66f451Sopenharmony_ci 4130f66f451Sopenharmony_ci // Handle - 4140f66f451Sopenharmony_ci if (!gof.arg[1]) goto notflag; 4150f66f451Sopenharmony_ci gof.arg++; 4160f66f451Sopenharmony_ci if (*gof.arg=='-') { 4170f66f451Sopenharmony_ci struct longopts *lo; 4180f66f451Sopenharmony_ci 4190f66f451Sopenharmony_ci gof.arg++; 4200f66f451Sopenharmony_ci // Handle -- 4210f66f451Sopenharmony_ci if (!*gof.arg) { 4220f66f451Sopenharmony_ci gof.stopearly += 2; 4230f66f451Sopenharmony_ci continue; 4240f66f451Sopenharmony_ci } 4250f66f451Sopenharmony_ci 4260f66f451Sopenharmony_ci // do we match a known --longopt? 4270f66f451Sopenharmony_ci for (lo = gof.longopts; lo; lo = lo->next) { 4280f66f451Sopenharmony_ci if (!strncmp(gof.arg, lo->str, lo->len)) { 4290f66f451Sopenharmony_ci if (!gof.arg[lo->len]) gof.arg = 0; 4300f66f451Sopenharmony_ci else if (gof.arg[lo->len] == '=' && lo->opt->type) 4310f66f451Sopenharmony_ci gof.arg += lo->len; 4320f66f451Sopenharmony_ci else continue; 4330f66f451Sopenharmony_ci // It's a match. 4340f66f451Sopenharmony_ci catch = lo->opt; 4350f66f451Sopenharmony_ci break; 4360f66f451Sopenharmony_ci } 4370f66f451Sopenharmony_ci } 4380f66f451Sopenharmony_ci 4390f66f451Sopenharmony_ci // Should we handle this --longopt as a non-option argument? 4400f66f451Sopenharmony_ci if (!lo && gof.noerror) { 4410f66f451Sopenharmony_ci gof.arg -= 2; 4420f66f451Sopenharmony_ci goto notflag; 4430f66f451Sopenharmony_ci } 4440f66f451Sopenharmony_ci 4450f66f451Sopenharmony_ci // Long option parsed, handle option. 4460f66f451Sopenharmony_ci gotflag(&gof, catch); 4470f66f451Sopenharmony_ci continue; 4480f66f451Sopenharmony_ci } 4490f66f451Sopenharmony_ci 4500f66f451Sopenharmony_ci // Handle things that don't start with a dash. 4510f66f451Sopenharmony_ci } else { 4520f66f451Sopenharmony_ci if (gof.nodash_now) toys.optflags |= FLAGS_NODASH; 4530f66f451Sopenharmony_ci else goto notflag; 4540f66f451Sopenharmony_ci } 4550f66f451Sopenharmony_ci 4560f66f451Sopenharmony_ci // At this point, we have the args part of -args. Loop through 4570f66f451Sopenharmony_ci // each entry (could be -abc meaning -a -b -c) 4580f66f451Sopenharmony_ci saveflags = toys.optflags; 4590f66f451Sopenharmony_ci while (*gof.arg) { 4600f66f451Sopenharmony_ci 4610f66f451Sopenharmony_ci // Identify next option char. 4620f66f451Sopenharmony_ci for (catch = gof.opts; catch; catch = catch->next) 4630f66f451Sopenharmony_ci if (*gof.arg == catch->c) 4640f66f451Sopenharmony_ci if (!((catch->flags&4) && gof.arg[1])) break; 4650f66f451Sopenharmony_ci 4660f66f451Sopenharmony_ci // Handle option char (advancing past what was used) 4670f66f451Sopenharmony_ci if (gotflag(&gof, catch) ) { 4680f66f451Sopenharmony_ci toys.optflags = saveflags; 4690f66f451Sopenharmony_ci gof.arg = toys.argv[gof.argc]; 4700f66f451Sopenharmony_ci goto notflag; 4710f66f451Sopenharmony_ci } 4720f66f451Sopenharmony_ci } 4730f66f451Sopenharmony_ci continue; 4740f66f451Sopenharmony_ci 4750f66f451Sopenharmony_ci // Not a flag, save value in toys.optargs[] 4760f66f451Sopenharmony_cinotflag: 4770f66f451Sopenharmony_ci if (gof.stopearly) gof.stopearly++; 4780f66f451Sopenharmony_ci toys.optargs[toys.optc++] = toys.argv[gof.argc]; 4790f66f451Sopenharmony_ci } 4800f66f451Sopenharmony_ci 4810f66f451Sopenharmony_ci // Sanity check 4820f66f451Sopenharmony_ci if (toys.optc<gof.minargs) 4830f66f451Sopenharmony_ci help_exit("Need%s %d argument%s", letters[!!(gof.minargs-1)], 4840f66f451Sopenharmony_ci gof.minargs, letters[!(gof.minargs-1)]); 4850f66f451Sopenharmony_ci if (toys.optc>gof.maxargs) 4860f66f451Sopenharmony_ci help_exit("Max %d argument%s", gof.maxargs, letters[!(gof.maxargs-1)]); 4870f66f451Sopenharmony_ci if (gof.requires && !(gof.requires & toys.optflags)) { 4880f66f451Sopenharmony_ci struct opts *req; 4890f66f451Sopenharmony_ci char needs[32], *s = needs; 4900f66f451Sopenharmony_ci 4910f66f451Sopenharmony_ci for (req = gof.opts; req; req = req->next) 4920f66f451Sopenharmony_ci if (req->flags & 1) *(s++) = req->c; 4930f66f451Sopenharmony_ci *s = 0; 4940f66f451Sopenharmony_ci 4950f66f451Sopenharmony_ci help_exit("Needs %s-%s", s[1] ? "one of " : "", needs); 4960f66f451Sopenharmony_ci } 4970f66f451Sopenharmony_ci 4980f66f451Sopenharmony_ci toys.exitval = 0; 4990f66f451Sopenharmony_ci 5000f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) { 5010f66f451Sopenharmony_ci llist_traverse(gof.opts, free); 5020f66f451Sopenharmony_ci llist_traverse(gof.longopts, free); 5030f66f451Sopenharmony_ci } 5040f66f451Sopenharmony_ci} 505