home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume44
/
arg_parse
/
part01
next >
Wrap
Internet Message Format
|
1994-09-05
|
69KB
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...