diff --git a/sys/man/8/getflags b/sys/man/8/getflags index 84ef31d3a..b0ea40835 100644 --- a/sys/man/8/getflags +++ b/sys/man/8/getflags @@ -7,62 +7,98 @@ getflags, usage \- command-line parsing for shell scripts .B aux/usage .SH DESCRIPTION .I Getflags -parses the options in its command-line arguments +parses the flags in its command-line arguments according to the environment variable .BR $flagfmt . -This variable should be a list of comma-separated options. -Each option can be a single letter, indicating that it does -not take arguments, or a letter followed by the space-separated -names of its arguments. +This variable should be a comma-separated list of flag specifiers. +Each flag is a single letter, optionally followed by a +colon and a name. It may be followed by a space-separated +list of argument names. + +.PP .I Getflags prints an .IR rc (1) -script on standard output which initializes the -environment variable -.BI $flag x -for every option mentioned in -.BR $flagfmt . -If the option is not present on the command-line, the script -sets that option's flag variable to an empty list. -Otherwise, the script sets that option's flag variable with -a list containing the option's arguments or, -if the option takes no arguments, -with the string -.BR 1 . -The script also sets the variable +script to be evaluated by the calling program. +For every flag specified in +.BR $flagfmt , +the generated script sets a corresponding environment variable. +If the flag specifier contains +.BR :name , +the corresponding variable is named +.BR $name . +Otherwise, it is named +.BI $flag x. + +.PP +After evaluating the script, the environment variables will +be set as follows: +If a flag is not present in the argument list, the environment +variable will default to the empty list. +If the flag is present and takes no arguments, the environment +variable will be initialized with the string +.BR '1' . +If the flag takes arguments, the flag's variable will be initialized +with a list of those argument values. +The script then sets the variable .B $* -to the list of arguments following the options. -The final line in the script sets the +to the list of remaining non-flag arguments. +.PP +The .B $status -variable, to the empty string on success -and to the string -.B usage +is variable to the empty string on success, or +.B 'usage' when there is an error parsing the command line. .PP .I Usage prints a usage message to standard error. -It creates the message using -.BR $flagfmt , -as described above, -.BR $args , -which should contain the string to be printed explaining -non-option arguments, -and +The message is constructed using .BR $0 , -the program name -(see -.IR rc (1)). +.BR $flagfmt , +and +.BR $args . +The program name is taken from +.BR $0 , +as set by +.IR rc (1) +The list of flags is extracted from +.BR $flagfmt . +The description of positional argument list is taken from +.BR $args . + .SH EXAMPLE +.PP +An example of the script generated: +.IP +.EX +% flagfmt='e:example,x,a:arg with args' +% aux/getflags -exa arg list positional stuff +example=() +flagx=() +arg=() +example=1 +flagx=1 +arg=(arg list) +*=(positional stuff) +status='' +.EE +.PP Parse the arguments for .IR leak (1): .IP .EX -flagfmt='b,s,f binary,r res,x width' +flagfmt='b:showbmp,s:acidfmt,f binary,r res,x width' args='name | pid list' if(! ifs=() eval `{aux/getflags $*} || ~ $#* 0){ aux/usage exit usage } +if(~ $#showbmp 0) + echo '-b flag not set' +echo $showbmp # named +echo $acidfmt # also named +echo $flagf # default name +echo $flagr # default name .EE .SH SOURCE .B /sys/src/cmd/aux/getflags.c diff --git a/sys/src/cmd/aux/getflags.c b/sys/src/cmd/aux/getflags.c index 407b73781..880f0dc38 100644 --- a/sys/src/cmd/aux/getflags.c +++ b/sys/src/cmd/aux/getflags.c @@ -1,5 +1,6 @@ #include #include +#include void usage(void) @@ -22,6 +23,21 @@ findarg(char *flags, Rune r) return nil; } +char* +argname(char *p) +{ + Rune r; + int n; + + while(1){ + n = chartorune(&r, p); + if(!isalpharune(r) && !isdigitrune(r)) + break; + p += n; + } + return p; +} + int countargs(char *p) { @@ -39,7 +55,7 @@ countargs(char *p) void main(int argc, char *argv[]) { - char *flags, *p, buf[512]; + char *flags, *p, *s, *e, buf[512]; int i, n; Fmt fmt; @@ -55,19 +71,38 @@ main(int argc, char *argv[]) } fmtfdinit(&fmt, 1, buf, sizeof buf); - for(p=flags; p!=(char*)1; p=strchr(p, ',')+1) - fmtprint(&fmt, "flag%.1s=()\n", p); + for(p=flags; p!=(char*)1 && *p != 0; p=strchr(p, ',')+1){ + s = e = nil; + if(p[1] == ':'){ + s = p + 2; + e = argname(s); + } + if(s != e) + fmtprint(&fmt, "%.*s=()\n", (int)(e - s), s); + else + fmtprint(&fmt, "flag%.1s=()\n", p); + } ARGBEGIN{ default: if((p = findarg(flags, ARGC())) == nil) usage(); p += runelen(ARGC()); + s = p + 1; + e = p + 1; + if(*p == ':' && (e = argname(s)) != s) + p = e; if(*p == ',' || *p == 0){ - fmtprint(&fmt, "flag%C=1\n", ARGC()); + if(s != e) + fmtprint(&fmt, "%.*s=1\n", (int)(e - s), s); + else + fmtprint(&fmt, "flag%C=1\n", ARGC()); break; } n = countargs(p); - fmtprint(&fmt, "flag%C=(", ARGC()); + if(s != e) + fmtprint(&fmt, "%.*s=(", (int)(e - s), s); + else + fmtprint(&fmt, "flag%C=(", ARGC()); for(i=0; i