aux/getflags: support named flags

When using aux/getflags, it produces unnecessarily obscure
names for the flags. This allows the caller of aux/getflags
to support arbitrary names.
This commit is contained in:
Ori Bernstein 2020-04-18 15:38:38 -07:00
parent 8ae77554dd
commit 380adf8b48
3 changed files with 113 additions and 39 deletions

View file

@ -7,62 +7,98 @@ getflags, usage \- command-line parsing for shell scripts
.B aux/usage .B aux/usage
.SH DESCRIPTION .SH DESCRIPTION
.I Getflags .I Getflags
parses the options in its command-line arguments parses the flags in its command-line arguments
according to the environment variable according to the environment variable
.BR $flagfmt . .BR $flagfmt .
This variable should be a list of comma-separated options. This variable should be a comma-separated list of flag specifiers.
Each option can be a single letter, indicating that it does Each flag is a single letter, optionally followed by a
not take arguments, or a letter followed by the space-separated colon and a name. It may be followed by a space-separated
names of its arguments. list of argument names.
.PP
.I Getflags .I Getflags
prints an prints an
.IR rc (1) .IR rc (1)
script on standard output which initializes the script to be evaluated by the calling program.
environment variable For every flag specified in
.BI $flag x .BR $flagfmt ,
for every option mentioned in the generated script sets a corresponding environment variable.
.BR $flagfmt . If the flag specifier contains
If the option is not present on the command-line, the script .BR :name ,
sets that option's flag variable to an empty list. the corresponding variable is named
Otherwise, the script sets that option's flag variable with .BR $name .
a list containing the option's arguments or, Otherwise, it is named
if the option takes no arguments, .BI $flag x.
with the string
.BR 1 . .PP
The script also sets the variable 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 $* .B $*
to the list of arguments following the options. to the list of remaining non-flag arguments.
The final line in the script sets the .PP
The
.B $status .B $status
variable, to the empty string on success is variable to the empty string on success, or
and to the string .B 'usage'
.B usage
when there is an error parsing the command line. when there is an error parsing the command line.
.PP .PP
.I Usage .I Usage
prints a usage message to standard error. prints a usage message to standard error.
It creates the message using The message is constructed using
.BR $flagfmt ,
as described above,
.BR $args ,
which should contain the string to be printed explaining
non-option arguments,
and
.BR $0 , .BR $0 ,
the program name .BR $flagfmt ,
(see and
.IR rc (1)). .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 .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 Parse the arguments for
.IR leak (1): .IR leak (1):
.IP .IP
.EX .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' args='name | pid list'
if(! ifs=() eval `{aux/getflags $*} || ~ $#* 0){ if(! ifs=() eval `{aux/getflags $*} || ~ $#* 0){
aux/usage aux/usage
exit 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 .EE
.SH SOURCE .SH SOURCE
.B /sys/src/cmd/aux/getflags.c .B /sys/src/cmd/aux/getflags.c

View file

@ -1,5 +1,6 @@
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
#include <ctype.h>
void void
usage(void) usage(void)
@ -22,6 +23,21 @@ findarg(char *flags, Rune r)
return nil; 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 int
countargs(char *p) countargs(char *p)
{ {
@ -39,7 +55,7 @@ countargs(char *p)
void void
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char *flags, *p, buf[512]; char *flags, *p, *s, *e, buf[512];
int i, n; int i, n;
Fmt fmt; Fmt fmt;
@ -55,19 +71,38 @@ main(int argc, char *argv[])
} }
fmtfdinit(&fmt, 1, buf, sizeof buf); fmtfdinit(&fmt, 1, buf, sizeof buf);
for(p=flags; p!=(char*)1; p=strchr(p, ',')+1) for(p=flags; p!=(char*)1 && *p != 0; p=strchr(p, ',')+1){
fmtprint(&fmt, "flag%.1s=()\n", p); 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{ ARGBEGIN{
default: default:
if((p = findarg(flags, ARGC())) == nil) if((p = findarg(flags, ARGC())) == nil)
usage(); usage();
p += runelen(ARGC()); p += runelen(ARGC());
s = p + 1;
e = p + 1;
if(*p == ':' && (e = argname(s)) != s)
p = e;
if(*p == ',' || *p == 0){ 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; break;
} }
n = countargs(p); 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<n; i++) for(i=0; i<n; i++)
fmtprint(&fmt, "%s%q", i ? " " : "", EARGF(usage())); fmtprint(&fmt, "%s%q", i ? " " : "", EARGF(usage()));
fmtprint(&fmt, ")\n"); fmtprint(&fmt, ")\n");

View file

@ -31,6 +31,9 @@ main(void)
single = 0; single = 0;
for(p=flags; *p; ){ for(p=flags; *p; ){
p += chartorune(&r, p); p += chartorune(&r, p);
if(*p == ':')
while(*p != '\0' && *p != ',' && *p != ' ')
p++;
if(*p == ',' || *p == 0){ if(*p == ',' || *p == 0){
if(!single){ if(!single){
fmtprint(&fmt, " [-"); fmtprint(&fmt, " [-");