home *** CD-ROM | disk | FTP | other *** search
- From: Paul.Heckbert@hostess.graphics.cs.cmu.edu (Paul Heckbert)
- Newsgroups: comp.sources.misc
- Subject: v44i050: arg_parse - simple, powerful argument parser, Part01/01
- Date: 5 Sep 1994 11:42:27 -0500
- Organization: Sterling Software
- Sender: kent@sparky.sterling.com
- Approved: kent@sparky.sterling.com
- Message-ID: <34fhpj$2kp@sparky.sterling.com>
- X-Md4-Signature: e6610ffa95163eabbcc84e048770f37f
-
- 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 <arg.h>
-
- 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 <varargs.h>
- X#include <ctype.h>
- X#include <string.h>
- 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<<np;
- X if (!*s || *s==']') break;
- X if (*s!='%' || !s[1]) {
- X fprintf(stderr, "arg: bad format string (%s)\n", f->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<<np;
- X if (np>=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<ACMAX;) { /* split line into tokens */
- X av[ac++] = p; /* save ptr to beginning of token */
- X do {
- X *p++ = c;
- X if (p >= 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; i<ac; i++) fprintf(stderr, "(%s) ", av[i]);
- X fprintf(stderr, "\n");
- X }
- X
- X err = arg_parse_form1(ac, av, form);
- X if (!ret) ret = err;
- X }
- X if (!ret) ret = arg_done();
- X regf = oldregf;
- X return ret;
- X}
- X
- X/*
- X * token_char: is next char in stream fp part of a token?
- X * returns TOKEN if char in token, SPACE if whitespace, END if end of input line
- X * *p gets new char.
- X * handles quoted strings and escaped characters.
- X */
- X
- Xstatic Token_type token_char(fp, p)
- XFILE *fp;
- Xchar *p;
- X{
- X int c, old_mode;
- X Token_type type;
- X static int mode = 0; /* = '"' or '\'' if inside quoted string */
- X
- X type = TOKEN;
- X do {
- X old_mode = mode;
- X c = getc(fp);
- X switch (c) {
- X case EOF:
- X type = END;
- X break;
- X case '\\':
- X switch (c = getc(fp)) {
- X case 'b': c = '\b'; break;
- X case 'f': c = '\f'; break;
- X case 'n': c = '\n'; break;
- X case 'r': c = '\r'; break;
- X case 't': c = '\t'; break;
- X case 'v': c = '\v'; break;
- X case '0': c = '\0'; break;
- X }
- X break;
- X case '"':
- X switch (mode) {
- X case 0: mode = '"'; break; /* begin " */
- X case '"': mode = 0; break; /* end " */
- X }
- X break;
- X case '\'':
- X switch (mode) {
- X case 0: mode = '\''; break; /* begin ' */
- X case '\'': mode = 0; break; /* end ' */
- X }
- X break;
- X case '\n':
- X switch (mode) {
- X case 0: type = END; break;
- X }
- X break;
- X }
- X /* loop until we read a literal character */
- X } while (old_mode != mode);
- X *p = c;
- X
- X if (type!=END && mode==0 && (c==' ' || c=='\t' || c=='\n'))
- X type = SPACE;
- X return type;
- X}
- X
- X/*
- X * arg_parse_argv: do the actual parsing!
- X * parse the arguments in av according to the formlist in form
- X * Returns 0 on success, negative on failure.
- X */
- X
- Xarg_parse_argv(ac, av, form)
- Xint ac;
- Xchar **av;
- XArg_form *form;
- X{
- X int ret;
- X Arg_form *oldregf;
- X
- X oldregf = regf;
- X regf = form;
- X arg_init(form);
- X ret = arg_parse_form1(ac, av, form);
- X if (!ret) ret = arg_done();
- X regf = oldregf;
- X return ret;
- X}
- X
- Xarg_parse_form1(ac, av, form)
- Xint ac;
- Xchar **av;
- XArg_form *form;
- X{
- X int i, di;
- X Arg_form *f;
- X
- X for (i=1; i<ac; i+=di) {
- X if (arg_debug)
- X fprintf(stderr, "arg %d: (%s)\n", i, av[i]);
- X if (av[i][0]=='-' && !NUMERIC(&av[i][1])) { /* flag argument */
- X f = arg_find_flag(av[i], form);
- X if (!f) {
- X if (av[i][1])
- X fprintf(stderr, "unrecognized arg: %s\n", av[i]);
- X else /* arg was "-"; print usage message */
- X arg_form_print(form);
- X return ARG_EXTRA;
- X }
- X di = arg_do(ac-i-1, &av[i+1], f);
- X if (di<0) return di;
- X di++;
- X }
- X else { /* regular argument */
- X f = arg_find_reg();
- X if (!f) {
- X /* regular args exhausted, see if any flagless subrflags */
- X f = arg_find_flag("", form);
- X if (!f) {
- X fprintf(stderr, "extra arg: %s\n", av[i]);
- X return ARG_EXTRA;
- X }
- X }
- X di = arg_do(ac-i, &av[i], f);
- X if (di<0) return di;
- X }
- X }
- X
- X return 0;
- X}
- X
- X/*
- X * arg_init: initialize formlist before parsing arguments
- X * Set simple flags and repeat counts to 0.
- X */
- X
- Xarg_init(form)
- XArg_form *form;
- X{
- X Arg_form *f;
- X
- X for (f=form; f; f=f->next)
- 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 = narg<f->nparam ? 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; i<narg; i++)
- X fprintf(stderr, "%s ", av[i]);
- X fprintf(stderr, "\n");
- X }
- X return skip;
- X}
- X
- X/*
- X * nargs: Count number of parameters in arg vector av before the next flag.
- X * Arguments can be grouped and "escaped" using -{ and -}.
- X * NOTE: modifies av
- X * Returns number of valid args in new av.
- X * Sets *skip to number of args to skip in old av to get to next flag.
- X *
- X * This is the most complex code in arg_parse.
- X * Is there a better way?
- X *
- X * examples:
- X * input: ac=3, av=(3 4 -go)
- X * output: av unchanged, skip=2, return 2
- X *
- X * input: ac=4, av=(-{ -ch r -})
- X * output: av=(-ch r X X), skip=4, return 2
- X *
- X * input: ac=4, av=(-{ -foo -} -go)
- X * output: av=(-foo X X -go), skip=3, return 1
- X *
- X * input: ac=6, av=(-{ -ch -{ -rgb -} -})
- X * output: av=(-ch -{ -rgb -} X X), skip=6, return 4
- X *
- X * where X stands for junk
- X */
- X
- Xstatic nargs(ac, av, f, skip)
- Xint ac, *skip;
- Xchar **av;
- XArg_form *f;
- X{
- X char *flag, **au, **av0;
- X int i, j, level, die, np, mask, voracious;
- X
- X np = f->nparam;
- 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<np && i<ac && !die; ) {
- X if (voracious) j = 999;
- X else for (j=i+1; !(mask>>j&1); j++); /* go until we can stop */
- X /* try to grab params i through j-1 */
- X for (; i<j && i<ac || level>0; 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; j<au-av0; j++)
- X fprintf(stderr, "%s ", av0[j]);
- X fprintf(stderr, "\n");
- X }
- X *skip = i;
- X return au-av0;
- X}
- X
- X/*
- X * scan: call sscanf to read args into param array and do conversion
- X * returns error code (0 on success)
- X */
- X
- Xstatic scan(narg, arg, f)
- Xint narg;
- Xchar **arg;
- XArg_form *f;
- X{
- X static char str[]="%X";
- X char *s;
- X int i, **p;
- X double x;
- X
- X if (f->nparam<narg) narg = f->nparam;
- 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; i<narg; i++, p++) {
- X str[1] = f->code[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<c1&~7; c=(c+7)&~7) putc('\t', fp);
- X for (; c<c1; c++) putc(' ', fp);
- X}
- X
- Xav_print(str, ac, av)
- Xint ac;
- Xchar *str, **av;
- X{
- X int i;
- X
- X fprintf(stderr, "%s: ", str);
- X for (i=0; i<ac; i++)
- X fprintf(stderr, "%s ", av[i]);
- X fprintf(stderr, "\n");
- X}
- END_OF_FILE
- if test 21871 -ne `wc -c <'libarg/arg.c'`; then
- echo shar: \"'libarg/arg.c'\" unpacked with wrong size!
- fi
- # end of 'libarg/arg.c'
- fi
- if test -f 'libarg/arg.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'libarg/arg.h'\"
- else
- echo shar: Extracting \"'libarg/arg.h'\" \(2790 characters\)
- sed "s/^X//" >'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 <stdio.h>
- X#include <expr.h>
- 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 <arg.h>
- 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 <arg.h>
- 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 <stdio.h>
- 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\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; i<ac; i++)
- X printf("person[%d]=%s\en", i, av[i]);
- X}
- X
- Xstatic arg_dsize(ac, av)
- Xint ac;
- Xchar **av;
- X{
- X if (ac<1 || ac>3) {
- 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 <ctype.h>
- X#include <math.h>
- 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<base; s++)
- X x = x*base+d;
- X if (*s=='.') {
- X s++;
- X for (y=1.; d = digit(*s), d>=0 && d<base; s++) {
- X x = x*base+d; /* fraction is in variable base */
- X y *= base;
- X }
- X x /= y;
- X }
- X if (*s=='e' || *s=='E') {
- X s++;
- X if (*s=='-') {s++; pos = 0;}
- X else if (*s=='+') {s++; pos = 1;}
- X else pos = 1;
- X for (exp=0; isdigit(*s); s++)
- X exp = exp*10+*s-'0'; /* exponent is in base 10 */
- X y = expt(base, exp);
- X if (pos) x *= y;
- X else x /= y;
- X }
- X return x;
- X}
- X
- Xstatic digit(c)
- Xint c;
- X{
- X return isdigit(c) ? c-'0' :
- X c>='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 <stdio.h>
- X#include <math.h>
- 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 <stdio.h>
- 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; i<ac; i++)
- X printf("person[%d]=%s\n", i, av[i]);
- X}
- X
- Xstatic arg_dsize(ac, av)
- Xint ac;
- Xchar **av;
- X{
- X if (ac<1 || ac>3) {
- 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...
-