[alt.sources] Complete parseargs

peter@ficc.uu.net (Peter da Silva) (02/25/90)

Archive-name: parseargs

This is the complete parseargs package: the three posts to comp.sources.misc
in one easy-to-find place.

--8<--- snip snip ------------------------------------------------------------
:
#! /bin/sh
# This is a shell archive, created at Ferranti International Controls Corp.
# by peter (peter da silva, +1 713 274 5180) on Sat Feb 24 20:56:43 1990
# Remove anything before the "#! /bin/sh" line, then unpack it by saving
# it into a file and typing "sh file".  If you do not have sh, you need 
# unshar, a dearchiving program which is widely available.  In the absolute
# wost case, you can crack the files out by hand.
# If the archive is complete, you will see the message "End of archive."
# at the end.
# This archive contains the following files...
# 'README'
# 'README.PDS'
# 'Makefile'
# 'funclist.h'
# 'parseargs.h'
# 'useful.h'
# 'ckalloc.c'
# 'argtype.c'
# 'arglist.c'
# 'fp_argtype.c'
# 'funclist.c'
# 'openpath.c'
# 'amiga_args.c'
# 'unix_args.c'
# 'syserr.c'
# 'traceset.c'
# 'stest.c'
# 'ckalloc.3'
# 'funclist.3'
# 'openpath.3'
# 'parseargs.3'
# 'syserr.3'
# 'trace.3'
# To extract them, run the following through /bin/sh
echo x - README
sed 's/^X//' > README << '//END_OF_FILE'
X
X			  NIFTY UTILITY LIBRARY
X
X			     Eric P. Allman
X			University of California
X			  Berkeley, California
X			    eric@Berkeley.EDU
X
X
XSUMMARY
X	This directory contains a subset of a utility library that I have
X	used (in various forms) for several years now.  This particular
X	version is rather sparse, being a relatively recent reimplementation.
X
X	I am making this available as a result of the rather surprising
X	response to my C Advisor column in UNIX Review Vol. 7 No. 11 on
X	argument parsing, in which I described an alternative to getopt.
X	Several dozen people have asked for the source code -- an amazing
X	number, considering that in the four years prior to this column,
X	I have gotten perhaps six letters in toto.
X
X	Rather than limiting this distribution to the single argument
X	parsing module, I have added several other routines, many of which
X	my more faithful readers will recognize.
X
XCOPY/REUSE POLICY
X	Permission is hereby granted to freely copy and redistribute this
X	software, provided that the author is clearly credited in all
X	copies and derivations.  Neither the name of the author nor that
X	of the University may be used to endorse or promote products
X	derived from this software without specific written permission.
X	This software is provided ``As Is'' and without any express or
X	implied warranties.
X
XCONTENTS
X	This directory contains:
X		README -- this file.
X		Makefile -- a makefile for the library.
X		useful.h -- a general header file, used by most everything.
X		ckalloc.c -- an interface to malloc(3) which diagnoses
X			out of memory conditions.  This topic was discussed
X			in the C Advisor column in UNIX Review Vol. 7 No 7.
X		ckalloc.3 -- documentation for ckalloc.
X		funclist.h -- a header file specific to the function list
X			routines in funclist.c.
X		funclist.c -- routines that provide lists of ``callout''
X			functions, used by several of the other routines
X			to handle errors.
X		funclist.3 -- documentation for the funclist routines.
X		lwp_dump.c -- a routine to dump the state of the LightWeight
X			Process system in SunOS 4.0 -- this isn't used, but
X			may be useful to some of you.
X		openpath.c -- a routine to open a file, searching through
X			a path of directories to find it.
X		openpath.3 -- documentation for openpath.
X		parseargs.h -- headers for the argument parser.
X		parseargs.c -- a command line argument parser.  Popular
X			response to my C Advisor column about this routine
X			in UNIX Review Vol. 7 No. 11 prompted me to make
X			this distribution available.
X		parseargs.3 -- documentation for parseargs.
X		fp_args.c -- argument value parsers for floating point values;
X			used by parseargs.  This is only included if you need
X			FP values.  I had to break this out because RISC/os
X			4.01 from Mips doesn't seem to support strtod in the
X			BSD environment.  You may find you need to include
X			-lm for this to work.
X		syserr.c -- error message printing routines.  A version of
X			this was discussed in the C Advisor column in UNIX
X			Review, Vol. 7 No. 7.
X		syserr.3 -- documentation for syserr.
X		traceset.c -- routines to set trace flags.  The flags are
X			tested by macros contained in useful.h.
X		trace.3 -- documentation for the trace routines and macros.
X		stest.c -- a small test program for the argument parser.
X
X	The parseargs routine really ought to have a way of matching a
X	list (e.g., return the rest of argv).  This isn't especially
X	hard to do, but I haven't gotten around to it yet.
X
XDISCLAIMERS
X	I hacked this code up to (hopefully) work on ANSI C compilers,
X	since several readers seem to be interested in this sort of thing.
X	I can't claim to have really tested this.
X
X	The original version was tested under SunOS 4.0 on SPARC architectures.
X	The version you see has been loosely tested on a Mips M/2000 running
X	RISC/os 4.01; I have only tried it in the BSD environment, and that
X	only loosely.
X
XACKNOWLEDGEMENTS
X	I wrote the first version of this code while working at the
X	International Computer Science Institute in Berkeley, CA.
X
X$Header: README,v 2.1 89/12/30 20:59:14 eric Exp $
//END_OF_FILE
echo x - README.PDS
sed 's/^X//' > README.PDS << '//END_OF_FILE'
XUpdate to parseargs by Peter da Silva (peter@ficc.uu.net).
X(second update: more improvements to arg parsing, argChar type)
X(third update, return to original calling sequence, argList type)
X
XParseargs is a really nifty set of routines, but it doesn't fit too
Xwell with standard UNIX semantics. In particular, you can get into a
Xlot of trouble using it in a script if it drops into interactive mode
Xon you. Also, it's not as useful as it could be for non-UNIX systems.
XTo make it work better, I've made a couple of changes.
X
XIt compiled straight out of the box on System III once I'd provided
Xbcopy, bcmp, and strtol. The strtol I've provided is almost totally
Xuntested, but hopefully you won't need to use it. It's only for folks with
Xold UNIX systems.
X
XFirst change was to disable the interactive prompting for arguments.
XI think that's inconsistent with usual UNIX semantics. You can undo
Xthis change by #defining INTERACTIVE when compiling parseargs.c.
X
XThe second change was to allow for a trailing list of arguments. I
Xoriginally implemented this by changing the calling sequence to parseargs.
XOn reflection this would just produce incompatibilities, so I added a
Xnew flag, ARGLIST, and a new type, listStr. Later, other kinds of lists
Xcan presumably be added. A list handles a pointer to a list of objects:
X
X	struct arglist *fred;
X
XOperations are defined on arglists, L_NEXT(fred) produces the next element
Xin fred. L_STRING(fred) produces the value of fred cast to "char *".
X
XDuring evaluation the list is kept in LIFO order, and it's reversed just
Xbefore returning to parseargs. This simplifies the coding of the list
Xroutines, but still lets you step through the list in a reasonable order.
X
XThe final change is the addition of a 'argChar' type. This parses character
Xarguments (such as the '-T' option to 'awk'), accepting single characters,
X'\nnn' octal escapes, and '^X' for control characters.
X
XParseargs itself no longer uses ckalloc, traceset, funclist, and so on.
Xthese routines are pretty cool, but when you're grafting parseargs onto
Xan existing program they just get in the way. Also, it's possible to make
Xparseargs fail in a cleaner fashion by handling out-of-memory cases myself.
XCertainly there's not going to be any loose memory lying around to be
Xcollected when parseargs starts up! To turn on TRACE and the funclist code
Xjust add -DTRACESTUFF and -DFUNCLISTSTUFF to the Makefile.
X
XAlso, the error messages have been made a bit more descriptive. Instead
Xof saying "stest: value required for -c flag", it prints "stest: RepCount
Xrequired for -c flag". The ad_prompt element should relly be a descriptive
Xword that can be used in a sentence... or for non_UNIX systems a multi-
Xcharacter or keyword based flag. I have an Amiga version included, for
Xexample, that uses keyword syntax. In that version, the usage message reads:
X
XUsage: amiga_test <name> [GROUP <newsgroup>]... [REP <repcount>] +
X	[DIR <dirname>] [X] [Y] [Z] [TAB <tabchar>] [<file>]...
X
XInstead of:
X
XUsage: unix_test <Name> [-n <newsGROUP>]... [-c <REPcount>] \
X	[-d <DIRname>] [-x] [-y] [-z] [-t <TABchar>] [<File>]...
X
XThis would solve the old problem of UNIX programs sticking out like a
Xsore thumb in other operating systems.
X
XThe Amiga version still needs to prompt for options if called with a
Xsingle '?' as an argument. This may require some redesign: it's almost
Xcertainly not going to be possible to stuff *extra* file names into
Xargv. Perhaps something like:
X
X	parseargs(&argc, &argv, "File");
X
X????????
//END_OF_FILE
echo x - Makefile
sed 's/^X//' > Makefile << '//END_OF_FILE'
X# $Header: Makefile,v 2.1 89/12/30 20:59:01 eric Exp $
X
XTARGET=		/usr
XLIBDIR=		/lib
XINCDIR=		${TARGET}/include
X
XINCLUDES=	-I.
XMODEL=		-Ms
XMODELNAME=	S
XSTYLE=		unix
XO=		-O
XOPTIONS=	# -DINTERACTIVE -DTRACESTUFF -DFUNCLISTSTUFF
X
XCFLAGS=		${MODEL} ${INCLUDES} $O ${OPTIONS}
X
XOFILES=		ckalloc.o \
X		argtype.o \
X		arglist.o \
X		fp_argtype.o \
X		funclist.o \
X		openpath.o \
X		${STYLE}_args.o \
X		syserr.o \
X		traceset.o \
X		strtol.o
X
XHFILES=		funclist.h \
X		parseargs.h \
X		useful.h
X
XCFILES=		ckalloc.c \
X		argtype.c \
X		arglist.c \
X		fp_argtype.c \
X		funclist.c \
X		openpath.c \
X		amiga_args.c \
X		unix_args.c \
X		syserr.c \
X		traceset.c \
X		stest.c
XMANFILES=	ckalloc.3 \
X		funclist.3 \
X		openpath.3 \
X		parseargs.3 \
X		syserr.3 \
X		trace.3
XXXFILES=	README \
X		README.PDS \
X		Makefile
X
XOBJS=		${OFILES}
XSRCFILES=	${XXFILES} ${HFILES} ${CFILES} ${MANFILES}
XLIBARGS=	${MODELNAME}lib${STYLE}_args.a
XLIBFILE=	${MODELNAME}libparse.a
XLOCLIBS=	${LIBARGS}
XSYSLIBS=	-lm
XLIBS=		${LOCLIBS} ${SYSLIBS}
XSHAR=		shar
X
X${STYLE}_test: stest.o ${LOCLIBS}
X	${CC} ${CFLAGS} -o ${STYLE}_test stest.o ${LIBS}
X
Xckalloc.o funclist.o syserr.o: funclist.h
X
X${STYLE}_args.o: parseargs.h
X
X${LIBARGS}: ${OBJS}
X	ar rvu $@ ${OBJS}
X	ranlib $@
X
Xinstall: ${INCDIR}/parseargs.h ${LIBDIR}/${LIBFILE}
X
X${INCDIR}/parseargs.h: parseargs.h
X	(cd ${INCDIR}; rm -f ${HFILES})
X	cp ${HFILES} ${INCDIR}
X
X${LIBDIR}/${LIBFILE}: ${LIBARGS}
X	rm -f ${LIBDIR}/${LIBFILE}
X	cp ${LIBARGS} ${LIBDIR}/${LIBFILE}
X	ranlib ${LIBDIR}/${LIBFILE}
X
Xshar: parseargs.shar
X
Xparseargs.shar: ${SRCFILES}
X	rm -f parseargs.shar
X	${SHAR} ${SRCFILES} > parseargs.shar
X
Xclean:
X	rm -f ${OFILES} ${LIBARGS} ${STYLE}_test tags stest.o
X
Xtags: ${CFILES} ${HFILES}
X	ctags ${CFILES} ${HFILES}
//END_OF_FILE
echo x - funclist.h
sed 's/^X//' > funclist.h << '//END_OF_FILE'
X/*
X**  FUNCLIST.H -- headers for function list routines
X**
X**	$Header: funclist.h,v 2.0 89/12/24 00:56:23 eric Exp $
X**
X**	Each entry in this list contains a pointer to a function to be
X**	invoked, and an integer argument to be passed to it.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X#ifndef FUNCLIST
X
X#ifndef _USEFUL_H_
X#include <useful.h>
X#endif
X
X#define FUNCLIST	struct _funclist
X
XFUNCLIST
X{
X	FUNCLIST	*_fl_next;	/* next in chain */
X	VOID		(*_fl_func)();	/* function to invoke */
X	ARBPTR		_fl_arg;	/* first argument */
X};
X
X#define FUNCLISTNULL	((FUNCLIST *) NULL)
X
X#endif
//END_OF_FILE
echo x - parseargs.h
sed 's/^X//' > parseargs.h << '//END_OF_FILE'
X/*
X**  PARSEARGS.H -- declarations for argument vector parser
X**
X**	$Header: parseargs.h,v 2.0 89/12/24 00:56:29 eric Exp $
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X#ifndef ARGDESC
X
X#ifndef _USEFUL_H_
X#include <useful.h>
X#endif
X
X#define ARGDESC		struct _argdesc
X
XARGDESC
X{
X	char	ad_name;	/* flag name */
X	char	ad_flags;	/* flags */
X	BOOL	(*ad_type) ARGS((ARGDESC *, char *, BOOL));
X				/* function to parse value */
X	ARBPTR	ad_valp;	/* pointer to value storage area */
X	char	*ad_prompt;	/* prompt string */
X};
X
X/* bits for ad_flags */
X#define ARGREQ		0x01	/* required argument */
X#define ARGOPT		0x00	/* optional argument pseudo-flag */
X#define ARGHIDDEN	0x02	/* don't display in usage message */
X#define ARGGIVEN	0x08	/* (internal) argument has been specified */
X#define ARGLIST		0x10	/* Argument is a list handler */
X
X/* types available for ad_type */
Xextern BOOL	argBool ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argChar ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argStr ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argInt ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argShort ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argLong ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argFloat ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	argDouble ARGS((ARGDESC *, char *, BOOL));
Xextern BOOL	listStr ARGS((ARGDESC *, char *, BOOL));
X
Xstruct arglist {
X	struct arglist *nl_next;
X	ARBPTR nl_val;
X};
X
X#define L_NEXT(l) ((l)->nl_next)		/* Next elt of list */
X#define L_STRING(e) ((char *)((e)->nl_val))	/* Elt as a string */
X
X#define ENDOFARGS	{ '\0' }
X
X#endif
//END_OF_FILE
echo x - useful.h
sed 's/^X//' > useful.h << '//END_OF_FILE'
X/*
X**  USEFUL.H -- various definitions of general interest
X**
X**	$Header: useful.h,v 2.0 89/12/24 00:56:33 eric Exp $
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X#ifndef _USEFUL_H_
X#define _USEFUL_H_
X
X#include <stdio.h>
X
X/* give a stab at the multiple-language dilemma */
X#ifdef __STDC__
X#define ARGS(x)		x
X#define NOARGS		(void)
X#define __ANSI_C__
X#else
X#if defined(c_plusplus) || defined(__cplusplus)
X#define ARGS(x)		x
X#define NOARGS		()
X#define __ANSI_C__
X#else
X#define ARGS(x)		()
X#define NOARGS		()
X#endif
X#endif
X
X#ifndef VOID
X#ifdef __ANSI_C__
X#define VOID		void
X#else
X#define VOID		int
X#endif
X#endif
X
X#ifndef TRUE
X#define TRUE		1
X#define FALSE		0
X#endif
X
X#ifndef BOOL
X#define BOOL		char
X#endif
X
X#ifndef STATIC
X#ifndef NODEBUG
X#define STATIC
X#else
X#define STATIC		static
X#endif
X#endif
X
X#ifndef EXTERN
X#define EXTERN		extern
X#endif
X
X#ifndef CONST
X#ifdef __ANSI_C__
X#define CONST		const
X#else
X#define CONST
X#endif
X#endif
X
X#ifndef NULL
X#define NULL		0
X#endif
X
X#ifndef CHARNULL
X#define CHARNULL	((char *) NULL)
X#endif
X
X#define FILENULL	((FILE *) NULL)
X
X#ifdef __ANSI_C__
X#define ARBPTR		void *
X#else
X#define ARBPTR		char *
X#endif
X#define __		(ARBPTR)
X#define ARBNULL		(__ NULL)
X
X#ifndef TRACESTUFF
X#define NODEBUG
X#endif
X
X#ifndef NODEBUG
X#define _TRACE_SIZE	200
Xextern unsigned char	_TraceVect[_TRACE_SIZE];
X#define TRACEF(f, l)	(_TraceVect[f] >= l)
X#define TRACE(f, l, m)	(TRACEF(f, l) ? printf m : 0)
X#else
X#define TRACEF(f, l)	(FALSE)
X#define TRACE(f, l, m)
X#endif
X
X#ifdef lint
X#define VERSIONID(v)
X#else
X#define VERSIONID(v)	static char _Version[] = v;
X#endif
X
X#define BITSET(b, w)	(((b) & (w)) != 0)
X
X#ifdef __STDC__
X#include <string.h>
X#else
Xextern char	*strchr ARGS((char *, char));
Xextern char	*strrchr ARGS((char *, char));
X#endif
X
Xextern ARBPTR	ckalloc ARGS((unsigned int));
X
X#define MAXINPUTLINE	200		/* maximum string input line */
X#define MAXWORDLEN	100		/* maximum word (token) length */
X
X#ifndef BSD
X#define bcopy(f,t,l) memcpy(t,f,l)
X#define bcmp(s,t,l) memcmp(s,t,l)
X#endif
X
X#endif /* _USEFUL_H_ */
//END_OF_FILE
echo x - ckalloc.c
sed 's/^X//' > ckalloc.c << '//END_OF_FILE'
X#include <useful.h>
X#include <funclist.h>
X
XVERSIONID("$Header: ckalloc.c,v 2.0 89/12/24 00:56:21 eric Exp $");
X
X/*
X**  CKALLOC -- allocate memory, checking for error conditions
X**
X**	If we cannot allocate memory, we call the list of functions
X**	in OutOfMemoryFuncs.  We then try again.  If the second attempt
X**	fails, we terminate the process.
X**
X**	Parameters:
X**		size -- the number of bytes of memory to be allocated.
X**
X**	Returns:
X**		A pointer to the allocated memory.
X**		Never returns if memory cannot be allocated.
X**
X**	Side Effects:
X**		As implied by memory allocation.
X**
X**	Globals:
X**		OutOfMemoryFuncs -- a FUNCLIST that is called when memory
X**			cannot be allocated.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
XFUNCLIST	*OutOfMemoryFuncs =	FUNCLISTNULL;
X
XARBPTR
Xckalloc(size)
X	unsigned int size;
X{
X	ARBPTR p;
X	extern char *malloc ARGS((unsigned int));
X
X	p = __ malloc(size);
X	if (p == ARBNULL)
X	{
X		funclist_call(OutOfMemoryFuncs, ARBNULL);
X		p = __ malloc(size);
X		if (p == ARBNULL)
X			syserr("ckalloc: out of memory");
X	}
X	return (p);
X}
//END_OF_FILE
echo x - argtype.c
sed 's/^X//' > argtype.c << '//END_OF_FILE'
X#include <useful.h>
X#include <parseargs.h>
X#include <ctype.h>
X
X#ifdef __STDC__
Xtypedef void *pointer;
X#else
Xtypedef char *pointer;
X#endif
X
Xextern pointer malloc();
X
X#define ALL_AD		ad = argd; ad->ad_name != '\0'; ad++
X#define ALL_DEFS	ad = _DefaultArgs; ad->ad_name != '\0'; ad++
X
Xextern char	*ProgName;
X
X/*
X**  ARGtype -- argument translation routines.
X**
X**	Each of these converts a parameter value to the internal form,
X**	including validity checking.  Their parameters and return values
X**	all behave identically.
X**
X**	Parameters:
X**		ad -- the argument descriptor for this parameter.
X**		vp -- a pointer to the string input value.
X**		copyf -- if TRUE, the value will be destroyed later,
X**			and so should be copied if it will be retained
X**			(as for a string).
X**
X**	Returns:
X**		TRUE -- if the conversion was successful.  The actual
X**			value should be stored in the location indicated
X**			by ad->ad_valp.
X**		FALSE -- if the conversion failed.  The reason for failure
X**			should be diagnosed using usrerr().
X**
X**	Side Effects:
X**		The value should be stored through ad->ad_valp.
X*/
X
XBOOL
XargStr(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	char *cp;
X
X	if (copyf)
X	{
X		register int i;
X
X		i = strlen(vp) + 1;
X		cp = (char *) malloc(i);
X		if(!cp) {
X			usrerr("out of memory parsing %s", ad->ad_prompt);
X			return FALSE;
X		}
X		bcopy(vp, cp, i);
X	}
X	else
X	{
X		cp = vp;
X	}
X	*(char **) ad->ad_valp = cp;
X	return (TRUE);
X}
X
XBOOL
XargChar(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	auto char *vpp;
X	extern long strtol ARGS((char *, char **, int));
X	int status = FALSE;
X	char c;
X
X	if (strlen(vp) == 2 && vp[0]=='^')
X	{
X		c = vp[1] ^ '@';
X		status = TRUE;
X	}
X	else if(strlen(vp) > 1 && vp[0]=='\\')
X	{
X		c = (int) strtol(&vp[1], &vpp, 8);
X		if (*vpp == '\0')
X			status = TRUE;
X	}
X	else if(strlen(vp) == 1)
X	{
X		c = *vp;
X		status = TRUE;
X	}
X
X	if(status == TRUE)
X		*(char *) ad->ad_valp = c;
X	else
X	{
X		usrerr("invalid character argument '%s' for %s",
X			vp, ad->ad_prompt);
X	}
X	return status;
X}
X
X/*ARGSUSED*/
XBOOL
XargInt(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	auto char *vpp;
X	extern long strtol ARGS((char *, char **, int));
X
X	*(int *) ad->ad_valp = (int) strtol(vp, &vpp, 0);
X	if (*vpp != '\0')
X	{
X		usrerr("invalid integer argument '%s' for %s",
X			vp, ad->ad_prompt);
X		return (FALSE);
X	}
X	else
X	{
X		return (TRUE);
X	}
X}
X
X/*ARGSUSED*/
XBOOL
XargShort(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	auto char *vpp;
X	extern long strtol ARGS((char *, char **, int));
X
X	*(short *) ad->ad_valp = (short) strtol(vp, &vpp, 0);
X	if (*vpp != '\0')
X	{
X		usrerr("invalid integer argument '%s' for %s",
X			vp, ad->ad_prompt);
X		return (FALSE);
X	}
X	else
X	{
X		return (TRUE);
X	}
X}
X
X/*ARGSUSED*/
XBOOL
XargLong(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	auto char *vpp;
X	extern long strtol ARGS((char *, char **, int));
X
X	*(long *) ad->ad_valp = strtol(vp, &vpp, 0);
X	if (*vpp != '\0')
X	{
X		usrerr("invalid integer argument '%s' for %s",
X			vp, ad->ad_prompt);
X		return (FALSE);
X	}
X	else
X	{
X		return (TRUE);
X	}
X}
X
Xstruct booltab
X{
X	char	*bname;		/* string to match against */
X	char	bneedmatch;	/* number of characters that must match */
X	BOOL	bval;		/* value to use */
X};
X
XSTATIC struct booltab	_BoolTab[] =
X{
X	"yes",		1,	TRUE,
X	"no",		1,	FALSE,
X	"true",		1,	TRUE,
X	"false",	1,	FALSE,
X	"on",		2,	TRUE,
X	"off",		3,	FALSE,
X	CHARNULL
X};
X
X/*ARGSUSED*/
XBOOL
XargBool(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	register struct booltab *b;
X	register char *cp;
X	int l;
X
X	/* convert input to lower case */
X	for (l = 0, cp = vp; *cp != '\0'; l++, cp++)
X	{
X		if (isupper(*cp))
X			*cp = tolower(*cp);
X	}
X
X	/* search for a match in the table */
X	for (b = _BoolTab; b->bname != CHARNULL; b++)
X	{
X		/* if too short, don't even bother trying */
X		if (l < b->bneedmatch)
X			continue;
X
X		if (bcmp(vp, b->bname, l) == 0)
X		{
X			/* got a match */
X			*(BOOL *) ad->ad_valp = b->bval;
X			return (TRUE);
X		}
X	}
X
X	usrerr("invalid Boolean argument '%s' for %s", vp, ad->ad_prompt);
X	return (FALSE);
X}
X
X#ifdef TRACESTUFF
X/*ARGSUSED*/
XBOOL
XargTrace(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	traceset(vp);
X	return (TRUE);
X}
X#endif
X
X/*ARGSUSED*/
XBOOL
XargEnd(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	return (FALSE);
X}
//END_OF_FILE
echo x - arglist.c
sed 's/^X//' > arglist.c << '//END_OF_FILE'
X#include <parseargs.h>
X#include <ctype.h>
X
X#ifdef __STDC__
Xtypedef void *pointer;
X#else
Xtypedef char *pointer;
X#endif
X
Xextern pointer malloc();
X
X#define ALL_AD		ad = argd; ad->ad_name != '\0'; ad++
X#define ALL_DEFS	ad = _DefaultArgs; ad->ad_name != '\0'; ad++
X
Xextern char	*ProgName;
X
X/* Argument list utility routines. After processing, parseargs calls
X * cleanup_lists to reverse all argument lists so they stay in order.
X */
X
X/* Reverse a list */
Xstruct arglist *reverselist(from)
Xstruct arglist *from;
X{
X	struct arglist *to, *tmp;
X
X	to = NULL;
X	while(from) {
X		tmp = from; /* remove top from old list */
X		from = from->nl_next;
X		tmp->nl_next = to; /* insert top in new list */
X		to = tmp;
X	}
X	return to;
X}
X
X/* Reverse all arglists in argd */
Xcleanup_lists(argd)
XARGDESC *argd;
X{
X	ARGDESC *ad;
X
X	for(ALL_AD) {
X		if( (ad->ad_flags & ARGLIST) &&
X		    *(struct arglist **)ad->ad_valp) {
X			*(struct arglist **)ad->ad_valp =
X				reverselist( *(struct arglist **)ad->ad_valp );
X		}
X	}
X}
X
X/*
X**  ARGlist -- list argument translation routines.
X**
X**	Each of these converts a parameter value to the internal form,
X**	including validity checking.  Their parameters and return values
X**	all behave identically. These are the routines for dealing with
X**	lists...
X**
X**	Parameters:
X**		ad -- the argument descriptor for this parameter.
X**		vp -- a pointer to the string input value.
X**		copyf -- if TRUE, the value will be destroyed later,
X**			and so should be copied if it will be retained
X**			(as for a string).
X**
X**	Returns:
X**		TRUE -- if the conversion was successful.  The actual
X**			value should be added to the list stored in the
X**			location indicated by ad->ad_valp.
X**		FALSE -- if the conversion failed.  The reason for failure
X**			should be diagnosed using usrerr().
X**
X**	Side Effects:
X**		The value should be stored through ad->ad_valp.
X*/
X
XBOOL
XlistStr(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	char *cp;
X	struct arglist *nl;
X
X	if (copyf)
X	{
X		register int i;
X
X		i = strlen(vp) + 1;
X		cp = (char *) malloc(i);
X		if(!cp) {
X			usrerr("out of memory saving string %s", ad->ad_prompt);
X			return FALSE;
X		}
X		bcopy(vp, cp, i);
X	}
X	else
X	{
X		cp = vp;
X	}
X
X	nl = (struct arglist *) malloc(sizeof *nl);
X	if(!nl) {
X		usrerr("out of memory saving arg %s", ad->ad_prompt);
X		if(copyf) free(cp);
X		return FALSE;
X	}
X
X	nl->nl_next = *(struct arglist **) ad->ad_valp;
X	nl->nl_val = (ARBPTR)cp;
X	*(struct arglist **) ad->ad_valp = nl;
X	return (TRUE);
X}
X
//END_OF_FILE
echo x - fp_argtype.c
sed 's/^X//' > fp_argtype.c << '//END_OF_FILE'
X#include <useful.h>
X#include <parseargs.h>
X
XVERSIONID("$Header: fp_args.c,v 2.0 89/12/24 00:56:21 eric Exp $");
X
X/*
X**  PARSEARGV argument type functions for floating point operands.
X**
X**	These are broken out to avoid loading the floating precision
X**	conversion routines when they aren't actually needed.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X/*ARGSUSED*/
XBOOL
XargDouble(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	auto char *vpp;
X	extern double strtod ARGS((char *, char **));
X
X	*(double *) ad->ad_valp = strtod(vp, &vpp);
X	if (*vpp != '\0')
X	{
X		usrerr("invalid floating point argument '%s' for %s",
X			vp, ad->ad_prompt);
X		return (FALSE);
X	}
X	else
X	{
X		return (TRUE);
X	}
X}
X
X/*ARGSUSED*/
XBOOL
XargFloat(ad, vp, copyf)
X	register ARGDESC *ad;
X	register char *vp;
X	BOOL copyf;
X{
X	auto char *vpp;
X	extern double strtod ARGS((char *, char **));
X
X	*(float *) ad->ad_valp = (float) strtod(vp, &vpp);
X	if (*vpp != '\0')
X	{
X		usrerr("invalid floating point argument '%s' for %s",
X			vp, ad->ad_prompt);
X		return (FALSE);
X	}
X	else
X	{
X		return (TRUE);
X	}
X}
//END_OF_FILE
echo x - funclist.c
sed 's/^X//' > funclist.c << '//END_OF_FILE'
X#include <useful.h>
X#include <funclist.h>
X
XVERSIONID("$Header: funclist.c,v 2.0 89/12/24 00:56:22 eric Exp $");
X
X/*
X**  FUNCLIST_ADD -- add a function specification to a function list
X**
X**	Parameters:
X**		flh -- the address of a pointer to a function list.  Note
X**			that this must be a FUNCLIST **.
X**		func -- the function to add to the list.
X**		arg -- the first argument to the function.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		The indicated function is added to the function list
X**		for later invocation by funclist_call.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
XVOID
Xfunclist_add(flh, func, arg)
X	FUNCLIST **flh;
X	VOID (*func)();
X	ARBPTR arg;
X{
X	register FUNCLIST *fl;
X
X	fl = (FUNCLIST *) ckalloc(sizeof *fl);
X	fl->_fl_func = func;
X	fl->_fl_arg = arg;
X	fl->_fl_next = *flh;
X	*flh = fl;
X}
X/*
X**  FUNCLIST_CALL -- call all the functions on a function list.
X**
X**	Parameters:
X**		fl -- the function list to call.  Unlike funclist_add,
X**			this should not be an indirect pointer.
X**		arg -- the second argument to the functions.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		As implied by the function list.
X*/
X
XVOID
Xfunclist_call(fl, arg)
X	register FUNCLIST *fl;
X	ARBPTR arg;
X{
X	while (fl != FUNCLISTNULL)
X	{
X		(*fl->_fl_func)(fl->_fl_arg, arg);
X		fl = fl->_fl_next;
X	}
X}
//END_OF_FILE
echo x - openpath.c
sed 's/^X//' > openpath.c << '//END_OF_FILE'
X#include <useful.h>
X
XVERSIONID("$Header: openpath.c,v 2.0 89/12/24 00:56:26 eric Exp $");
X
X/*
X**  OPENPATH -- open a file, searching through a path
X**
X**	Parameters:
X**		fname -- the file name, relative to the desired path.
X**		mode -- the open mode, just like fopen.
X**		path -- the search path.
X**
X**	Returns:
X**		FILENULL if the file could not be found at all.
X**		A FILE pointer open for reading for the first instance
X**			of the file that could be found in the path
X**			otherwise.
X**
X**	Side Effects:
X**		none.
X**
X**	Notes:
X**		Should really just find the file, not open it.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
Xchar	*DefaultPath =		CHARNULL;
X
X#ifndef DEFAULTROOTPATH
X#define DEFAULTROOTPATH		"/usr/local:/usr:~:"
X#endif
X
XFILE *
Xopenpath(fname, mode, path)
X	char *fname;
X	char *mode;
X	char *path;
X{
X	register char *dp;
X	register char *ep;
X	register char *bp;
X	FILE *fp;
X	char fbuf[200];
X	extern char *getenv ARGS((char *));
X
X	dp = path;
X	if (dp == CHARNULL)
X	{
X		dp = DefaultPath;
X		if (dp == CHARNULL)
X		{
X			/* locate the root of our utility tree */
X			dp = getenv("ROOTPATH");
X			if (dp == CHARNULL)
X				dp = DEFAULTROOTPATH;
X			DefaultPath = dp;
X		}
X	}
X	for (;; dp = ++ep)
X	{
X		register int l;
X		int i;
X		int fspace;
X
X		/* extract a component */
X		ep = strchr(dp, ':');
X		if (ep == CHARNULL)
X			ep = &dp[strlen(dp)];
X
X		/* find the length of that component */
X		l = ep - dp;
X		bp = fbuf;
X		fspace = sizeof fbuf - 2;
X		if (l > 0)
X		{
X			/*
X			**  If the length of the component is zero length,
X			**  start from the current directory.  If the
X			**  component begins with "~", start from the
X			**  user's $HOME environment variable.  Otherwise
X			**  take the path literally.
X			*/
X
X			if (*dp == '~' && (l == 1 || dp[1] == '/'))
X			{
X				char *home;
X
X				home = getenv("HOME");
X				if (home != CHARNULL)
X				{
X					i = strlen(home);
X					if ((fspace -= i) < 0)
X						goto toolong;
X					bcopy(home, bp, i);
X					bp += i;
X				}
X				dp++;
X				l--;
X			}
X			if (l > 0)
X			{
X				if ((fspace -= l) < 0)
X					goto toolong;
X				bcopy(dp, bp, l);
X				bp += l;
X			}
X
X			/* add a "/" between directory and filename */
X			if (ep[-1] != '/')
X				*bp++ = '/';
X		}
X
X		/* now append the file name */
X		i = strlen(fname);
X		if ((fspace -= i) < 0)
X		{
X	toolong:
X			fprintf(stderr, "openpath: pathname too long (ignored)\n");
X			*bp = '\0';
X			fprintf(stderr, "\tDirectory \"%s\"\n", fbuf);
X			fprintf(stderr, "\tFile \"%s\"\n", fname);
X			continue;
X		}
X		bcopy(fname, bp, i + 1);
X
X		/* try to open that file */
X		fp = fopen(fbuf, mode);
X
X		/* if it exists, all is well */
X		if (fp != FILENULL)
X			return (fp);
X
X		/* if not, and no other alternatives, life is bleak */
X		if (*ep == '\0')
X			return (FILENULL);
X
X		/* otherwise try the next component in the search path */
X	}
X}
//END_OF_FILE
echo x - amiga_args.c
sed 's/^X//' > amiga_args.c << '//END_OF_FILE'
X#include <useful.h>
X#include <parseargs.h>
X#include <ctype.h>
X
XVERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $");
X
X/*
X**  PARSEARGS -- parse an argument vector, given a description
X**
X**	Parameters:
X**		argv -- the argument vector as passed to main().
X**		argd -- the argument descriptor array.
X**
X**	Returns:
X**		Nothing
X**			Exits with return code 20 if error in args.
X**			Exits with return code 10 if system error.
X**
X**	Side Effects:
X**		Converts and stores arguments into variables as
X**		described by argd.
X**
X**	Globals:
X**		DefaultPath -- the pathname of a set of places to
X**			look for system files, set from the ROOTPATH
X**			environment variable, or a default.
X**		ProgName -- the name of this program, saved for error
X**			messages and the like.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X#define ALL_AD		ad = argd; ad->ad_name != '\0'; ad++
X#define ALL_DEFS	ad = _DefaultArgs; ad->ad_name != '\0'; ad++
X
X#define HANDLE(a,n,f) ((*(a)->ad_type)(a,n,f))
X
Xchar	*ProgName;
X
X#ifdef TRACESTUFF
Xextern BOOL	argTrace ARGS((ARGDESC *, char *, BOOL));
X#endif
Xextern BOOL	argEnd ARGS((ARGDESC *, char *, BOOL));
X
X/* default arguments -- apply to all programs */
XSTATIC ARGDESC	_DefaultArgs[] =
X{
X     /* name	flags	type		valp		prompt		*/
X#ifdef TRACESTUFF
X	'T',	ARGOPT,	argTrace,	ARBNULL,	"TRACE",
X#endif
X	'-',	ARGOPT,	argEnd,		ARBNULL,	"ARGS",
X	ENDOFARGS
X};
X
X/* override argument descriptor, if none given by user */
XSTATIC ARGDESC	_NullArgDesc[] =
X{
X	ENDOFARGS
X};
X
XVOID
Xparseargs(argv, argd)
X	char **argv;
X	ARGDESC argd[];
X{
X	register ARGDESC *ad, *list;
X	register char **av;
X	register char *p;
X	BOOL noflags;
X	BOOL error;
X	extern char *getenv ARGS((char *));
X
X	av = argv++;
X	/* save the name of this program (for error messages) */
X	ProgName = *av;
X
X	/* allow null argument descriptor */
X	if (argd == (ARGDESC *) NULL)
X		argd = _NullArgDesc;
X
X	/* clear out any cruft in the argument descriptor */
X	for (ALL_AD)
X	{
X		ad->ad_flags &= ~ARGGIVEN;
X	}
X	for (ALL_DEFS)
X	{
X		ad->ad_flags &= ~ARGGIVEN;
X	}
X
X	/* run through the argument vector */
X	noflags = FALSE;
X	error = FALSE;
X	ad = NULL; /* No argument requested */
X	list = NULL;
X	while (*++av != CHARNULL)
X	{
X		/* Previous keyword required a value */
X		if(ad)
X		{
X			/* try to convert the type */
X			if (!HANDLE(ad, *av, FALSE))
X				error = TRUE;
X			else
X				ad->ad_flags |= ARGGIVEN;
X			ad = NULL;
X			continue;
X		}
X		
X		/* If looking for keywords, see if this is one */
X		if(!noflags) {
X			for(ALL_AD)
X				if(match(*av, ad->ad_prompt) == 0)
X					break;
X			if(ad->ad_name == '\0')
X				for(ALL_DEFS)
X					if(match(*av, ad->ad_prompt) == 0)
X						break;
X		}
X		if(ad->ad_name == '\0')
X			ad = NULL;
X
X		/* If we have a keyword here */
X		if(!noflags && ad)
X		{
X			list = NULL;
X			p = strchr(*av, '=');
X			if(p) /* matched NAME=VALUE */
X			{
X				p++;
X				/* try to convert the type */
X				if (!HANDLE(ad, p, FALSE))
X					error = TRUE;
X				else
X					ad->ad_flags |= ARGGIVEN;
X				ad = NULL;
X			}
X			else
X			{
X				if (ad->ad_type == argBool)
X				{
X					*(BOOL *) ad->ad_valp = TRUE;
X					ad->ad_flags |= ARGGIVEN;
X					ad = NULL;
X				}
X				else if (ad->ad_type == argEnd)
X				{
X					noflags = TRUE;
X					ad->ad_flags |= ARGGIVEN;
X					ad = NULL;
X				}
X				else if (ad->ad_flags & ARGLIST)
X				{
X					list = ad;
X					ad = NULL;
X				}
X			}
X		}
X		else /* it's a positional argument */
X		{
X			if(list) {
X				if (!HANDLE(list, *av, FALSE))
X					error = TRUE;
X				list->ad_flags |= ARGGIVEN;
X				continue;
X			}
X			else
X			{
X				for (ALL_AD)
X				{
X					if (ad->ad_name == ' ' &&
X					    ( (ad->ad_flags & ARGLIST) ||
X					     !BITSET(ARGGIVEN, ad->ad_flags))
X					   )
X						break;
X				}
X				if (ad->ad_name == '\0')
X				{
X					usrerr("too any arguments");
X					error = TRUE;
X					ad = NULL;
X				}
X				else
X				{
X					/* try to convert */
X					if (!HANDLE(ad, *av, FALSE))
X						error = TRUE;
X					else
X						ad->ad_flags |= ARGGIVEN;
X					ad = NULL;
X				}
X			}
X		}
X	}
X
X	/* If last argument was a keyword and required an option
X	 * then complain about it
X	 */
X	if(ad) {
X		usrerr("missing argument for %s", ad->ad_prompt);
X		error = TRUE;
X	}
X	*argv = NULL;
X
X	/* now rescan for missing required arguments */
X	for (ALL_AD)
X	{
X		if (BITSET(ARGREQ, ad->ad_flags) && !BITSET(ARGGIVEN, ad->ad_flags))
X		{
X			if (!BITSET(ARGGIVEN, ad->ad_flags))
X			{
X				/* still didn't get a value... sigh */
X				if (ad->ad_name == ' ')
X				{
X					usrerr("%s required",
X						ad->ad_prompt);
X				}
X				else
X				{
X					usrerr("%s required for -%c flag",
X						ad->ad_prompt, ad->ad_name);
X				}
X				error = TRUE;
X			}
X		}
X	}
X
X	if (error)
X	{
X		usage(argd);
X		exit(20);
X	}
X	cleanup_lists(argd);
X}
X/*
X**  USAGE -- print a usage message
X**
X**	Parameters:
X**		argd -- the description of expected arguments.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		prints on stderr
X**
X**	Globals:
X**		MaxOutputLine -- the length of the maximum output line
X**			allowed before wrapping.  This should be fetched
X**			from the terminal driver on systems that support
X**			this sort of thing.
X*/
X
Xint	MaxOutputLine = 72;
X
XVOID
Xusage(argd)
X	ARGDESC *argd;
X{
X	register ARGDESC *ad;
X	int ll;
X	int pl;
X
X	fprintf(stderr, "Usage: %s", ProgName);
X	ll = strlen(ProgName) + 7;
X
X	for (ALL_AD)
X	{
X		char keyword[BUFSIZ];
X		char name[BUFSIZ];
X		int i, j;
X
X		j = 0;
X		for(i = 0; ad->ad_prompt[i]; i++) {
X			if(isupper(ad->ad_prompt[i])) {
X				keyword[j++] = ad->ad_prompt[i];
X				name[i] = tolower(ad->ad_prompt[i]);
X			}
X			else
X				name[i] = ad->ad_prompt[i];
X		}
X		name[i] = 0;
X		if(j > 0)
X			keyword[j] = 0;
X		else
X			strcpy(keyword, ad->ad_prompt);
X
X		/* don't display hidden arguments */
X		if (BITSET(ARGHIDDEN, ad->ad_flags))
X			continue;
X
X		/* figure out how wide this parameter is (for printing) */
X		if (ad->ad_name != ' ')
X		{
X			pl = strlen(keyword);
X			if (ad->ad_type != argBool)
X				pl += strlen(name) + 3;/* _< > */
X		}
X		else
X		{
X			pl = strlen(name) + 2;		/* < > */
X		}
X		if (!BITSET(ARGREQ, ad->ad_flags))
X			pl += 2;				/* [ ] */
X		if (ad->ad_flags & ARGLIST)
X			pl += 3;			/* ... */
X		pl += 1;					/* leading sp */
X
X		/* see if this will fit */
X		if (ll + pl > MaxOutputLine)
X		{
X			/* no... start a new line */
X			fprintf(stderr, " +\n\t");
X			ll = 7;
X		}
X		else
X		{
X			/* yes... just throw in a space */
X			fprintf(stderr, " ");
X		}
X		ll += pl;
X
X		/* show the argument */
X		if (!BITSET(ARGREQ, ad->ad_flags))
X			fprintf(stderr, "[");
X		if (ad->ad_name != ' ')
X		{
X			fprintf(stderr, "%s", keyword);
X			if (ad->ad_type != argBool)
X				fprintf(stderr, " ");
X		}
X		if (ad->ad_name == ' ' || ad->ad_type != argBool)
X			fprintf(stderr, "<%s>", name);
X		if (!BITSET(ARGREQ, ad->ad_flags))
X			fprintf(stderr, "]");
X		if (ad->ad_flags & ARGLIST)
X			fprintf(stderr, "...");
X	}
X	fprintf(stderr, "\n");
X}
X
X/* match(s1, s2)
X**
X** Compares two strings, returning >0, <0, or =0 if they match. First a
X** check is done on letters capitalised in the second word, and if this
X** fails then a complete match is done. Case is ignored in both matches.
X** This lets you use case to indicate what part of a keyword is significant.
X*/
Xint match(candidate, target)
Xchar *target, *candidate;
X{
X	int i, j;
X	char c;
X
X	i = j = 0;
X
X	while(target[i] || candidate[i]) {
X		while(islower(target[i])) i++;
X		if(!target[i]) {
X			if(!candidate[j]) return 0;
X			return dictcmp(target, candidate);
X		}
X		c = islower(candidate[j])
X		    ? toupper(candidate[j])
X		    : candidate[j];
X		if(target[i] != c) return dictcmp(target, candidate);
X		i++;
X		j++;
X	}
X	return 0;
X}
X
Xint dictcmp(s1, s2)	/* "Dictionary" comparison of two strings */
Xchar *s1, *s2;
X{
X	char c1, c2;
X
X	while(*s1 || *s2) {
X		c1 = *s1++;
X		c2 = *s2++;
X		if(!c1 || !c2) return c1 - c2;
X		if(isupper(c1)) c1 = tolower(c1);
X		if(isupper(c2)) c2 = tolower(c2);
X		if(c1 != c2) return c1 - c2;
X	}
X	return 0;
X}
//END_OF_FILE
echo x - unix_args.c
sed 's/^X//' > unix_args.c << '//END_OF_FILE'
X#include <useful.h>
X#include <parseargs.h>
X#include <ctype.h>
X
XVERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $");
X
X/*
X**  PARSEARGS -- parse an argument vector, given a description
X**
X**	Parameters:
X**		argv -- pointer to the argument vector as passed to main().
X**		argd -- the argument descriptor array.
X**
X**	Returns:
X**		Nothing.
X**			Exits with return code 2 if error in args.
X**			Exits with return code 1 if system error.
X**
X**	Side Effects:
X**		Converts and stores arguments into variables as
X**		described by argd.
X**
X**	Globals:
X**		DefaultPath -- the pathname of a set of places to
X**			look for system files, set from the ROOTPATH
X**			environment variable, or a default.
X**		ProgName -- the name of this program, saved for error
X**			messages and the like.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X#define ALL_AD		ad = argd; ad->ad_name != '\0'; ad++
X#define ALL_DEFS	ad = _DefaultArgs; ad->ad_name != '\0'; ad++
X
X#define HANDLE(a,n,f) ((*(a)->ad_type)(a,n,f))
X
Xchar	*ProgName;
X
X#ifdef TRACESTUFF
Xextern BOOL	argTrace ARGS((ARGDESC *, char *, BOOL));
X#endif
Xextern BOOL	argEnd ARGS((ARGDESC *, char *, BOOL));
X
X/* default arguments -- apply to all programs */
XSTATIC ARGDESC	_DefaultArgs[] =
X{
X     /* name	flags	type		valp		prompt		*/
X#ifdef TRACESTUFF
X	'T',	ARGOPT,	argTrace,	ARBNULL,	"TRACE",
X#endif
X	'-',	ARGOPT,	argEnd,		ARBNULL,	"ARGS",
X	ENDOFARGS
X};
X
X/* override argument descriptor, if none given by user */
XSTATIC ARGDESC	_NullArgDesc[] =
X{
X	ENDOFARGS
X};
X
XVOID
Xparseargs(argv, argd)
X	char **argv;
X	ARGDESC argd[];
X{
X	register ARGDESC *ad, *list;
X	register char **av;
X	register char *p;
X	int argc;
X	BOOL noflags;
X	BOOL error;
X#ifdef INTERACTIVE
X	BOOL shouldprompt;
X#endif
X	extern char *getenv ARGS((char *));
X#ifdef INTERACTIVE
X	extern int isatty ARGS((int));
X#endif
X
X	av = argv++;
X	argc = 1;
X
X	/* save the name of this program (for error messages) */
X	ProgName = *av;
X
X#ifdef INTERACTIVE
X	/* test to see if we are interactive */
X	shouldprompt = (BOOL) isatty(0) && (BOOL) isatty(2);
X#endif
X
X	/* allow null argument descriptor */
X	if (argd == (ARGDESC *) NULL)
X		argd = _NullArgDesc;
X
X	/* clear out any cruft in the argument descriptor */
X	for (ALL_AD)
X	{
X		ad->ad_flags &= ~ARGGIVEN;
X	}
X	for (ALL_DEFS)
X	{
X		ad->ad_flags &= ~ARGGIVEN;
X	}
X
X	/* run through the argument vector */
X	noflags = FALSE;
X	error = FALSE;
X	list = NULL;
X	while ((p = *++av) != CHARNULL)
X	{
X		if (*p == '-' && !noflags)
X		{
X			/* flag argument */
X			if (*++p == '-' && p[1] == '\0')
X			{
X				/* -- indicates end of flags */
X				noflags = TRUE;
X				list = NULL;
X				continue;
X			}
X			while (*p != '\0')
X			{
X				/* find the flag in the list */
X				for (ALL_AD)
X				{
X					if (ad->ad_name == *p)
X						break;
X				}
X				if (ad->ad_name == '\0')
X				{
X					for (ALL_DEFS)
X					{
X						if (ad->ad_name == *p)
X							break;
X					}
X					if (ad->ad_name == '\0')
X					{
X						usrerr("flag -%c unknown", *p++);
X						error = TRUE;
X						continue;
X					}
X				}
X
X				/* move p up to point to the (possible) value */
X				p++;
X
X				/* booleans are special, having no value */
X				if (ad->ad_type == argBool)
X				{
X					*(BOOL *) ad->ad_valp = TRUE;
X					ad->ad_flags |= ARGGIVEN;
X					continue;
X				}
X#ifdef TRACESTUFF
X				else if (ad->ad_type == argTrace)
X				{
X					traceset(p);
X					ad->ad_flags |= ARGGIVEN;
X					break;
X				}
X#endif
X
X				/* now get the real value */
X				if (*p == '\0')
X				{
X					p = *++av;
X					if (p == CHARNULL || *p == '-')
X					{
X						av--;
X						usrerr("%s required for -%c flag",
X							ad->ad_prompt, ad->ad_name);
X						error = TRUE;
X						break;
X					}
X				}
X
X				/* try to convert the type */
X				if (!HANDLE(ad, p, FALSE))
X					error = TRUE;
X				else
X					ad->ad_flags |= ARGGIVEN;
X				if(ad->ad_flags & ARGLIST)
X					list = ad;
X				else
X					list = NULL;
X				break;
X			}
X		}
X		else
X		{
X			/* parsing a list of arguments */
X			if(list) {
X				if (!HANDLE(list, p, FALSE))
X					error = TRUE;
X				continue;
X			}
X			/* positional argument */
X			for (ALL_AD)
X			{
X				if (ad->ad_name == ' ' &&
X				    ( (ad->ad_flags & ARGLIST) ||
X				     !BITSET(ARGGIVEN, ad->ad_flags))
X				    )
X					break;
X			}
X			if (ad->ad_name == '\0')
X			{
X				usrerr("too any arguments");
X				error = 1;
X				continue;
X			}
X
X			/* try to convert */
X			if (!HANDLE(ad, p, FALSE))
X				error = TRUE;
X			else
X				ad->ad_flags |= ARGGIVEN;
X		}
X	}
X
X	/* now rescan for missing required arguments */
X	for (ALL_AD)
X	{
X		if (BITSET(ARGREQ, ad->ad_flags) && !BITSET(ARGGIVEN, ad->ad_flags))
X		{
X#ifdef INTERACTIVE
X			/* can we prompt? */
X			while (shouldprompt && !error)
X			{
X				char buf[MAXINPUTLINE];
X
X				fprintf(stderr, "%s? ", ad->ad_prompt);
X				fflush(stderr);
X				if (fgets(buf, sizeof buf, stdin) == CHARNULL)
X					break;
X				p = strchr(buf, '\n');
X				if (p != CHARNULL)
X					*p = '\0';
X				if (buf[0] == '\0')
X				{
X					usrerr("value required");
X					continue;
X				}
X				if (HANDLE(ad, buf, TRUE))
X				{
X					ad->ad_flags |= ARGGIVEN;
X					break;
X				}
X			}
X#endif
X
X			if (!BITSET(ARGGIVEN, ad->ad_flags))
X			{
X				/* still didn't get a value... sigh */
X				if (ad->ad_name == ' ')
X				{
X					usrerr("%s required",
X						ad->ad_prompt);
X				}
X				else
X				{
X					usrerr("%s required for -%c flag",
X						ad->ad_prompt, ad->ad_name);
X				}
X				error = TRUE;
X			}
X		}
X	}
X
X	if (error)
X	{
X		usage(argd);
X		exit(2);
X	}
X
X	cleanup_lists(argd);
X}
X/*
X**  USAGE -- print a usage message
X**
X**	Parameters:
X**		argd -- the description of expected arguments.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		prints on stderr
X**
X**	Globals:
X**		MaxOutputLine -- the length of the maximum output line
X**			allowed before wrapping.  This should be fetched
X**			from the terminal driver on systems that support
X**			this sort of thing.
X*/
X
Xint	MaxOutputLine = 72;
X
XVOID
Xusage(argd)
X	ARGDESC *argd;
X{
X	register ARGDESC *ad;
X	int ll;
X	int pl;
X
X	fprintf(stderr, "Usage: %s", ProgName);
X	ll = strlen(ProgName) + 7;
X
X	for (ALL_AD)
X	{
X		/* don't display hidden arguments */
X		if (BITSET(ARGHIDDEN, ad->ad_flags))
X			continue;
X
X		/* figure out how wide this parameter is (for printing) */
X		if (ad->ad_name != ' ')
X		{
X			pl = 2;					/* -x */
X			if (ad->ad_type != argBool)
X				pl += strlen(ad->ad_prompt) + 3;/* _< > */
X		}
X		else
X		{
X			pl = strlen(ad->ad_prompt) + 2;		/* < > */
X		}
X		if (!BITSET(ARGREQ, ad->ad_flags))
X			pl += 2;				/* [ ] */
X		if (ad->ad_flags & ARGLIST)			/* ... */
X			pl += 3;
X		pl += 1;					/* leading sp */
X
X		/* see if this will fit */
X		if (ll + pl > MaxOutputLine)
X		{
X			/* no... start a new line */
X			fprintf(stderr, " \\\n\t");
X			ll = 7;
X		}
X		else
X		{
X			/* yes... just throw in a space */
X			fprintf(stderr, " ");
X		}
X		ll += pl;
X
X		/* show the argument */
X		if (!BITSET(ARGREQ, ad->ad_flags))
X			fprintf(stderr, "[");
X		if (ad->ad_name != ' ')
X		{
X			fprintf(stderr, "-%c", ad->ad_name);
X			if (ad->ad_type != argBool)
X				fprintf(stderr, " ");
X		}
X		if (ad->ad_name == ' ' || ad->ad_type != argBool)
X			fprintf(stderr, "<%s>", ad->ad_prompt);
X		if (!BITSET(ARGREQ, ad->ad_flags))
X			fprintf(stderr, "]");
X		if (ad->ad_flags & ARGLIST)
X			fprintf(stderr, "...");
X	}
X	fprintf(stderr, "\n");
X}
//END_OF_FILE
echo x - syserr.c
sed 's/^X//' > syserr.c << '//END_OF_FILE'
X#include <useful.h>
X#include <funclist.h>
X
XVERSIONID("$Header: syserr.c,v 2.0 89/12/24 00:56:31 eric Exp $");
X
X/*
X**  SYSERR -- indicate a system error
X**
X**	Parameters:
X**		m -- the printf() message to print.
X**		a, b, c, d, e -- parameters to the message
X**
X**	Returns:
X**		never
X**
X**	Side Effects:
X**		Terminates the process if no acceptable recover takes place.
X**
X**	Globals:
X**		SyserrFuncs -- a list of functions to call.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
X#ifdef FUNCLISTSTUFF
XFUNCLIST	*SyserrFuncs =		FUNCLISTNULL;
X#endif
X
XVOID
Xsyserr(m, a, b, c, d, e)
X	char *m;
X{
X	static BOOL exiting = FALSE;
X
X	/* print the error message */
X	_error_message(m, a, b, c, d, e);
X
X	/* if we recursively syserr during exit, drop out now! */
X	if (exiting)
X		_exit(1);
X
X#ifdef FUNCLISTSTUFF
X	/* call any possible cleanup functions */
X	funclist_call(SyserrFuncs, ARBNULL);
X#endif
X
X	/* try a clean exit */
X	exiting = TRUE;
X	exit(1);
X	/*NOTREACHED*/
X}
X/*
X**  USRERR -- print a user error
X**
X**	Parameters:
X**		m -- the printf() message to print.
X**		a, b, c, d, e -- parameters to the message
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		clears the global error number.
X*/
X
X#ifdef FUNCLISTSTUFF
XFUNCLIST	*UsrerrFuncs =		FUNCLISTNULL;
X#endif
X
XVOID
Xusrerr(m, a, b, c, d, e)
X	char *m;
X{
X	extern int errno;
X
X	/* print the error message */
X	_error_message(m, a, b, c, d, e);
X
X#ifdef FUNCLISTSTUFF
X	/* allow cleanup actions */
X	funclist_call(UsrerrFuncs, ARBNULL);
X#endif
X
X	/* give us a clean slate */
X	errno = 0;
X}
X/*
X**  _ERROR_MESSAGE -- generic message printing routine.
X**
X**	Includes the program name with the message, as well as the
X**	various possible error codes.
X**
X**	Parameters:
X**		m -- the printf() message to print.
X**		a, b, c, d, e -- parameters to the message
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X**
X**	Globals:
X**		ProgName -- the name of the program.
X*/
X
XSTATIC VOID
X_error_message(m, a, b, c, d, e)
X	char *m;
X{
X	extern char *ProgName;
X	int saveerr;
X	extern int errno;
X
X	saveerr = errno;
X	if (ProgName != CHARNULL)
X		fprintf(stderr, "%s: ", ProgName);
X	fprintf(stderr, m, a, b, c, d, e);
X	fprintf(stderr, "\n");
X	if (saveerr != 0)
X	{
X		errno = saveerr;
X		perror("System error");
X	}
X}
//END_OF_FILE
echo x - traceset.c
sed 's/^X//' > traceset.c << '//END_OF_FILE'
X#ifdef NODEBUG
X#undef NODEBUG
X#endif
X#define TRACESTUFF
X#include <useful.h>
X#include <ctype.h>
X
XVERSIONID("$Header: traceset.c,v 2.0 89/12/24 00:56:33 eric Exp $");
X
X/*
X**  TRACESET -- set trace flags
X**
X**	Parameters:
X**		s -- the string describing the trace flags desired.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets values in the trace vector.
X**
X**	Globals:
X**		_TraceVect -- used by TRACE and TRACEF to determine the
X**			current trace flag settings.
X**
X**	Syntax:
X**		The argument points to a set of comma separated trace
X**		flag settings.  Each setting is a trace flag name, or
X**		a range of trace flags separated by a hyphen.  The
X**		setting may be followed by a dot and a level.  For
X**		example, "5,23-26.4" sets trace flag 5 to level 1 (the
X**		default), and flags 23 through 26 to level 4.
X**
X**		Trace flags names may be numeric or symbolic.  Symbolic
X**		names are looked up in the file "lib/traceflags", which
X**		is found somewhere in the search path.  This file has a
X**		simple "name <white-space> value" syntax.
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
XSTATIC FILE	*_TraceSymFile =	FILENULL;
XSTATIC BOOL	_TraceSymCant =		FALSE;
Xunsigned char	_TraceVect[_TRACE_SIZE];
X
XVOID
Xtraceset(s)
X	char *s;
X{
X	int lo;
X	int hi;
X	int lev;
X
X	if (s == CHARNULL || *s == '\0')
X		s = "0-999";
X
X	do
X	{
X		/* get the low limit of the range */
X		lo = _tracetok(&s, TRUE);
X		if (lo < 0)
X			lo = 0;
X
X		/* get the high limit of the range */
X		if (*s == '-')
X			hi = _tracetok(&s, TRUE);
X		else
X			hi = lo;
X		if (hi >= _TRACE_SIZE)
X			hi = _TRACE_SIZE - 1;
X
X		/* get the trace level */
X		if (*s == '.')
X			lev = _tracetok(&s, FALSE);
X		else
X			lev = 1;
X		if (lev < 0)
X			lev = 1;
X
X		/* now do the actual setting */
X		while (lo <= hi)
X			_TraceVect[lo++] = lev;
X	} while (*s != '\0');
X
X	if (_TraceSymFile != FILENULL)
X	{
X		fclose(_TraceSymFile);
X		_TraceSymFile = FILENULL;
X	}
X}
X/*
X**  _TRACETOK -- extract a token
X**
X**	Parameters:
X**		sp -- an indirect pointer to the string form.  It is
X**			updated to point to the token terminator.
X**		symok -- if TRUE, a symbolic representation can be
X**			used.
X**
X**	Returns:
X**		The value of the token.
X**		-1 on error.
X**
X**	Side Effects:
X**		none.
X*/
X
XSTATIC int
X_tracetok(sp, symok)
X	char **sp;
X	BOOL symok;
X{
X	register char *s = *sp;
X	register char *tp;
X	char tbuf[MAXWORDLEN + 1];
X	char dbuf[MAXINPUTLINE];
X
X	/* skip leading cruft */
X	while (*s != '\0' && !isalnum(*s))
X		s++;
X
X	/* extract token */
X	for (tp = tbuf; isalnum(*s); *tp++ = *s++)
X		continue;
X	*tp = '\0';
X
X	/* drop trailing cruft */
X	while (isspace(*s))
X		s++;
X	*sp = s;
X
X	/* now decode token */
X	if (isdigit(tbuf[0]))
X		return (atoi(tbuf));
X	if (!symok)
X		return (-1);
X
X	/* look up symbol */
X	if (!_TraceSymCant && _TraceSymFile == FILENULL)
X	{
X		extern FILE *openpath ARGS((char *, char *, char *));
X
X		_TraceSymFile = openpath("lib/traceflags", "r", CHARNULL);
X	}
X	if (_TraceSymFile == FILENULL)
X	{
X		_TraceSymCant = TRUE;
X		return (-1);
X	}
X	rewind(_TraceSymFile);
X
X	/* search through the trace file for a matching line */
X	while (fgets(dbuf, sizeof dbuf, _TraceSymFile) != CHARNULL)
X	{
X		register char *ep;
X		extern char *_tracematch ARGS((char *, char *));
X
X		/* comment? */
X		if (dbuf[0] == '#' || dbuf[0] == '\n')
X			continue;
X
X		/* does it match? */
X		ep = _tracematch(tbuf, dbuf);
X		if (ep != CHARNULL)
X			return (atoi(ep));
X	}
X	return (-1);
X}
X
XSTATIC char *
X_tracematch(tp, dp)
X	register char *tp;
X	register char *dp;
X{
X	for ( ; *tp != '\0'; tp++, dp++)
X	{
X		/* if we have an exact match, continue trying */
X		if (*tp == *dp)
X			continue;
X
X		/* well, let's try a caseless match */
X		if (isupper(*tp) && tolower(*tp) == *dp)
X			continue;
X		if (isupper(*dp) && tolower(*dp) == *tp)
X			continue;
X
X		/* well foo...  I guess it doesn't match */
X		return (CHARNULL);
X	}
X
X	/* check the boundary conditions */
X	if (!isspace(*dp))
X		return (CHARNULL);
X
X	/* drop the trailing white space and return the pointer */
X	while (isspace(*dp))
X		dp++;
X	return (dp);
X}
//END_OF_FILE
echo x - stest.c
sed 's/^X//' > stest.c << '//END_OF_FILE'
X#include <useful.h>
X#include <parseargs.h>
X
XVERSIONID("$Header: stest.c,v 2.0 89/12/24 00:56:29 eric Exp $");
X
X/*
X**  STEST -- a simple test program for the argument parser
X**
X**	Author:
X**		Eric Allman
X**		University of California, Berkeley
X*/
X
Xint	RepCount;
Xchar	*Name;
Xchar	*DirName =	".";
XBOOL	XFlag =		FALSE;
XBOOL	YFlag =		FALSE;
XBOOL	ZFlag =		FALSE;
Xchar	TabChar =	':';
Xstruct arglist *Argv =	NULL;
Xstruct arglist *Groups = NULL;
X
XARGDESC	Args[] =
X{
X	' ',	ARGREQ,		argStr,		__ &Name,	"Name",
X	'n',	ARGOPT|ARGLIST,	listStr,	__ &Groups,	"newsGROUP",
X	'c',	ARGOPT,		argInt,		__ &RepCount,	"REPcount",
X	'd',	ARGOPT,		argStr,		__ &DirName,	"DIRname",
X	'x',	ARGOPT,		argBool,	__ &XFlag,	"Xflag",
X	'y',	ARGOPT,		argBool,	__ &YFlag,	"Yflag",
X	'z',	ARGOPT,		argBool,	__ &ZFlag,	"Zflag",
X	't',	ARGOPT,		argChar,	__ &TabChar,	"TABchar",
X	' ',	ARGOPT|ARGLIST,	listStr,	__ &Argv,	"File",
X	ENDOFARGS
X};
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	parseargs(argv, Args);
X
X	printf("Name = \"%s\", DirName = \"%s\", RepCount = %d,\n",
X		Name, DirName, RepCount);
X	printf("XFlag = %d, YFlag = %d, ZFlag = %d, TabChar=%03o;\n",
X		XFlag, YFlag, ZFlag, TabChar);
X
X	if(Groups) {
X		printf("Newsgroups: ");
X		while(Groups) {
X			printf("%s", L_STRING(Groups));
X			Groups = L_NEXT(Groups);
X			if(Groups)
X				putchar(' ');
X			else
X				putchar('\n');
X		}
X	}
X
X	if(Argv) {
X		printf("Remaining args: ");
X		while(Argv) {
X			printf("%s", L_STRING(Argv));
X			Argv = L_NEXT(Argv);
X			if(Argv)
X				putchar(' ');
X			else
X				putchar('\n');
X		}
X	}
X	exit(0);
X}
//END_OF_FILE
echo x - ckalloc.3
sed 's/^X//' > ckalloc.3 << '//END_OF_FILE'
X.\" $Header: ckalloc.3,v 2.0 89/12/24 00:56:12 eric Exp $
X.TH CKALLOC 3
X.SH NAME
Xckalloc \- allocate memory safely
X.SH SYNOPSIS
Xvoid *ckalloc(size)
X.br
Xunsigned int size;
X.PP
X#include <funclist.h>
X.br
Xextern FUNCLIST *OutOfMemoryFuncs;
X.SH DESCRIPTION
X.I ckalloc
Xallocates memory from the heap in the same manner as
X.IR malloc (3).
XHowever, if sufficient memory is unavailable,
Xrecovery is attempted by calling the list of functions
Xindicated by the
X.I OutOfMemoryFuncs
Xglobal function list
X(see
X.IR funclist (3)
Xfor details).
XAfter these functions are called,
Xthe allocation is retried.
X.PP
XIf the second attempt fails,
X.I ckalloc
Ximmediately terminates the process with a message
Xusing
X.IR syserr (3).
X.SH SEE ALSO
Xmalloc(3),
Xfunclist(3),
Xsyserr(3)
X.SH AUTHOR
XEric Allman, University of California, Berkeley
//END_OF_FILE
echo x - funclist.3
sed 's/^X//' > funclist.3 << '//END_OF_FILE'
X.\" $Header: funclist.3,v 2.0 89/12/24 00:56:22 eric Exp $
X.TH FUNCLIST 3
X.SH NAME
Xfunclist_add, funclist_call \- manipulate lists of function pointers
X.SH SYNOPSIS
X#include <funclist.h>
X.PP
Xvoid funclist_add(flp, func, arg1)
X.br
XFUNCLIST **flp;
X.br
Xvoid (*func)(void *, void *);
X.br
Xvoid *arg1;
X.PP
Xvoid funclist_call(fl, arg2)
X.br
XFUNCLIST *fl;
X.br
Xvoid *arg2;
X.SH DESCRIPTION
XA
X.I "function list"
Xis a linked list of function pointers
Xthat can be invoked at some future time.
XThese are often used to handle exceptional conditions
Xin generic library routines.
XThe implementation is not particularly efficient;
Xthese are intended primarily to allow flexible recovery
Xfrom relatively rare error conditions.
X.PP
XFunction lists are declared using:
X.PP
X	FUNCLIST *FuncListHead = FUNCLISTNULL;
X.PP
XFunctions are added to the list using:
X.PP
X	funclist_add(&FuncListHead, &func, arg1)
X.PP
Xwhere ``func'' is the name of the function to be invoked.
XThe
X.I arg1
Xargument is passed as the first parameter to the function when it is called,
Xand is otherwise uninterpreted.
X.PP
XFunction lists are invoked using:
X.PP
X	funclist_call(FuncListHead, arg2)
X.PP
XFunctions are invoked in the opposite order of their addition
Xto the function list.
XEach function is called as:
X.PP
X	func(arg1, arg2);
X.PP
XThat is, the first argument is from the addition to the list,
Xand the second is from the invocation.
X.SH BUGS
XThere should be some way to remove functions from the list.
X.SH AUTHOR
XEric Allman, University of California, Berkeley.
//END_OF_FILE
echo x - openpath.3
sed 's/^X//' > openpath.3 << '//END_OF_FILE'
X.\" $Header: openpath.3,v 2.0 89/12/24 00:56:24 eric Exp $
X.TH OPENPATH 3
X.SH NAME
Xopenpath \- open a file, searching through a search path
X.SH SYNOPSIS
XFILE *openpath(fname, mode, path)
X.br
Xchar *fname;
X.br
Xchar *mode;
X.br
Xchar *path;
X.PP
Xextern char *DefaultPath;
X.SH DESCRIPTION
X.I openpath
Xopens a file like
X.IR fopen (3),
Xexcept it uses a search path.
XA FILE pointer is returned for the first file matching the name
Xthat could be opened
Xin the search path.
XIf no file name matches,
XFILENULL is returned.
X.PP
XThe
X.I mode
Xis passed directly to
X.IR fopen (3).
X.PP
XThe
X.I path
Xis a colon-separated list of possible directories.
XEach element of the path can begin with a tilde (``~'')
Xto interpolate the
X$HOME
Xenvironment variable.
XZero length entries indicate the current directory.
XIf the path is not specified (CHARNULL),
Xthe external variable
X.I DefaultPath
Xis used.
XThis is set from the
X$ROOTPATH
Xenvironment variable.
XIf this is not set, a system default is used,
Xusually ``/usr/local:/usr:~:''.
X.SH SEE ALSO
Xfopen(3)
X.SH AUTHOR
XEric Allman, University of California, Berkeley
//END_OF_FILE
echo x - parseargs.3
sed 's/^X//' > parseargs.3 << '//END_OF_FILE'
X.\" $Header: parseargs.3,v 2.0 89/12/24 00:56:26 eric Exp $
X.TH PARSEARGS 3
X.SH NAME
Xparseargs, usage \- parse command line argument vectors
X.SH SYNOPSIS
X#include <parseargs.h>
X.PP
Xvoid parseargs(argv, argd)
X.br
Xchar *argvp[];
X.br
XARGDESC argd[];
X.PP
Xvoid usage(argd)
X.SH DESCRIPTION
XGiven a vector of string-valued arguments
Xsuch as that passed to
X.I main
Xand a vector describing the possible arguments,
X.I parseargs
Xmatches actual arguments to possible arguments,
Xconverts values to the desired type,
Xand diagnoses problems such as
Xmissing arguments,
Xextra arguments,
Xand argument values that are syntactically incorrect.
X.PP
XWhen invoked interactively,
Xunder the UNIX operating system and
Xif parseargs has been compiled for interactive operation,
X.I parseargs
Xprompts the user for argument values that are
Xrequired but are not given on the command line,
Xand values that are supplied but syntactically incorrect.
X.PP
XBy default this option is not compiled in to avoid problems in
Xshell scripts, where a program unexpectedly going into interactive
Xmode might tun out to be a little disconcerting.
X.PP
XGiven a description of possible arguments,
X.I usage
Xprints a reasonably friendly version of this description.
X.PP
XThe argument description vector
Xcontains one entry for each possible flag.
XEach entry has five fields:
X.IP name \w'prompt'u+2n
XThe single character name of the associated flag.
XFor example, to indicate that the program is expecting a ``\-x'' flag,
Xthis field would contain \'x\'.
XPositional arguments (those without a ``\-\fIx\fP'' prefix)
Xare indicated by passing a ``space'' character.
X.IP flags
XFlags modifying the semantics of this entry,
XThese should have one of
XARGREQ
Xto indicate a required argument or
XARGOPT
Xto indicate an optional argument.
XARGHIDDEN
Xcan be ``ored'' in to indicate a flag that should not be printed
Xin usage messages \(em
Xfor example, flags intended for internal debugging purposes.
X.IP type
XThe type of the argument.
XThis is actually a pointer to a function, as described below.
XStandard types include
XargBool (Boolean flags),
XargStr (string-valued arguments),
XargList (a LIFO list of string-valued arguments),
XargChar (char-valued arguments),
XargInt (native integer arguments),
XargShort (short integer arguments),
XargLong (long integer arguments),
XargFloat (short floating point arguments),
Xand
XargDouble (long floating point arguments).
X.IP valp
XA pointer to the variable that should receive the converted value.
XSince this is typed as ``pointer to anything'',
Xthis field should be preceded with a double underscore (``_\|_'')
X(a macro defined in parseargs.h)
Xto perform the appropriate type cast.
X.IP prompt
XThe string used when prompting interactively for argument values,
Xand printed in usage messages.
X.PP
XThe list of arguments is terminated using ENDOFARGS.
X.PP
XFor example, consider the description:
X.RS
X.PP
X.nf
Xint	RepCount =	2;
XBOOL	Verbose =	FALSE;
Xchar	*InFile;
Xchar	*OutFile =	CHARNULL;
XBOOL	XRated =	FALSE;
Xstruct namelist *Files = NULL;
X
XARGDESC Args[] =
X{
X	'c',	ARGOPT,		argInt,		__ &RepCount,	"REPcount",
X	'v',	ARGOPT,		argBool,	__ &Verbose,	"Verbose",
X	' ',	ARGREQ,		argStr,		__ &InFile,	"INPUTfile",
X	' ',	ARGOPT,		argStr,		__ &OutFile,	"OUTPUTfile",
X	'X',	ARGHIDDEN,	argBool,	__ &XRated,	"XratedMODE",
X	' ',	ARGOPT,		argList,	__ &Files,	"File",
X	ENDOFARGS
X};
X.fi
X.RE
X.PP
XThis describes a program accepting up to three flag arguments and
Xone or two positional arguments, plus a list of additional file arguments.
XOnly the first positional argument is required.
XThe possible flags (in UNIX) are:
X.TP
X\fB\-c\fP \fIcount\fP
XAn integer repetition count.
XThis defaults to two.
X.TP
X\fB\-v\fP
XA Boolean ``verbose'' flag.
XIt defaults to FALSE.
X.TP
X\fB\-X\fP
XA Boolean ``X Rated'' flag.
XThis is not printed in the usage message.
X.PP
XThe two positional arguments are both strings, as is the final list.
XIn AmigaDOS, the options would be
X\fBREP\fP \fBcount\fP,
X\fBV\fP, and 
X\fXMODE\fP.
X.SH ARGUMENT TYPE FUNCTIONS
XThe argument types recognized by
X.I parseargs
Xcan be extended by adding new type functions.
XArgument type functions are declared as:
X.RS
X.PP
X.nf
XBOOL argXxx(argd, argp, copyf)
X	ARGDESC *argd;
X	char *argp;
X	BOOL copyf;
X.fi
X.RE
X.PP
XThe
X.I argd
Xargument points to the descriptor for the argument being converted.
XIts main use is to find the location in which to store the converted value,
Xlocated in argd\(->ad_valp.
XThe string value to be converted is passed in
X.IR argp .
XThe
X.I copyf
Xflag is TRUE if the
X.I argp
Xstring value must be copied when saved.
XMost non-string types are copied implicitly
X(for example, integer arguments are stored in binary form,
Xso the original string value need not be saved),
Xso this argument can usually be ignored.
XPut simply, this flag is
XTRUE
Xwhen
X.I argp
Xpoints to a temporary buffer area.
X.PP
XThe type function successfully converts the value,
Xit should return
XTRUE.
XOtherwise,
Xit should print a message using
X.IR usrerr (3)
Xand return FALSE. This message should be of the form
X\fB"invalid xxxx option 'yyyy' for Zzzz"\fP, where xxxx is the type of the
Xoption, yyyy is the string passed in vp, and zzzz is the name (taken from
X\fBad->ad_prompt\fP).
X.PP
XFor example, a type function that took a filename
Xand stored an open file pointer might be coded as:
X.RS
X.PP
X.nf
X/*ARGSUSED*/
XBOOL
XargReadFile(argd, argp, copyf)
X	register ARGDESC *argd;
X	register char *argp;
X	BOOL copyf;
X{
X	register FILE *fp;
X
X	fp = fopen(argp, "r");
X	if (fp == NULL)
X	{
X		usrerr("cannot open '%s' for reading as %s",
X			argp, argd->ad_prompt);
X		return (FALSE);
X	}
X	*(FILE *) argd->ad_valp = fp;
X	return (TRUE);
X}
X.fi
X.RE
X.SH SEE ALSO
Xsyserr(3)
X.br
XThe ``C Advisor'' column in
X.ul
XUNIX Review
XVol. 7 No. 11.
X.SH AUTHOR
XEric Allman, University of California, Berkeley
X.SH MODIFICATIONS
XModified to accept a vector of arguments, better error messages,
Xand AmigaDOS version by Peter da Silva.
//END_OF_FILE
echo x - syserr.3
sed 's/^X//' > syserr.3 << '//END_OF_FILE'
X.\" $Header: syserr.3,v 2.0 89/12/24 00:56:30 eric Exp $
X.TH SYSERR 3
X.SH NAME
Xsyserr, usrerr \- display error messages
X.SH SYNOPSIS
Xvoid syserr(msg, arg1, ...)
X.br
Xchar *msg;
X.br
Xvoid *arg1;
X.PP
Xvoid usrerr(msg, arg1, ...)
X.PP
X#include <funclist.h>
X.br
Xextern FUNCLIST *SyserrFuncs;
X.br
Xextern FUNCLIST *UsrerrFuncs;
X.PP
Xextern char *ProgName;
X.SH DESCRIPTION
XThese routines print error messages.
XUnrecoverable errors should be sent using
X.IR syserr ,
Xwhich is guaranteed to never return.
XRecoverable errors can be printed using
X.IR usrerr .
X.PP
XBoth routines label the message with the contents of the
X.I ProgName
Xvariable, which is set by
X.IR parseargs (3)
Xto the name of the invoking program.
XThe
X.I msg
Xand up to five arguments are passed to
X.IR fprintf (3)
Xto be printed.
XThe message should
X.I not
Xcontain a trailing newline;
Xthis is added internally.
XThe contents of the
X.I errno
Xglobal variable is then appended to the message.
X.PP
XAfter printing the message,
X.I syserr
Xinvokes the
X.I SyserrFuncs
Xfunction list.
XA member function can attempt to clean up and return to a top loop
Xusing
X.IR longjmp (3)
Xor some similar mechanism.
XIf the function list returns normally,
Xthe process is terminated.
X.PP
XAfter
X.I usrerr
Xprints the message,
Xit invokes the
X.I UsrerrFuncs
Xfunction list.
XMember functions can use non-local jumps if a ``quick abort'' policy
Xis desired.
XIf this function list returns,
X.I usrerr
Xreturns to the caller.
XThe global
X.I errno
Xvariable is reset to zero.
X.SH BUGS
XShould allow an arbitrary number of arguments.
X.PP
XMessages printed by
X.I syserr
Xshould probably also be logged using
X.IR syslog (3)
Xor the equivalent.
X.PP
XIt is possible to get recursive calls to
X.I syserr
Xif one of the
X.I SyserrFuncs
Xcalls
X.IR syserr .
XSimilar comments apply to
X.IR usrerr .
X.SH SEE ALSO
Xsyslog(3),
Xfunclist(3),
Xlongjmp(3),
Xexit(3)
//END_OF_FILE
echo x - trace.3
sed 's/^X//' > trace.3 << '//END_OF_FILE'
X.\" $Header: trace.3,v 2.0 89/12/24 00:56:31 eric Exp $
X.TH TRACE 3
X.SH NAME
XTRACE, TRACEF, traceset \- trace package
X.SH SYNOPSIS
X#include <useful.h>
X.PP
XBOOL TRACEF(flag, level)
X.br
Xint flag;
X.br
Xint level;
X.PP
XTRACE(flag, level, (\fIprintf arguments\fP));
X.PP
Xvoid traceset(tracestring)
X.br
Xchar *tracestring;
X.SH DESCRIPTION
XThe
X.I traceset
Xroutine and the
X.I TRACE
Xand
X.I TRACEF
Xmacros implement a simple trace flag package.
XA large number of trace flags
X(200 default)
Xcan each be set to a trace level
Xvarying from zero
X(no tracing for this flag)
Xto 255
X(full tracing).
X.PP
XThe
X.I TRACEF
Xmacro tests whether the specified flag is set to at least the indicated level.
XFor example:
X.PP
X	if (TRACEF(24, 3))
X.PP
Xtests flag 24; if it is set to 3 or higher,
Xthe
X.B if
Xstatement succeeds.
X.PP
XThe
X.I TRACE
Xmacro incorporates a
X.IR printf (3)
Xstatement.
XFor example, the code:
X.PP
X	TRACE(24, 3, ("xyzzy = %d\n", xyzzy));
X.PP
XPrints the ``xyzzy'' variable
Xwhen trace flag 24 is set to level 3 or higher.
XThe extra set of parentheses is important.
X.PP
XFlags can be adjusted using the
X.I traceset
Xroutine.
XThis parses the trace string, setting indicated flags as it goes.
XThe string is a comma-separated list of trace settings.
XEach setting has the syntax:
X.PP
X	flag[\-flag][.level]
X.PP
XIf the level is omitted, one is assumed.
XIf only one flag is indicated,
Xit is set individually.
XIf a range of flags is given,
Xall flags inclusively between the two values are set.
XFor example:
X.PP
X	20\-29.2
X.PP
Xsets the ten flags numbered between 20 and 29 to level 2.
X.PP
XFlag names can be symbolic.
XSymbolic names are looked up in the file
X``<searchpath>/lib/traceflags'',
Xwhere
X<searchpath>
Xis the default search path used by
X.IR openpath (3).
XThis file is a series of lines, each containing a flag name,
Xwhite space,
Xand the flag value.
XBlank lines
Xand lines beginning with `#' are ignored.
XCase is ignored in flag names.
X.SH SEE ALSO
Xopenpath(3),
Xparseargs(3)
X.SH AUTHOR
XEric Allman, University of California, Berkeley
//END_OF_FILE
echo "End of archive."
# end of archive.
exit 0
-- 
 _--_|\  Peter da Silva. +1 713 274 5180. <peter@ficc.uu.net>.
/      \
\_.--._/ Xenix Support -- it's not just a job, it's an adventure!
      v  "Have you hugged your wolf today?" `-_-'