From: SMTP%"titandmp@ftp-gw-1.pa.dec.com" 5-SEP-1994 12:53:21.45 To: USRC CC: Subj: v44i050: arg_parse - simple, powerful argument parser, Part01/01 Resent-Date: 5 Sep 1994 11:42:27 -0500 Path: decwrl!spool.mu.edu!howland.reston.ans.net!math.ohio-state.edu!jussieu.fr!univ-lyon1.fr!swidir.switch.ch!newsfeed.ACO.net!Austria.EU.net!EU.net!uunet!sparky!not-for-mail From: Paul.Heckbert@HOSTESS.GRAPHICS.CS.CMU.EDU (Paul Heckbert) Newsgroups: comp.sources.misc Subject: v44i050: arg_parse - simple, powerful argument parser, Part01/01 Followup-To: comp.sources.d Date: 5 Sep 1994 11:42:27 -0500 Organization: Sterling Software Lines: 2338 Sender: kent@sparky.sterling.com Approved: kent@sparky.sterling.com Message-Id: <34fhpj$2kp@sparky.sterling.com> Nntp-Posting-Host: sparky.sterling.com X-Md4-Signature: e6610ffa95163eabbcc84e048770f37f To: unix-sources@pa.dec.com Resent-Message-Id: <"5cwdh3.0.U31.MnqQk"@ftp-gw-1.pa.dec.com> Resent-From: unix-sources@pa.dec.com X-Mailing-List: archive/latest/65 X-Loop: unix-sources@pa.dec.com Precedence: list Resent-Sender: unix-sources-request@pa.dec.com Submitted-by: Paul.Heckbert@hostess.graphics.cs.cmu.edu (Paul Heckbert) Posting-number: Volume 44, Issue 50 Archive-name: arg_parse/part01 Environment: Sun, DEC, SGI, HP arg_parse is a subroutine for parsing command-line arguments. It is very easy to use, and yields a powerful, consistent command-line interface to programs. It supports argument conversion and type checking, arbitrary argument order, multi-character flag names, automatic usage messages, and expression evaluation. It is written in C. I've tested it on Sun, DEC, SGI (MIPS), and HP machines and it works perfectly. I haven't tested it much on other machine types. Paul Heckbert ph@cs.cmu.edu Computer Science Dept., Carnegie Mellon University 5000 Forbes Ave, Pittsburgh PA 15213-3891, USA ============================================================ Here is a more complete description, from the man page: ============================================================ DESCRIPTION arg_parse is a subroutine for parsing and conversion of command-line arguments. This parser is an alternative to the common method of argument parsing, which is an ad-hoc parser in each program, typically written with a large, cumbersome switch statement. arg_parse allows a command- line parser to be described very concisely while retaining the flexibility to handle a variety of syntaxes. The parser has a number of features: + arbitrary order of flag arguments + automatic argument conversion and type checking + multiple-character flag names + required, optional, and flag arguments + automatic usage message + subroutine call for exotic options (variable number of parameters) + modularized parsers encourage standardized options + expression evaluation + works either from argv or in interactive mode, as a primitive language parser and interpreter + concise specification + easy to use It is hoped that use of arg_parse will help standardize argument conventions and reduce the tedium of adding options to programs. APPETIZER Here is a simple example: #include main(argc, argv) int argc; char **argv; { char *file; int level = 3, debug; double xsize = 20., ysize = 10.; arg_parse(argc, argv, "", "Usage: prog [options]", "%S", &file, "set output file", "[%d]", &level, "set recursion level [default=%d]", level, "-size %F %F", &xsize, &ysize, "set x and y sizes", "-debug", ARG_FLAG(&debug), "turn on debugging", 0); The arg_parse call defines the program's arguments, in this case: one required argument (a filename), an optional argu- ment (an integer level number), an optional flag with two parameters (floating point size), and a simple flag (boolean debug flag). If the above program (call it prog) were run with prog joe.c it would set file to joe.c, and set debug to 0, and if run with prog -size 100 400/3 joe.c -debug 5 it would set file="joe.c", level=5, xsize=100, ysize=133.33, and debug=1. In all programs using arg_parse, a hyphen arguments elicits a usage message, so the command prog - results in the printout Usage: prog [options] %S set output file [%d] set recursion level [default=3] -size %F %F set x and y sizes -debug turn on debugging --------- #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # Contents: libarg libarg/Makefile libarg/README libarg/arg.c # libarg/arg.h libarg/arg_parse.3 libarg/expr.c libarg/expr.h # libarg/simple.h libarg/tb.c # Wrapped by kent@sparky on Mon Sep 5 11:39:15 1994 PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 1 (of 1)."' if test ! -d 'libarg' ; then echo shar: Creating directory \"'libarg'\" mkdir 'libarg' fi if test -f 'libarg/Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/Makefile'\" else echo shar: Extracting \"'libarg/Makefile'\" \(593 characters\) sed "s/^X//" >'libarg/Makefile' <<'END_OF_FILE' X# Makefile for libarg X# $Header: /gourd/usr2/ph/sys/libsys/libarg/RCS/Makefile,v 4.2 94/08/03 20:09:41 ph Exp Locker: ph $ X XDEST = /usr/johndoe XCOPTS = -g XIPATH = -I. XCFLAGS = $(COPTS) $(IPATH) $(CONFIG) XLIB = libarg.a X Xall: tb X X$(LIB): arg.o expr.o X ar rcu $(LIB) arg.o expr.o X ranlib $(LIB) X Xexpr: expr.c X cc $(COPTS) -o expr -DMAIN expr.c -lm X Xinstall: all X mv $(LIB) $(DEST)/lib X cp arg.h expr.h simple.h $(DEST)/include X X# test programs X Xtb: tb.o $(LIB) X cc $(COPTS) -o tb tb.o $(LIB) -lm X X# misc X Xprint: X tbl arg_parse.3 | troff -man X Xclean: X rm -f $(LIB) *.o tb X Xarg.o expr.o: simple.h END_OF_FILE if test 593 -ne `wc -c <'libarg/Makefile'`; then echo shar: \"'libarg/Makefile'\" unpacked with wrong size! fi # end of 'libarg/Makefile' fi if test -f 'libarg/README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/README'\" else echo shar: Extracting \"'libarg/README'\" \(799 characters\) sed "s/^X//" >'libarg/README' <<'END_OF_FILE' XThis is source code to an argument parser with lots of nifty features. XIt's written in C (Kernighan and Ritchie, not ANSI). X XSee the man page arg_parse.3 for documentation. XTo test it run "make" and then play with the "tb" command. X XThis code is available via anonymous ftp from Xhostess.graphics.cs.cmu.edu (128.2.206.188) in /usr/ph/ftp/libarg.tar.Z XAfter you "get" the file, run X zcat libarg.tar.Z | tar xvf - X XIf you find significant bugs in it, like it a lot, or enhance it significantly, Xplease send me email. X XPaul Heckbert ph@cs.cmu.edu XComputer Science Dept., Carnegie Mellon University X5000 Forbes Ave, Pittsburgh PA 15213-3891, USA X X3 Aug 94 X X$Header: /gourd/usr2/ph/sys/libsys/libarg/RCS/README,v 4.2 94/08/03 20:39:58 ph Exp Locker: ph $ END_OF_FILE if test 799 -ne `wc -c <'libarg/README'`; then echo shar: \"'libarg/README'\" unpacked with wrong size! fi # end of 'libarg/README' fi if test -f 'libarg/arg.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/arg.c'\" else echo shar: Extracting \"'libarg/arg.c'\" \(21871 characters\) sed "s/^X//" >'libarg/arg.c' <<'END_OF_FILE' X/* X * arg_parse: Command line argument parser. X * X * notable features: X * arbitrary order of flag arguments X * automatic argument conversion and type checking X * multiple-character flag names X * required, optional, and flag arguments X * automatic usage message X * subroutine call for exotic options (variable number of parameters) X * modularized parsers encourage standardized options X * expression evaluation X * works either from argv or in interactive mode, X * as a primitive language parser and interpreter X * concise specification X * easy to use X * X * Paul Heckbert ph@cs.cmu.edu X * X * 19 April 1988 - written at UC Berkeley X * X * simpler version written at Pacific Data Images, Aug 1985. X * Ideas borrowed from Ned Greene's ARGS at New York Inst. of Tech. X * and Alvy Ray Smith's AARG at Pixar. X */ X Xstatic char rcsid[] = "$Header: /gourd/usr2/ph/sys/libsys/RCS/arg.c,v 4.2 94/08/03 15:41:10 ph Exp Locker: ph $"; X X#include X#include X#include X X#include "simple.h" X#include "arg.h" X X#define CHECKTYPE(form, keyword) \ X if (form->type!=0) { \ X fprintf(stderr, "arg: %s doesn't belong in %s paramlist\n", \ X keyword, form->format); \ X return 0; \ X } \ X else X X/* recognize a valid numeric constant or expression by its first char: */ X#define NUMERIC(s) (isdigit(*(s)) || \ X *(s)=='.' || *(s)=='-' || *(s)=='+' || *(s)=='(') X Xint arg_debug = 0; /* debugging level; 0=none */ Xint arg_doccol = 24; /* column at which to print doc string*/ Xint arg_warning = 1; /* print warnings about repeated flags? */ X Xstatic Arg_form *regf; /* advancing form ptr used by arg_find_reg */ X Xva_list arg_doc_parse(); X X/* X * arg_parse(ac, av, varargs_list) X * Parse the arguments in av according to the varargs list, which contains X * format strings, parameter and subroutine ptrs, and other stuff. X * The varargs list must be terminated by a 0. X * Returns an error code. X */ X Xarg_parse(va_alist) Xva_dcl X{ X char **av; X int ac, ret; X va_list ap; X Arg_form *form; X X va_start(ap); X ac = va_arg(ap, int); X av = va_arg(ap, char **); X if (ac<1 || ac>ARG_NARGMAX) { X fprintf(stderr, X "arg_parse: first arg to arg_parse (%d) doesn't look like argc\n", X ac); X return ARG_BADCALL; X } X X /* convert varargs to formlist */ X form = arg_to_form1(ap); X if (!form) X return ARG_BADCALL; X X /* parse args according to form */ X if (ac==2 && str_eq(av[1], "-stdin")) /* no args, run interactive version */ X ret = arg_parse_stream(stdin, form); X else /* args supplied, parse av */ X ret = arg_parse_argv(ac, av, form); X return ret; X} X X/*----------------------------------------------------------------------*/ X X/* X * arg_to_form: convert varargs to formlist X * not called by arg_parse, but sometimes called from outside to build sublists X */ X XArg_form *arg_to_form(va_alist) Xva_dcl X{ X va_list ap; X X va_start(ap); X return arg_to_form1(ap); X} X X/* X * arg_to_form1: convert varargs to formlist recursively. X * assumes va_start has already been called X * calls va_end when done to clean up X * returns 0 on error. X */ X XArg_form *arg_to_form1(ap) Xva_list ap; X{ X char *s, *prevs; X int pi, t; X Arg_form *form, *prevform, *rootform; X X /* X * varargs syntax is: X * formatstr [KEYWORD val] paramptr* docstr docargptr* X * where there are as many paramptrs as %'s in the format string X * and as many docargptrs as %'s in the doc string X */ X rootform = 0; X prevs = ""; X for (prevform=0; (s = va_arg(ap, char *)) != 0; prevform=form) { X X /* first, read the format string */ X if (checkstr(s, "format string", prevs)) return 0; X ALLOC(form, Arg_form, 1); X form->next = 0; X form->format = s; X form->flag = 0; X form->type = 0; X form->param = 0; X form->parammask = 0; X form->subr = 0; X form->sublist = 0; X if (prevform) prevform->next = form; X else rootform = form; X X /* parse format to create flag and code strings, compute #params */ X t = arg_format(form); X if (t) return 0; X X /* next, read the parameters and keywords */ X pi = 0; X if (form->nparam>0) { X form->type = form->flag[0]=='-' ? ARG_PARAMFLAG : ARG_REGULAR; X assert(form->param = (int **)malloc(form->nparam*sizeof(int *))); X } X for (; (s = va_arg(ap, char *)) != 0;) { X /* note that we continue (not break) in all cases except one */ X switch ((int)s) { X case ARG_FLAGNEXT: /* ptr to flag vbl */ X CHECKTYPE(form, "FLAG"); X form->type = ARG_SIMPFLAG; X ALLOC(form->param, int *, 1); X *form->param = va_arg(ap, int *); X continue; X case ARG_SUBRNEXT: /* ptr to action subr */ X CHECKTYPE(form, "SUBR"); X form->type = ARG_SUBRFLAG; X form->subr = (int (*)())va_arg(ap, int *); X /* append dots to end of format string */ X assert(s = (char *)malloc(strlen(form->format)+5)); X sprintf(s, "%s ...", form->format); X form->format = s; X continue; X case ARG_LISTNEXT: /* ptr to sub-formlist */ X CHECKTYPE(form, "SUBLIST"); X form->type = ARG_SUBLISTFLAG; X form->sublist = va_arg(ap, Arg_form *); X continue; X default: /* ptr to param */ X if (pi>=form->nparam) break; X form->param[pi++] = (int *)s; X continue; X } X break; /* end of params/keywords */ X } X X if (!form->flag[0] && form->type==ARG_SUBLISTFLAG) { X fprintf(stderr, "arg: sublist must be given a flag name\n"); X return 0; X } X if (!form->type) /* just a doc string */ X form->type = ARG_NOP; X /* finally, read the doc string */ X if (checkstr(s, "doc string", form->format)) return 0; X form->doc = prevs = s; X X /* skip over doc args */ X ap = arg_doc_parse(form, ap); X } X va_end(ap); X return rootform; X} X X/* checkstr: check that s is a valid string */ X Xstatic checkstr(s, name, prev) Xchar *s, *name, *prev; X{ X char *delim; X X delim = prev ? "\"" : ""; X if (!s || (int)s&ARG_MASKNEXT) { X fprintf(stderr, "bad arg call: missing %s after %s%s%s\n", X name, delim, prev, delim); X return 1; X } X return 0; X} X X/* X * arg_format: parse the format string to create flag string, X * code string, parammask, and count the number of params. X * e.g.: format="-size %d %F" => flag="-size", code="dF", nparam=2 X */ X Xarg_format(f) XArg_form *f; X{ X char *s, *c; X int n, np; X X if (f->format[0]=='-') { /* flag string present */ X /* find the end of the flag string, put flag string in f->flag */ X for (s= &f->format[1]; *s && *s!=' ' && *s!='%' && *s!='['; s++); X n = s-f->format; X assert(f->flag = (char *)malloc(n+1)); X bcopy(f->format, f->flag, n); X f->flag[n] = 0; X } X else { X s = f->format; /* no flag string: probably a reg arg */ X f->flag = ""; /* or maybe a flagless subrflag */ X } X X /* extract scanf codes from remainder of format string, put in f->code */ X n = (f->format+strlen(f->format)-s)/2; /* overestimate # of % codes */ X assert(f->code = (char *)malloc(n+1)); X for (c=f->code, np=0;; np++, s++) { X for (; *s==' ' || *s=='['; s++) X if (*s=='[') f->parammask |= 1<format); X return ARG_BADCALL; X } X *c++ = *++s; X } X for (; *s; s++) X if (*s!=' ' && *s!=']') { X fprintf(stderr, "bad format (%s), nothing allowed after ']'s\n", X f->format); X return ARG_BADCALL; X } X f->parammask |= 1<=8*sizeof(int)) { X fprintf(stderr, "out of bits in parammask! too many params to %s\n", X f->flag); X return ARG_BADCALL; X } X X /* number of parameters to flag = number of '%'s in format string */ X f->nparam = np; X *c = 0; X if (c-f->code!=f->nparam) fprintf(stderr, "OUCH!\n"); X return 0; X} X X/* X * arg_doc_parse: Find the '%' format codes in f->doc and increment varargs X * ptr ap over the doc string parameters. Updates f->doc to be the formatted X * documentation string and returns the new ap. X */ X Xva_list arg_doc_parse(f, ap) XArg_form *f; Xva_list ap; X{ X char *s, buf[256]; X int size, gotparam; X va_list ap0; X X ap0 = ap; X gotparam = 0; X for (s=f->doc; *s; s++) { X for (; *s; s++) /* search for next format code */ X if (s[0]=='%') X if (s[1]=='%') s++; /* skip over %% */ X else break; X if (!*s) break; X /* skip over numerical parameters */ X for (s++; *s && *s=='-' || *s>'0'&&*s<='9' || *s=='.'; s++); X /* now *s points to format code */ X switch (*s) { X case 'h': size = 0; s++; break; /* half */ X case 'l': size = 2; s++; break; /* long */ X default : size = 1; break; /* normal size */ X } X if (!*s) { X fprintf(stderr, "arg: premature end of string in (%s)\n", f->doc); X break; X } X gotparam = 1; X /* X * simulate printf's knowledge of type sizes X * (it's too bad we have to do this) X */ X switch (*s) { X case 'd': case 'D': X case 'o': case 'O': X case 'x': case 'X': X case 'c': X if (size==2 || *s>='A' && *s<='Z') va_arg(ap, long); X else va_arg(ap, int); X break; X case 'e': X case 'f': X case 'g': X /* note: float args are converted to doubles by MOST compilers*/ X va_arg(ap, double); X break; X case 's': X va_arg(ap, char *); X break; X default: X fprintf(stderr, "arg: unknown format code %%%c in %s\n", X *s, f->doc); X va_arg(ap, int); X break; X } X } X if (gotparam) { /* there are doc parameters, format a new doc string */ X vsprintf(buf, f->doc, ap0); X assert(f->doc = (char *)malloc(sizeof(buf)+1)); X strcpy(f->doc, buf); X } X X return ap; /* varargs ptr past end of doc params */ X} X X/*----------------------------------------------------------------------*/ X X#define LINEMAX 256 X#define ACMAX 128 X Xtypedef enum {SPACE, TOKEN, END} Token_type; XToken_type token_char(); X X/* X * arg_parse_stream: parse from an input stream (not from an arg vector) X * parse args in stream fp, line by line, according to formlist in form X * Returns 0 on success, negative on failure. X */ X Xarg_parse_stream(fp, form) XFILE *fp; XArg_form *form; X{ X char c, *av[ACMAX], line[LINEMAX], *p; X Token_type type; X int i, ac, ret, err; X Arg_form *oldregf; X X oldregf = regf; X regf = form; X arg_init(form); X X av[0] = "hi"; X ret = 0; X for (;;) { /* read and process line */ X p = line; X while ((type = token_char(fp, &c))==SPACE); X for (ac=1; type!=END && ac= line+LINEMAX) { X fprintf(stderr, "input line too long\n"); X exit(1); X } X } while ((type = token_char(fp, &c))==TOKEN); X *p++ = 0; /* terminate this token in line[] */ X if (type==END) break; X while ((type = token_char(fp, &c))==SPACE); X } X if (feof(fp)) break; X if (arg_debug) { X fprintf(stderr, "ac=%d: ", ac); X for (i=1; inext) X if (f->type==ARG_SUBLISTFLAG) arg_init(f->sublist); /* recurse */ X else { X f->rep = 0; X if (f->type==ARG_SIMPFLAG) **f->param = 0; X } X} X Xarg_done() X{ X for (; regf; regf=regf->next) /* any required reg args remaining? */ X if (regf->type==ARG_REGULAR && !(regf->parammask&1)) { X fprintf(stderr, "regular arg %s (%s) not set\n", X regf->format, regf->doc); X return ARG_MISSING; X } X return 0; X} X X/* X * arg_find_flag: find the flag matching arg in the form list (tree) X * returns form ptr if found, else 0 X */ X XArg_form *arg_find_flag(arg, form) Xchar *arg; XArg_form *form; X{ X Arg_form *f, *t; X X for (f=form; f; f=f->next) { X if (f->type!=ARG_REGULAR && f->type!=ARG_NOP && str_eq(f->flag, arg)) X return f; X if (f->type==ARG_SUBLISTFLAG) { X t = arg_find_flag(arg, f->sublist); /* recurse */ X if (t) return t; X } X } X return 0; X} X X/* X * arg_find_reg: find next regular argument X * each call advances the global pointer regf through the formlist X */ X XArg_form *arg_find_reg() X{ X Arg_form *f; X X for (; regf; regf=regf->next) { X if (regf->type==ARG_REGULAR) { X f = regf; X regf = regf->next; X return f; X } X } X return 0; X} X X/* X * arg_do: process one form by parsing arguments in av according to the X * single form in f X * X * f was found by arg_find_flag or arg_find_reg, X * so if f is a flag then we know av[-1] matches f->flag X * X * examine av[0]-av[ac-1] to determine number of parameters supplied X * if simpleflag, set flag parameter and read no args X * if subrflag, call subroutine on sub-args X * if sublist, call arg_parse_form on sub-args X * else it's a paramflag or regular arg, do arg-to-param assignments X * return number of arguments gobbled, or negative error code X */ X Xarg_do(ac, av, f) Xint ac; Xchar **av; XArg_form *f; X{ X int narg, skip, used, err, i; X X if (arg_debug) X av_print(" arg_do", ac, av); X if (f->type==ARG_SIMPFLAG || f->type==ARG_PARAMFLAG) { X /* don't complain about repeated subrflags or sublists */ X assert(str_eq(av[-1], f->flag)); X f->rep++; X if (f->rep>1 && arg_warning) X fprintf(stderr, "warning: more than one %s flag in arglist\n", X f->flag); X } X X narg = nargs(ac, av, f, &skip); X X used = 0; X switch (f->type) { X case ARG_SIMPFLAG: X **f->param = 1; X break; X case ARG_SUBRFLAG: X (*f->subr)(narg, av); X break; X case ARG_SUBLISTFLAG: X arg_parse_argv(narg+1, &av[-1], f->sublist); /* recurse */ X used = narg; X break; X default: /* convert parameters */ X err = scan(narg, av, f); X if (err) return err; X used = nargnparam ? narg : f->nparam; X break; X } X X if ((f->type==ARG_REGULAR || f->type==ARG_PARAMFLAG) && used!=narg) { X fprintf(stderr, "warning: %d unused arg%s to %s: ", X narg-used, narg-used>1 ? "s" : "", av[-1]); X for (i=used; inparam; X mask = f->parammask; X flag = f->type==ARG_REGULAR ? f->format : f->flag; X voracious = f->type==ARG_SUBRFLAG || f->type==ARG_SUBLISTFLAG; X /* subrs&sublists want all the args they can get */ X X level = 0; X av0 = au = av; X if (voracious) np = 999; X for (die=0, i=0; i>j&1); j++); /* go until we can stop */ X /* try to grab params i through j-1 */ X for (; i0; i++, au++, av++) { X if (au!=av) *au = *av; X if (str_eq(*av, "-{")) { X if (level<=0) au--; /* skip "-{" in av if level 0 */ X level++; /* push a level */ X } X else if (str_eq(*av, "-}")) { X level--; /* pop a level */ X if (level<=0) au--; /* skip "-}" in av if level 0 */ X if (level<0) X fprintf(stderr, "ignoring spurious -}\n"); X } X else if (level==0 && av[0][0]=='-' && !NUMERIC(&av[0][1])) { X die = 1; /* break out of both loops */ X break; /* encountered flag at level 0 */ X } X } X } X if (arg_debug) { X fprintf(stderr, " %s: requested %d, got %d args: ", X flag, np, au-av0); X for (j=0; jnparamnparam; X if (!(f->parammask>>narg&1)) { X fprintf(stderr, "you can't give %s just %d params\n", X f->format, narg); X return ARG_MISSING; X } X for (p=f->param, i=0; icode[i]; X switch (str[1]) { X case 'S': X /* X * dynamically allocate memory for string X * for arg_parse_argv: in case argv gets clobbered (rare) X * for arg_parse_stream: since line[] buffer is reused (always) X */ X ALLOC(s, char, strlen(arg[i])+1); X strcpy(s, arg[i]); X *(char **)*p = s; X break; X case 's': /* scanf "%s" strips leading, trailing blanks */ X strcpy(*p, arg[i]); X break; X case 'd': X *(int *)*p = expr_eval_int(arg[i]); X if (expr_error==EXPR_BAD) { /* expression is garbage */ X fprintf(stderr, "bad %s param\n", f->flag); X return ARG_BADARG; X } X break; X case 'D': X *(long *)*p = expr_eval_long(arg[i]); X if (expr_error==EXPR_BAD) { /* expression is garbage */ X fprintf(stderr, "bad %s param\n", f->flag); X return ARG_BADARG; X } X break; X case 'f': case 'F': X x = expr_eval(arg[i]); X if (expr_error==EXPR_BAD) { /* expression is garbage */ X fprintf(stderr, "bad %s param\n", f->flag); X return ARG_BADARG; X } X if (str[1]=='f') *(float *)*p = x; X else *(double *)*p = x; X break; X default: X if (sscanf(arg[i], str, *p) != 1) { X fprintf(stderr, "bad %s param: \"%s\" doesn't match %s\n", X f->flag, arg[i], str); X return ARG_BADARG; X } X break; X } X } X return 0; /* return 0 on success */ X} X Xstatic char *bar = "==================================================\n"; X X/* arg_form_print: print Arg_form as usage message to stderr */ X Xstatic arg_form_print(form) XArg_form *form; X{ X Arg_form *f; X X for (f=form; f; f=f->next) { X if (f->type!=ARG_NOP || f->format[0]) { X fprintf(stderr, "%s", f->format); X space(stderr, strlen(f->format), arg_doccol); X } X fprintf(stderr, "%s\n", f->doc); X if (arg_debug) X fprintf(stderr, " %d (%s) [%s][%s]%x (%s)\n", X f->type, f->format, f->flag, f->code, f->parammask, f->doc); X if (f->type==ARG_SUBLISTFLAG) { X fprintf(stderr, bar); X arg_form_print(f->sublist); X fprintf(stderr, bar); X } X } X} X X/* X * space: currently in column c; tab and space over to column c1 X * assumes 8-space tabs X */ X Xstatic space(fp, c, c1) XFILE *fp; Xint c, c1; X{ X if (c>=c1) { X putc('\n', fp); X c = 0; X } X for (; c'libarg/arg.h' <<'END_OF_FILE' X/* arg.h: definitions for argument parsing package */ X X#ifndef ARG_HDR X#define ARG_HDR X X/* $Header: /gourd/usr2/ph/sys/libsys/RCS/arg.h,v 4.2 94/08/03 15:41:11 ph Exp Locker: ph $ */ X X#include X#include X Xtypedef struct arg_form { /* ARGUMENT FORM */ X X /* a "form" contains the format, doc string, and miscellaneous internal */ X /* info about an argument. It's an argument descriptor, basically */ X X struct arg_form *next; /* next in linked list */ X char *format; /* scanf-style format: "-size %d %F" */ X char *flag; /* flag portion of format:"-size" */ X char *code; /* just the format codes: "dF" */ X char *doc; /* documentation string: "set widget size" */ X short type; /* REGULAR | SIMPFLAG | PARAMFLAG | X SUBRFLAG | SUBLISTFLAG | NOP */ X short nparam; /* number of parameters to flag */ X int parammask; /* bit i says ok to stop before param i, i=0..*/ X int **param; /* parameter pointer list */ X int (*subr)(); /* subroutine to call for action (if any) */ X struct arg_form *sublist; /* subordinate list (if any) */ X short rep; /* # times this flag repeated in arglist */ X} Arg_form; X X/* form type values */ X#define ARG_REGULAR 1 /* a regular argument */ X#define ARG_SIMPFLAG 2 /* a simple flag (no parameters) */ X#define ARG_PARAMFLAG 3 /* a flag with parameters */ X#define ARG_SUBRFLAG 4 /* a flag with subroutine action */ X#define ARG_SUBLISTFLAG 5 /* a sub-formlist */ X#define ARG_NOP 6 /* no arg or flag, just a doc string */ X X/* the following must be impossible pointer values (note: machine-dependent) */ X#define ARG_MASKNEXT 0x80000000 /* mask for these NEXT flags */ X#define ARG_FLAGNEXT 0x80000001 X#define ARG_SUBRNEXT 0x80000002 X#define ARG_LISTNEXT 0x80000003 X X/* varargs tricks */ X#define ARG_FLAG(ptr) ARG_FLAGNEXT, (ptr) /* for SIMPFLAG */ X#define ARG_SUBR(ptr) ARG_SUBRNEXT, (ptr) /* for SUBRFLAG */ X#define ARG_SUBLIST(ptr) ARG_LISTNEXT, (ptr) /* for SUBLISTFLAG */ X X/* error codes: BADCALL is a programmer error, the others are user errors */ X#define ARG_BADCALL -1 /* arg_parse call itself is bad */ X#define ARG_BADARG -2 /* bad argument given */ X#define ARG_MISSING -3 /* argument or parameter missing */ X#define ARG_EXTRA -4 /* extra argument given */ X X#define ARG_NARGMAX 10000 /* max number of allowed args */ X Xextern int arg_debug, arg_doccol; Xextern int arg_warning; /* print warnings about repeated flags? */ XArg_form *arg_to_form1(), *arg_find_flag(), *arg_find_reg(); X X#ifdef __cplusplus X extern "C" { X int arg_parse(int ac, char **av ...); X int arg_parse_argv(int ac, char **av, Arg_form *form); X int arg_parse_stream(FILE *fp, Arg_form *form); X Arg_form *arg_to_form(...); X int arg_form_print(Arg_form *form); X } X#else X Arg_form *arg_to_form(); X#endif X X#endif END_OF_FILE if test 2790 -ne `wc -c <'libarg/arg.h'`; then echo shar: \"'libarg/arg.h'\" unpacked with wrong size! fi # end of 'libarg/arg.h' fi if test -f 'libarg/arg_parse.3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/arg_parse.3'\" else echo shar: Extracting \"'libarg/arg_parse.3'\" \(21195 characters\) sed "s/^X//" >'libarg/arg_parse.3' <<'END_OF_FILE' X.\" arg_parse.3: to format, run through tbl and troff -man X.\" $Header: /gourd/usr2/ph/sys/libsys/RCS/arg_parse.3,v 4.2 94/08/03 21:20:58 ph Exp Locker: ph $ X.\" a few macros X.de Cs \" code start X.DS X.ps 9 X.vs 11p X.ft C X.ta 9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n,+9n X.. X.de Ce \" code end X.ft R X.ps 10 X.vs 12p X.DE X.fi X.. X.de DS X.nf X.in +4n X.sp .5v X.. X.de DE X.sp .5v X.in -4n X.fi X.. X.TH ARG_PARSE 3 "23 April 1988" X.po 1i X.SH NAME Xarg_parse \- parse arguments to a command X.SH SYNOPSIS X.nf X#include X\fBarg_parse\fP(argc, argv, [formatstr, paramptrs, docstr, docparams]*, 0) Xint argc; Xchar **argv, *formatstr, *docstr; X Xdouble \fBexpr_eval\fP(str) Xchar *str; X.fi X.SH DESCRIPTION X\fIarg_parse\fP is a subroutine for parsing Xand conversion of command-line arguments. XThis parser is an alternative to the common method of Xargument parsing, which is an ad-hoc parser in each program, typically written Xwith a large, cumbersome switch statement. X\fIarg_parse\fP allows a command-line parser to be described very Xconcisely while retaining the flexibility to handle Xa variety of syntaxes. X.PP XThe parser has a number of features: X.DS X\(bu arbitrary order of flag arguments X\(bu automatic argument conversion and type checking X\(bu multiple-character flag names X\(bu required, optional, and flag arguments X\(bu automatic usage message X\(bu subroutine call for exotic options (variable number of parameters) X\(bu modularized parsers encourage standardized options X\(bu expression evaluation X\(bu works either from argv or in interactive mode, \ Xas a primitive language parser and interpreter X\(bu concise specification X\(bu easy to use X.DE XIt is hoped that use of \fIarg_parse\fP will help standardize argument Xconventions and reduce the tedium of adding options to programs. X.SH APPETIZER XHere is a simple example: X X.Cs X#include X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char *file; X int level = 3, debug; X double xsize = 20., ysize = 10.; X X arg_parse(argc, argv, X "", "Usage: prog [options]", X "%S", &file, "set output file", X "[%d]", &level, "set recursion level [default=%d]", level, X "-size %F %F", &xsize, &ysize, "set x and y sizes", X "-debug", ARG_FLAG(&debug), "turn on debugging", X 0); X.Ce X XThe \fIarg_parse\fP call defines the program's arguments, Xin this case: Xone required argument (a filename), an optional argument X(an integer level number), Xan optional flag with two parameters (floating point size), Xand a simple flag (boolean debug flag). XIf the above program (call it \fIprog\fP) were run with X.Cs Xprog joe.c X.Ce Xit would set \fIfile\fP to joe.c, and set \fIdebug\fP to 0, Xand if run with X.Cs Xprog -size 100 400/3 joe.c -debug 5 X.Ce Xit would set \fIfile\fP="joe.c", \fIlevel\fP=5, \fIxsize\fP=100, X\fIysize\fP=133.33, and \fIdebug\fP=1. XIn all programs using \fIarg_parse\fP, Xa hyphen arguments elicits a usage message, Xso the command X.Cs Xprog - X.Ce Xresults in the printout X.Cs XUsage: prog [options] X%S set output file X[%d] set recursion level [default=3] X-size %F %F set x and y sizes X-debug turn on debugging X.Ce X.SH TERMINOLOGY XIn order to speak precisely about the description and use of argument Xparsers, it helps to define some terminology. X X.TS Xcenter,box; Xlt lt lw(2.5i). XTERM EXAMPLES MEANING X= X\fBargument\fP -size T{ XAny of the strings in argv, supplied by the user. XT} X joe.c X_ X\fBflag arg\fP -size T{ XThe name of an option. XT} X_ X\fBparameter arg\fP 100 T{ XA value (numerical or otherwise) for an option. XT} X_ X\fBsimple flag\fP -debug T{ XA flag with no parameters that sets a boolean variable. XT} X_ X\fBregular arg\fP joe.c T{ XAn argument that is not a flag or a parameter to a flag. XCan be either a required or optional argument. XT} X= X\fBformat string\fP "-size %F%F" T{ XThe character string describing the syntax of an option. XT} X_ X\fBparameter ptr\fP &xsize T{ XPointer to a parameter variable through which converted values are stored. XT} X_ X\fBdoc string\fP "set output file" T{ XDocumentation string describing the option's effect. XT} X_ X\fBform\fP "-res%d", &r, "set res" T{ XFormat string, parameter pointers, and documentation describing Xan option. XT} X "[%d]", &level, "set level" X.TE X XWe will describe the syntax of formlists first, Xthen the method for matching arguments to forms. X.SH FORMLIST SYNTAX XThe syntax and conversion rules for parsing are specified in Xthe \fBformlist\fP following \fIargc\fP and \fIargv\fP in the X\fIarg_parse\fP call. X\fIarg_parse\fP reads its subroutine parameters using Xthe \fIvarargs(3)\fP convention for run-time procedure calls, Xso it is crucial that the formlist be terminated with a 0. XEach form consists of a \fIscanf\fP-style format string, Xa list of parameter pointers, a documentation string, and a list of Xdocumentation parameters. XIn some cases the paramptr and docparam lists will be empty, Xbut the format string and doc string arguments are mandatory. X.PP X.B Format String X.PP XThe format string consists of a flag string Xfollowed by parameter conversion codes (if any). XA flag is a hyphen followed by a string. XNone of the characters in the string may be a '%' Xand the string must not begin with a numeral. XAcceptable conversion codes in the format string are a '%' followed Xby any single character codes accepted by \fIscanf\fP plus the new Xconversion 'S': X.DS X.TS Xl l. XCODE TYPE X%c char X%d int X%f float X%F double X%s char array X%S char * X\&... (see \fIscanf(3)\fP for a complete list) X.TE X.DE XThe %S conversion is like %s except it copies only a pointer to a string X(a \fCchar *\fP), not a whole string. XWhen using %s, space must be allocated for the copied string, Xbut with %S only room for a pointer is needed. XAn example of %S use is given later. XA format string with no flag but only conversion codes describes Xa \fBregular argument\fP, Xwhile a flag followed by conversion codes defines a X\fBflag with arguments\fP. XBrackets around conversion codes indicate that they are optional, Xfor example: X.DS X.TS Xl l. X"%S %d" two required args X"%d [%F]" first arg required, second arg optional X"-pt [%F%F%F[%F]]" a flag with 0, 3, or 4 parameters X.TE X.DE XSince assignments of args to parameter pointers are done left-right Xwithin the form, no conversion codes can follow the first ']'. XIn fact, the ]'s are optional since they can be inferred to Xbe at the end of the format string. XSpaces between conversion codes are optional and ignored. X.PP XFollowing the format string is the list of parameter pointers, Xwhose number must match the number of conversion codes in Xthe format string, like the arguments to \fIscanf\fP or X\fIprintf\fP. X.PP X.B Form Types X.PP XThere are six form types. XIn addition to the ones we've seen, regular arguments and Xflags with parameters, there are several others for more exotic circumstances: Xsimple flags, nop forms, subroutine flags, and sublists. X.PP XA \fBsimple flag\fP is a flag option with no parameters that sets a Xboolean variable to 1 if that flag appears in \fIargv\fP, else 0. XA pointer to the boolean (int) variable is passed after the Xformat string using the \fCARG_FLAG\fP macro. XFor example, \fCARG_FLAG(&debug)\fP Xwill set the boolean variable \fCdebug\fP. X.PP XA \fBnop form\fP is a documentation string with no associated flags or Xarguments that appears in the usage message but does not affect parsing. XNop forms have a format string and a doc string, the former containing Xneither a flag nor a conversion code. XExample: X.Cs X"", "This program converts an AIS picture file to PF format", X.Ce XWhen the usage message is printed, Xthe doc string is indented if the format string is non-null. X.PP XA \fBsubroutine flag\fP is an option that calls a user-supplied X\fIaction subroutine\fP every time it is used Xrather than using \fIarg_parse\fP's Xformat conversion and parameter assignment. XSubroutine flags are used just like flags with parameters Xin \fIargv\fP, but they are specified and implemented differently internally. XFor example, say our program \fIprog\fP needs a variable length Xlist of people. XWe could add a flag with arguments to handle a few names using the form: X.Cs Xchar *p1, *p2, *p3, *p4; X\&... X"-people %S[%S[%S[%S]]]]", &p1, &p2, &p3, &p4, "people names" X.Ce Xbut this limits the number of possible parameters to four. XSubroutine flags provide a trapdoor whereby the programmer can do Xcustom conversion or processing of parameters with arbitrary type and number. XTo parse our list of people with a subroutine flag instead, Xwe use the form: X.Cs X"-people", ARG_SUBR(arg_people), "people names" X.Ce Xwhere \fCarg_people\fP is a subroutine to gobble the parameters, Xjust like in the example near the end of this document. X.PP XThe macro \fCARG_SUBR\fP takes the name of a subroutine to call Xwhen the flag is encountered. XThe parameter arguments following the flag in \fIargv\fP are Xpackaged into a new argument vector \fIav\fP along with \fIac\fP, Xand the subroutine is called with these two arguments. XIn our list-of-people example, the command X\fCprog foo -people ned alvy bruce -debug\fP would call \fCarg_people\fP Xwith \fIac\fP=3 and \fIav\fP={"ned","alvy","bruce"}. X.PP XWhereas flags with arguments had the simple side effect of setting Xa variable, subroutine flags can have arbitrarily complex Xside effects, and can be used multiple times. XSubroutine flags can also be flagless; Xthat is, they can have null format strings. XIn this case, any ``leftover'' regular arguments are passed to the Xsupplied action subroutine. XFlagless subroutines are useful for reading lists of filenames. X.PP XThe final form type is a \fBsublist\fP. XA sublist is a subordinate parser defined as another formlist. XSublists can be used to build a tree of parsers, Xfor example a 3-D graphics program might have a standard set of commands Xfor controlling the display (setting the output device, screen window, Xand colors) and also a standard set of commands for transforming 3-D objects X(rotation, scaling, etc.). XWithin the display command parser there could well be a standard set of Xcommands for each output device (one for Suns, another for Versatec plotters, Xetc.). XUsing sublists we can prepare a standard parser for display commands Xand keep it in the source for the display library, Xa parser for the transformation commands in the transformation library, Xand so on, so that the parser for each graphics application Xcan be very simple, merely listing its own options and then Xinvoking the standard parsers for the major libraries it uses to Xhandle the bulk of the options. XModularizing parsers in this way reduces the redundancy of parsing Xcode between similar commands and encourages standardization of options Xbetween programs, reducing maintenance work for programmers Xand reducing option confusion among users. X.PP XTo invoke a sublist we use the form: X.Cs X"-display", ARG_SUBLIST(form), "display commands" X.Ce XThe \fCARG_SUBLIST\fP macro expects a structure pointer of type X\fCArg_form *\fP as returned from the \fCarg_to_form\fP routine. XIts use is illustrated in an example later. X.SH MATCHING ARGUMENTS TO FORMS X\fIarg_parse\fP steps through the arguments in \fIargv\fP from left Xto right, matching arguments against the format strings in the formlist. XFlag arguments (simple flags or flags with parameters) Xcan occur in arbitrary order but regular arguments are matched by Xstepping through the formlist in left to right order. XFor this reason regular arguments are also known as positional arguments. XMatching of parameters within an option is also done in a left-to-right, Xgreedy fashion within the form without regard for the parameter types. XNo permutation of the matching is done to avoid conversion errors. XTo illustrate, in our \fIprog\fP above, if we changed the size option Xto make the second parameter optional: X.Cs X"-size %F[%F]", &xsize, &ysize, "set sizes", X.Ce Xthen the command: X.Cs Xprog -size 100 -debug joe.c X.Ce Xsucceeds because it is clear that only one parameter is being supplied to size, Xbut if we try: X.Cs Xprog -size 100 joe.c -debug X.Ce Xthen \fIarg_parse\fP will attempt to convert \fC"joe.c"\fP via \fC%F\fP into X\fIysize\fP and fail, returning an error code. X.PP XThe matching algorithm for subroutine flags and sublists varies somewhat Xfrom that for the other form types. XFor most types, X\fIarg_parse\fP grabs as many arguments out of \fIargv\fP as the form can Xtake up to the next flag argument (or the end of \fIargv\fP), Xbut for subroutine flags and sublists, Xall arguments up to the next flag argument Xare grabbed and bundled into a smaller argument vector (call it \fIav\fP). X(For matching purposes, a flag argument is an argument that begins with Xa hyphen followed by any character except digits and '.'.) XThe new argument vector is passed to the action routine in the case of Xsubroutine flags or recursively to a sub-parser in the case of sublist flags. X.PP XThe sub-parser invoked by a sublist flag does matching identically. XNormally the entire formlist tree is traversed depth-first whenever a search Xfor a flag is being made. XIf there are no flag duplicates between different levels of the form tree Xthen the structure of the tree is irrelevant; Xthe user needn't be conscious of the command grouping or of Xthe sublist names. XBut if there are name duplicates, for example if there were a \fC-window\fP Xoption in both the display and transformation parsers, Xthen explicit control of search order within the tree is needed. XThis disambiguation problem is analogous to pathname specification Xof files within a UNIX directory tree. XWhen explicit sublist selection is needed it is done using the sublist Xflag followed by the arguments for the sub-parser, bracketed with X\fC-{\fP and \fC-}\fP flags. XFor example, if there were more than one \fCwindow\fP option, Xto explicitly select the one in the display parser, Xwe type: X.Cs X-display -{ -window 0 0 639 479 -} X.Ce XThe brace flags group and quote the arguments so that all of Xthe enclosed arguments will be passed to the sub-parser. XWithout them the argument matcher would think that \fCdisplay\fP has no Xparameters, since it is immediately followed by a flag (\fC-window\fP). XNote that in \fIcsh\fP, the braces must be escaped as X\fC-\e{\fP and \fC-\e}\fP. X.PP X[If you can think of a better way to do matching please tell me! -Paul]. X.PP XThe matching is checked in both directions: Xin the formlist, all required arguments must be assigned to and Xmost flags can be called at most once, Xand in \fIargv\fP, each argument must be recognized. XRegular arguments are \fBrequired\fP if they are unbracketed, Xand \fBoptional\fP if they are bracketed. XUnmatched forms for required arguments Xcause an error but unmatched forms for optional Xor flag arguments do not; they are skipped. XA warning message is printed if a simple flag or flag with parameters Xappears more than once in \fIargv\fP. XNote that it is not an error for subroutine flags to appear more than once, Xso they should be used when repeats of a flag are allowed. XUnmatched arguments in \fIargv\fP cause an ``extra argument'' error. X.PP XA hyphen argument in \fIargv\fP causes \fIarg_parse\fP to print a Xusage message constructed from the format and documentation strings, Xand return an error code. X.SH EXPRESSIONS X\fIarg_parse\fP does expression evaluation when converting numerical parameters. XThe expression evaluator allows the following operations: X+, -, *, /, % (mod), ^ (exponentiation), Xunary -, unary +, X\fIsqrt\fP, X\fIexp\fP, X\fIlog\fP, X\fIpow\fP, X\fIsin\fP, X\fIcos\fP, X\fItan\fP, X\fIasin\fP, X\fIacos\fP, X\fIatan\fP, X\fIatan2\fP (takes 2 args), X\fIsind\fP, X\fIcosd\fP, X\fItand\fP, X\fIdasin\fP, X\fIdacos\fP, X\fIdatan\fP, X\fIdatan2\fP (takes 2 args), X\fIfloor\fP, Xand X\fIceil\fP. XIt also knows the two constants X\fIpi\fP and X\fIe\fP. XNumerical constants can be integer or scientific notation, Xin decimal, octal, hexidecimal, or other base. XFor example, 10 = 012 (base 8) = 0xa (base 16) = 0b2:1010 (base 2). XThe normal trig functions work in radians, while the versions that begin Xor end in the letter 'd' work in degrees. XThus, \fC"exp(-.5*2^2)/sqrt(2*pi)"\fP is a legal expression. XAll expressions are computed in double-precision floating point. XNote that it is often necessary to quote expressions so the shell Xwon't get excited about asterisks and parentheses. XThe expression evaluator \fIexpr_eval\fP Xcan be used independently of \fIarg_parse\fP. X.SH INTERACTIVE MODE XIf the lone argument \fC-stdin\fP is passed in \fIargv\fP then X\fIarg_parse\fP goes into interactive mode. XInteractive mode reads its arguments from standard input rather than Xgetting them from the argument vector. XThis allows programs to be run semi-interactively. XTo encourage interactive use of a program, one or more of the options Xshould be a subroutine flag. XOne could have a \fC-go\fP flag, say, that causes computation to commence. XIn interactive mode the hyphens on flags are optional at the beginning Xof each line, so the input syntax resembles a programming language. XIn fact, scripts of such commands are often saved in files. X.SH EXAMPLE XThe following example illustrates most of the features of \fIarg_parse\fP. X.Cs X/* tb.c - arg_parse test program */ X#include Xdouble atof(); X X#include Xstatic double dxs = 1., dys = .75; Xstatic int x1 = 0, y1 = 0, x2 = 99, y2 = 99; Xstatic char *chanlist = "rgba"; Xint arg_people(), arg_dsize(); XArg_form *fb_init(); X Xmain(ac, av) Xint ac; Xchar **av; X{ X int fast, xs = 512, ys = 486; X double scale = 1.; X char *fromfile, tofile[80], *child = "jim"; X Arg_form *arg_fb; X X arg_fb = fb_init(); X if (arg_parse(ac, av, X "", "Usage: %s [options]", av[0], X "", "This program does nothing but test arg_parse", X "%S %s", &fromfile, tofile, "fromfile and tofile", X "[%F]", &scale, "set scale [default=%g]", scale, X "", ARG_SUBR(arg_people), "names of people", X "-fast", ARG_FLAG(&fast), "do it faster", X "-ch %S", &child, "set child name", X "-srcsize %d[%d]", &xs, &ys, "set source size [default=%d,%d]", xs, ys, X "-dstsize", ARG_SUBR(arg_dsize), "set dest size", X "-fb", ARG_SUBLIST(arg_fb), "FB COMMANDS", X 0) < 0) X exit(1); X X printf("from=%s to=%s scale=%g fast=%d child=%s src=%dx%d dst=%gx%g\en", X fromfile, tofile, scale, fast, child, xs, ys, dxs, dys); X printf("window={%d,%d,%d,%d} chan=%s\en", x1, y1, x2, y2, chanlist); X} X Xstatic arg_people(ac, av) Xint ac; Xchar **av; X{ X int i; X X for (i=0; i3) { X fprintf(stderr, "-dsize wants 1 or 2 args\en"); X exit(1); X } X /* illustrate two methods for argument conversion */ X dxs = atof(av[0]); /* constant conversion */ X if (ac>1) dys = expr_eval(av[1]); /* expression conversion */ X else dys = .75*dxs; X} X XArg_form *fb_init() X{ X return arg_to_form( X "-w%d%d%d%d", &x1, &y1, &x2, &y2, "set screen window", X "-ch%S", &chanlist, "set channels [default=%s]", chanlist, X 0); X} X.Ce XIn this example we have two required arguments, one optional argument, Xand a flagless subroutine (arg_people) to gobble the remaining regular Xarguments. XThe two required arguments illustrate the differences between \fC%S\fP Xand \fC%s\fP, and the advantages of the former. XThe \fC-srcsize\fP and \fC-dstsize\fP forms illustrate two different Xways to get a flag with either one or two parameters. XNote in the \fIarg_dsize\fP routine Xthat the expression evaluator \fIexpr_eval\fP is just Xas easy to use as \fIatof\fP. XA small sublist shows an example of command name ambiguity in Xthe flag \fC-ch\fP. X.PP XBelow are the results of several sample runs. X.Cs X\(bu tb one two X from=one to=two scale=1 fast=0 child=jim src=512x486 dst=1x0.75 X window={0,0,99,99} chan=rgba X.fi X\fIOnly the two required args are specified here and everything Xelse defaults.\fP X.nf X X\(bu tb -fast -srcsize 100 1+2 one two -dstsize 2 -ch amy -w 1 2 3 4 "sqrt(2)" X from=one to=two scale=1.41421 fast=1 child=amy src=100x3 dst=2x1.5 X window={1,2,3,4} chan=rgba X.fi X\fIThis illustrates expression evaluation, the precedence of the first\fP X-ch \fIflag over the one in the sublist, and easy access to a non-ambiguous Xsublist option, \fP-w. X.nf X X\(bu tb -fb -\e{ -ch abc -w 9 8 7 6 -\e} -ch -\e{ -jo -\e} A B 44 larry curly moe X person[0]=larry X person[1]=curly X person[2]=moe X from=A to=B scale=44 fast=0 child=-jo src=512x486 dst=1x0.75 X window={9,8,7,6} chan=abc X.fi X\fIThis shows access to a ``shadowed'' sublist option, \fP-ch\fI, and Xescaping a parameter string that happens to begin with a hyphen, \fP-jo\fI, Xwith braces, plus the use of a flagless subroutine to pick up extra Xregular arguments.\fP X.nf X.Ce X.SH RETURN VALUE X\fIarg_parse\fP returns a negative code on error, otherwise 0. XThe file \fIarg.h\fP contains definitions for the error codes: X.DS X.TS Xl l. XARG_BADCALL programmer error, bad formlist XARG_BADARG bad argument in \fIargv\fP XARG_MISSING required argument or parameter to flag missing XARG_EXTRA \fIargv\fP contains an extra, unrecognizable argument X.TE X.DE X.SH NOTE X\fIarg_parse\fP modifies \fIargv\fP as a side-effect to eliminate Xthe \fC-{\fP and \fC-}\fP arguments. X.SH COMPILING XIf \fIarg_parse\fP is installed in \fIlibarg.a\fP, Xcompile with \fCcc ... -larg -lm\fP. X.SH SEE ALSO Xscanf(3), varargs(3) X.SH AUTHOR XPaul Heckbert, ph@cs.cmu.edu, April 1988 END_OF_FILE if test 21195 -ne `wc -c <'libarg/arg_parse.3'`; then echo shar: \"'libarg/arg_parse.3'\" unpacked with wrong size! fi # end of 'libarg/arg_parse.3' fi if test -f 'libarg/expr.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/expr.c'\" else echo shar: Extracting \"'libarg/expr.c'\" \(6817 characters\) sed "s/^X//" >'libarg/expr.c' <<'END_OF_FILE' X/* X * expr_eval: expression evaluator - converts ascii string to floating point X * Works by top-down predictive parsing. X * Most of the routines gobble characters and advance global string pointer s. X * Sets global expr_err if an error occurs. X * X * supports: parentheses, % for mod, ^ for pow, elementary functions, X * constants pi and e, variable base constants X * X * Paul Heckbert 18 April 1988 X */ X Xstatic char rcsid[] = "$Header: /gourd/usr2/ph/sys/libsys/RCS/expr.c,v 4.2 94/08/03 15:41:17 ph Exp Locker: ph $"; X X#include X#include X X#include "simple.h" X#include "expr.h" X#define space() for (; isspace(*s); s++) X Xdouble expr_eval(), expr(), term(), factor(), signednumber(), number(), X paren(), posconst(), expt(); Xstatic char *s0, *s; Xint expr_error; X X#ifdef MAIN Xmain(ac, av) Xint ac; Xchar **av; X{ X double x; X X if (ac!=2) exit(1); X x = expr_eval(av[1]); X printf(">> %g\n", x); X} X#endif X Xint expr_eval_int(str) Xchar *str; X{ X double x; X X x = expr_eval(str); X /* do unsigned double to signed int conversion: */ X return x>MAXINT ? x+2.*MININT : x; X} X Xlong expr_eval_long(str) Xchar *str; X{ X double x; X X x = expr_eval(str); X /* do unsigned double to signed long conversion: */ X return x>MAXLONG ? x+2.*MINLONG : x; X} X Xdouble expr_eval(str) Xchar *str; X{ X double x; X X s0 = s = str; X expr_error = EXPR_GOOD; X x = expr(); X if (*s) { X error(s, 1, "garbage in expression"); X expr_error = s==s0 ? EXPR_BAD : EXPR_SOSO; X } X return x; X} X Xstatic double expr() X{ X double x; X X for (x=term();;) { X space(); X switch (*s) { X case '+': s++; x += term(); break; X case '-': s++; x -= term(); break; X default: return x; X } X } X} X Xstatic double term() X{ X double x, y; X X for (x=factor();;) { X space(); X switch (*s) { X case '*': s++; x *= factor(); break; X case '/': s++; x /= factor(); break; X case '%': s++; y = factor(); x = x-floor(x/y)*y; break; X default: return x; X } X } X} X Xstatic double factor() X{ X double x; X X for (x=signednumber();;) { X space(); X switch (*s) { X case '^': s++; return pow(x, factor()); /* right-associative */ X default: return x; X } X } X} X Xstatic double signednumber() X{ X space(); X switch (*s) { X case '-': s++; return -signednumber(); X case '+': s++; return signednumber(); X default: return number(); X } X} X Xstatic double number() X{ X char *func; X int n; X double x, y; X X space(); X if (isdigit(*s) || *s=='.') return posconst(); X if (*s=='(') return paren(); X X if (isalpha(*s)) { X func = s; X for (s++; isalpha(*s) || isdigit(*s); s++); X n = s-func; /* length of funcname */ X X if (eq(n, func, "pi")) return M_PI; X if (eq(n, func, "e")) return exp(1.); X X if (eq(n, func, "sqrt")) return sqrt(paren()); X if (eq(n, func, "exp")) return exp(paren()); X if (eq(n, func, "log")) return log(paren()); X if (eq(n, func, "pow")) {paren2(&x, &y); return pow(x, y);} X X if (eq(n, func, "sin")) return sin(paren()); X if (eq(n, func, "cos")) return cos(paren()); X if (eq(n, func, "tan")) return tan(paren()); X if (eq(n, func, "asin")) return asin(paren()); X if (eq(n, func, "acos")) return acos(paren()); X if (eq(n, func, "atan")) return atan(paren()); X if (eq(n, func, "atan2")) {paren2(&x, &y); return atan2(x, y);} X X if (eq(n, func, "sind")) return sin(DEG_TO_RAD(paren())); X if (eq(n, func, "cosd")) return cos(DEG_TO_RAD(paren())); X if (eq(n, func, "tand")) return tan(DEG_TO_RAD(paren())); X if (eq(n, func, "dasin")) return RAD_TO_DEG(asin(paren())); X if (eq(n, func, "dacos")) return RAD_TO_DEG(acos(paren())); X if (eq(n, func, "datan")) return RAD_TO_DEG(atan(paren())); X if (eq(n, func, "datan2")) {paren2(&x, &y); X return RAD_TO_DEG(atan2(x, y));} X X if (eq(n, func, "floor")) return floor(paren()); X if (eq(n, func, "ceil")) return ceil(paren()); X X error(func, n, "bad numerical expression"); X return 0.; X } X X error(s, 1, "syntax error"); X return 0.; X} X X/* paren: '(' expr ')' */ X Xstatic double paren() X{ X double x; X X space(); X if (*s!='(') error(s, 1, "expected '('"); X s++; X x = expr(); X space(); X if (*s!=')') error(s, 1, "expected ')'"); X s++; X return x; X} X X/* paren2: '(' expr ',' expr ')' */ X Xstatic paren2(x, y) Xdouble *x, *y; X{ X space(); X if (*s!='(') error(s, 1, "expected '('"); X s++; X *x = expr(); X space(); X if (*s!=',') error(s, 1, "expected ','"); X s++; X *y = expr(); X space(); X if (*s!=')') error(s, 1, "expected ')'"); X s++; X} X X/* X * posconst: given a string beginning at s, return floating point value. X * like atof but it uses and modifies the global ptr s X */ X Xstatic double posconst() X{ X int base, exp, pos, d; X double x, y; X X space(); X if (*s=='0') { /* change base: 10 = 012 = 0xa = 0b2:1010 */ X s++; X switch (*s) { X case 'b': X s++; X for (base=0; isdigit(*s); s++) X base = base*10+*s-'0'; /* base is in base 10! */ X if (*s!=':') error(s, 1, "expecting ':'"); X s++; X break; X case 'x': s++; base = 16; break; X case 't': s++; base = 10; break; X case '.': base = 10; break; /* a float, e.g.: 0.123 */ X default: base = 8; break; X } X } X else base = 10; X X x = 0.; X for (; d = digit(*s), d>=0 && d=0 && d='a'&&c<='z' ? c-'a'+10 : c>='A'&&c<='Z' ? c-'A'+10 : -1; X} X X/* expt: a^n for n>=0 */ X Xstatic double expt(a, n) Xint a, n; X{ X double t, x; X X if (n<0) { X fprintf(stderr, "expt: can't do negative exponents\n"); X return 1.; X } X if (n==0) return 1.; X for (t=a, x=1.; n>0; n>>=1) { X if (n&1) x *= t; X t *= t; X } X return x; X} X X/* eq: test equality of string a, of length n, with null-terminated string b */ X Xstatic eq(n, a, b) Xint n; Xchar *a, *b; X{ X char c; X int ret; X X c = a[n]; X a[n] = 0; X ret = str_eq(a, b); X a[n] = c; X return ret; X} X Xstatic error(s, len, err) Xchar *s, *err; Xint len; X{ X if (*s==0) s[len] = 0; /* just in case */ X printf("expr: %s: ", err); X prints(s-s0, s0); X printf("["); X prints(len, s); X printf("]"); X prints(s+strlen(s)-s0-len, s+len); X printf("\n"); X if (expr_error!=EXPR_BAD) X expr_error = s==s0 ? EXPR_BAD : EXPR_SOSO; X} X X/* prints: print string s of length n */ X Xstatic prints(n, s) Xint n; Xchar *s; X{ X char c; X X c = s[n]; X s[n] = 0; X printf("%s", s); X s[n] = c; X} END_OF_FILE if test 6817 -ne `wc -c <'libarg/expr.c'`; then echo shar: \"'libarg/expr.c'\" unpacked with wrong size! fi # end of 'libarg/expr.c' fi if test -f 'libarg/expr.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/expr.h'\" else echo shar: Extracting \"'libarg/expr.h'\" \(652 characters\) sed "s/^X//" >'libarg/expr.h' <<'END_OF_FILE' X/* expr.h: definitions for expression evaluator */ X X#ifndef EXPR_HDR X#define EXPR_HDR X X/* $Header: /gourd/usr2/ph/sys/libsys/RCS/expr.h,v 4.2 94/08/03 15:41:19 ph Exp Locker: ph $ */ X X/* error codes */ X#define EXPR_GOOD 0 /* expression totally good */ X#define EXPR_SOSO -1 /* expression partially good */ X#define EXPR_BAD -2 /* expression totally bad */ X Xextern int expr_error; /* holds error code after expr_eval */ X X#ifdef __cplusplus X extern "C" { X int expr_eval_int(char *str); X long expr_eval_long(char *str); X double expr_eval(char *str); X } X#else X int expr_eval_int(); X long expr_eval_long(); X double expr_eval(); X#endif X X#endif END_OF_FILE if test 652 -ne `wc -c <'libarg/expr.h'`; then echo shar: \"'libarg/expr.h'\" unpacked with wrong size! fi # end of 'libarg/expr.h' fi if test -f 'libarg/simple.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/simple.h'\" else echo shar: Extracting \"'libarg/simple.h'\" \(1343 characters\) sed "s/^X//" >'libarg/simple.h' <<'END_OF_FILE' X/* simple.h: definitions of some simple, common constants and macros */ X X#ifndef SIMPLE_HDR X#define SIMPLE_HDR X X/* $Header: /gourd/usr2/ph/sys/libsys/RCS/simple.h,v 4.2 94/08/03 15:41:23 ph Exp Locker: ph $ */ X X#include X#include X X/* better than standard assert.h: doesn't gag on 'if (p) assert(q); else r;' */ X#define assert(p) if (!(p)) \ X { \ X fprintf(stderr, "Assertion failed: %s line %d\n", __FILE__, __LINE__); \ X exit(1); \ X } \ X else X X#define str_eq(a, b) (strcmp(a, b) == 0) X#define MIN(a, b) ((a)<(b) ? (a) : (b)) X#define MAX(a, b) ((a)>(b) ? (a) : (b)) X#define ABS(a) ((a)>=0 ? (a) : -(a)) X#define SWAP(a, b, t) {t = a; a = b; b = t;} X#define LERP(t, a, b) ((a)+(t)*((b)-(a))) X#define ALLOC(ptr, type, n) assert(ptr = (type *)malloc((n)*sizeof(type))) X#define ALLOC_ZERO(ptr, type, n) assert(ptr = (type *)calloc(n, sizeof(type))) X X#define RAD_TO_DEG(x) ((x)*(180./M_PI)) X#define DEG_TO_RAD(x) ((x)*(M_PI/180.)) X X/* note: the following are machine dependent! (ifdef them if possible) */ X#define MINSHORT -32768 X#define MINLONG -2147483648 X#define MININT MINLONG X#ifndef MAXINT /* sgi has these in values.h */ X# define MAXSHORT 32767 X# define MAXLONG 2147483647 X# define MAXINT MAXLONG X#endif X X X#ifdef hpux /* hp's unix doesn't have bzero */ X# define bzero(a, n) memset(a, 0, n) X#endif X X#endif END_OF_FILE if test 1343 -ne `wc -c <'libarg/simple.h'`; then echo shar: \"'libarg/simple.h'\" unpacked with wrong size! fi # end of 'libarg/simple.h' fi if test -f 'libarg/tb.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libarg/tb.c'\" else echo shar: Extracting \"'libarg/tb.c'\" \(1940 characters\) sed "s/^X//" >'libarg/tb.c' <<'END_OF_FILE' X/* tb.c - arg_parse test program */ X Xstatic char rcsid[] = "$Header: /gourd/usr2/ph/sys/libsys/libarg/RCS/tb.c,v 4.2 94/08/03 20:09:48 ph Exp Locker: ph $"; X X#include Xdouble atof(); X X#include "arg.h" Xstatic double dxs = 1., dys = .75; Xstatic int x1 = 0, y1 = 0, x2 = 99, y2 = 99; Xstatic char *chanlist = "rgba"; Xint arg_people(), arg_dsize(); XArg_form *fb_init(); X Xmain(ac, av) Xint ac; Xchar **av; X{ X int fast, xs = 512, ys = 486; X double scale = 1.; X char *fromfile, tofile[80], *child = "jim"; X Arg_form *arg_fb; X X arg_fb = fb_init(); X if (arg_parse(ac, av, X "", "Usage: %s [options]", av[0], X "", "This program does nothing but test arg_parse", X "%S %s", &fromfile, tofile, "fromfile and tofile", X "[%F]", &scale, "set scale [default=%g]", scale, X "", ARG_SUBR(arg_people), "names of people", X "-fast", ARG_FLAG(&fast), "do it faster", X "-ch %S", &child, "set child name", X "-srcsize %d[%d]", &xs, &ys, "set source size [default=%d,%d]", xs, ys, X "-dstsize", ARG_SUBR(arg_dsize), "set dest size", X "-fb", ARG_SUBLIST(arg_fb), "FB COMMANDS", X 0) < 0) X exit(1); X X printf("from=%s to=%s scale=%g fast=%d child=%s src=%dx%d dst=%gx%g\n", X fromfile, tofile, scale, fast, child, xs, ys, dxs, dys); X printf("window={%d,%d,%d,%d} chan=%s\n", x1, y1, x2, y2, chanlist); X} X Xstatic arg_people(ac, av) Xint ac; Xchar **av; X{ X int i; X X for (i=0; i3) { X fprintf(stderr, "-dsize wants 1 or 2 args\n"); X exit(1); X } X /* illustrate two methods for argument conversion */ X dxs = atof(av[0]); /* constant conversion */ X if (ac>1) dys = expr_eval(av[1]); /* expression conversion */ X else dys = .75*dxs; X} X XArg_form *fb_init() X{ X return arg_to_form( X "-w%d%d%d%d", &x1, &y1, &x2, &y2, "set screen window", X "-ch%S", &chanlist, "set channels [default=%s]", chanlist, X 0); X} END_OF_FILE if test 1940 -ne `wc -c <'libarg/tb.c'`; then echo shar: \"'libarg/tb.c'\" unpacked with wrong size! fi # end of 'libarg/tb.c' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case...