[comp.lang.c] Patches to "Parseargs" by Eric Allman

peter@ficc.uu.net (Peter da Silva) (01/30/90)

Archive-name: parseargs/patch.pds

Parseargs is a much better design than getopt, and the first REAL
advancement in parsing C command line arguments since argv[argc]
was changed to NULL. But it needs a little tuning. These patches are
not intended to be comprehensive, but they do fill the main gaps
in the implementation. In particular, they allow you to have a list
of names on the command line.

:
#! /bin/sh
# This is a shell archive, created at Ferranti International Controls Corp.
# by peter (peter da silva,2419T 5180 @xds13) on Mon Jan 29 16:11:55 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.PDS'
# 'patches'
# 'strtol.c'
# To extract them, run the following through /bin/sh
echo x - README.PDS
sed 's/^X//' > README.PDS << '//END_OF_FILE'
XUpdate to parseargs by Peter da Silva (peter@ficc.uu.net).
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. To make it work better, I've made
Xa 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. There's
Xan extra argument, name, that's used by the usage routine and also flags
Xthat trailing arguments are acceptable. parseargs now returns a new argc
Xand edits argv to match.
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 plan on doing an Amiga version, for
Xexample, that uses keyword syntax. In that version, the usage message would
Xread:
X
XUsage: stest [Name] <Name> [RepCount <RepCount>] [DirName <DirName>] \
X	[XFlag] [YFlag] [ZFlag] [<File>]...
X
XInstead of:
X
XUsage: stest <Name> [-c <RepCount>] [-d <DirName>] [-x] [-y] [-z] \
X	 [<File>]...
X
XThis would solve the old problem of UNIX programs sticking out like a
Xsore thumb in other operating systems.
//END_OF_FILE
echo x - patches
sed 's/^X//' > patches << '//END_OF_FILE'
XMakefile orig/Makefile differ: char 70, line 3
X*** orig/Makefile
X--- Makefile
X***************
X*** 1,6 ****
X  # $Header: Makefile,v 2.1 89/12/30 20:59:01 eric Exp $
X  
X! TARGET=		/usr/local
X  INCLUDES=	-I.
X  O=		-O
X  
X--- 1,6 ----
X  # $Header: Makefile,v 2.1 89/12/30 20:59:01 eric Exp $
X  
X! TARGET=		/usr1/local
X  INCLUDES=	-I.
X  O=		-O
X  
X***************
X*** 12,18 ****
X  		openpath.o \
X  		parseargs.o \
X  		syserr.o \
X! 		traceset.o
X  HFILES=		funclist.h \
X  		parseargs.h \
X  		useful.h
X--- 12,19 ----
X  		openpath.o \
X  		parseargs.o \
X  		syserr.o \
X! 		traceset.o \
X! 		strtol.o
X  HFILES=		funclist.h \
X  		parseargs.h \
X  		useful.h
X***************
X*** 35,41 ****
X  
X  OBJS=		${OFILES}
X  SRCFILES=	${XXFILES} ${HFILES} ${CFILES} ${MANFILES}
X! LIBNAME=	libgoodies.a
X  LOCLIBS=	${LIBNAME}
X  SYSLIBS=	-lm
X  LIBS=		${LOCLIBS} ${SYSLIBS}
X--- 36,42 ----
X  
X  OBJS=		${OFILES}
X  SRCFILES=	${XXFILES} ${HFILES} ${CFILES} ${MANFILES}
X! LIBNAME=	libparseargs.a
X  LOCLIBS=	${LIBNAME}
X  SYSLIBS=	-lm
X  LIBS=		${LOCLIBS} ${SYSLIBS}
Xparseargs.c orig/parseargs.c differ: char 317, line 13
X*** orig/parseargs.c
X--- parseargs.c
X***************
X*** 10,19 ****
X  **	Parameters:
X  **		argv -- the argument vector as passed to main().
X  **		argd -- the argument descriptor array.
X  **
X  **	Returns:
X! **		none.  Terminates the process if parameters cannot be
X! **			properly matched up.
X  **
X  **	Side Effects:
X  **		Converts and stores arguments into variables as
X--- 10,21 ----
X  **	Parameters:
X  **		argv -- the argument vector as passed to main().
X  **		argd -- the argument descriptor array.
X+ **		name -- name of extra arguments for usage message,
X+ **			or NULL if none allowed.
X  **
X  **	Returns:
X! **		Modifies argv to eat arguments, returns new argc.
X! **			Exits with return code 2 if error in args.
X  **
X  **	Side Effects:
X  **		Converts and stores arguments into variables as
X***************
X*** 42,48 ****
X  STATIC ARGDESC	_DefaultArgs[] =
X  {
X       /* name	flags	type		valp		prompt		*/
X! 	'T',	ARGOPT,	argTrace,	ARBNULL,	"trace flags",
X  	ENDOFARGS
X  };
X  
X--- 44,50 ----
X  STATIC ARGDESC	_DefaultArgs[] =
X  {
X       /* name	flags	type		valp		prompt		*/
X! 	'T',	ARGOPT,	argTrace,	ARBNULL,	"TRACE",
X  	ENDOFARGS
X  };
X  
X***************
X*** 52,76 ****
X  	ENDOFARGS
X  };
X  
X  VOID
X! parseargs(argv, argd)
X  	char **argv;
X  	ARGDESC argd[];
X  {
X  	register ARGDESC *ad;
X! 	register char **av = argv;
X  	register char *p;
X  	BOOL noflags;
X  	BOOL error;
X  	BOOL shouldprompt;
X  	extern char *getenv ARGS((char *));
X  	extern int isatty ARGS((int));
X  
X  	/* save the name of this program (for error messages) */
X  	ProgName = *av;
X  
X  	/* test to see if we are interactive */
X  	shouldprompt = (BOOL) isatty(0) && (BOOL) isatty(2);
X  
X  	/* allow null argument descriptor */
X  	if (argd == (ARGDESC *) NULL)
X--- 54,90 ----
X  	ENDOFARGS
X  };
X  
X+ static char *Extra;
X+ 
X  VOID
X! parseargs(argv, argd, extra)
X  	char **argv;
X  	ARGDESC argd[];
X+ 	char *extra;
X  {
X  	register ARGDESC *ad;
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++; argc = 1;
X  	/* save the name of this program (for error messages) */
X  	ProgName = *av;
X+ 	Extra = extra;
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***************
X*** 147,154 ****
X  					if (p == CHARNULL || *p == '-')
X  					{
X  						av--;
X! 						usrerr("value required for -%c flag",
X! 							ad->ad_name);
X  						error = TRUE;
X  						break;
X  					}
X--- 161,168 ----
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*** 173,180 ****
X  			}
X  			if (ad->ad_name == '\0')
X  			{
X! 				usrerr("too many positional parameters");
X! 				error = TRUE;
X  				continue;
X  			}
X  
X--- 187,202 ----
X  			}
X  			if (ad->ad_name == '\0')
X  			{
X! 				if(extra==NULL) {
X! 					usrerr("too any arguments");
X! 					error = 1;
X! 					continue;
X! 				}
X! 				/* Compress extra parms back into av */
X! 				if(argv != av)
X! 					*argv = *av;
X! 				argv++;
X! 				argc++;
X  				continue;
X  			}
X  
X***************
X*** 186,191 ****
X--- 208,215 ----
X  		}
X  	}
X  
X+ 	*argv = NULL;
X+ 
X  	/* now rescan for missing required arguments */
X  	for (ALL_AD)
X  	{
X***************
X*** 191,196 ****
X--- 215,221 ----
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***************
X*** 214,219 ****
X--- 239,246 ----
X  					break;
X  				}
X  			}
X+ #endif
X+ 
X  			if (!BITSET(ARGGIVEN, ad->ad_flags))
X  			{
X  				/* still didn't get a value... sigh */
X***************
X*** 219,231 ****
X  				/* still didn't get a value... sigh */
X  				if (ad->ad_name == ' ')
X  				{
X! 					usrerr("value required for <%s> parameter",
X  						ad->ad_prompt);
X  				}
X  				else
X  				{
X! 					usrerr("value required for -%c flag",
X! 						ad->ad_name);
X  				}
X  				error = TRUE;
X  			}
X--- 246,258 ----
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*** 235,242 ****
X  	if (error)
X  	{
X  		usage(argd);
X! 		exit(1);
X  	}
X  }
X  /*
X  **  USAGE -- print a usage message
X--- 262,271 ----
X  	if (error)
X  	{
X  		usage(argd);
X! 		exit(2);
X  	}
X+ 
X+ 	return argc;
X  }
X  /*
X  **  USAGE -- print a usage message
X***************
X*** 319,325 ****
X  		if (!BITSET(ARGREQ, ad->ad_flags))
X  			fprintf(stderr, "]");
X  	}
X! 	fprintf(stderr, "\n");
X  }
X  /*
X  **  ARGtype -- argument translation routines.
X--- 348,362 ----
X  		if (!BITSET(ARGREQ, ad->ad_flags))
X  			fprintf(stderr, "]");
X  	}
X! 	if(Extra) {
X! 		pl = 8 + strlen(Extra);
X! 		if (ll + pl > MaxOutputLine) {
X! 			fprintf(stderr, " \\\n\t");
X! 			ll = 7;
X! 		}
X! 		fprintf(stderr, " [<%s>]...\n", Extra);
X! 	} else
X! 		fprintf(stderr, "\n");
X  }
X  /*
X  **  ARGtype -- argument translation routines.
Xstest.c orig/stest.c differ: char 267, line 15
X*** orig/stest.c
X--- stest.c
X***************
X*** 12,27 ****
X  */
X  
X  int	RepCount;
X! char	*XFlag;
X! char	*FirstArg;
X! BOOL	FFlag =		FALSE;
X  
X  ARGDESC	Args[] =
X  {
X! 	' ',	ARGREQ,		argStr,		__ &FirstArg,	"First Argument",
X! 	'c',	ARGREQ,		argInt,		__ &RepCount,	"Repetition Count",
X! 	'f',	ARGREQ,		argBool,	__ &FFlag,	"F Flag",
X! 	' ',	ARGOPT,		argStr,		__ &XFlag,	"This is a long string, to force wrap",
X  	ENDOFARGS
X  };
X  
X--- 12,31 ----
X  */
X  
X  int	RepCount;
X! char	*Name;
X! char	*DirName =	".";
X! BOOL	XFlag =		FALSE;
X! BOOL	YFlag =		FALSE;
X! BOOL	ZFlag =		FALSE;
X  
X  ARGDESC	Args[] =
X  {
X! 	' ',	ARGREQ,		argStr,		__ &Name,	"Name",
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  	ENDOFARGS
X  };
X  
X***************
X*** 29,37 ****
X  	int argc;
X  	char **argv;
X  {
X! 	parseargs(argv, Args);
X! 	printf("FirstArg = '%s', RepCount = %d, FFlag = %d\n",
X! 		FirstArg, RepCount, FFlag);
X  	TRACE(1, 2, ("hello world\n"));
X  	exit(0);
X  }
X--- 33,56 ----
X  	int argc;
X  	char **argv;
X  {
X! 	int newargc;
X! 
X! 	newargc = parseargs(argv, Args, "File");
X! 	printf("Name = '%s', DirName = '%s', RepCount = %d\n",
X! 		Name, DirName, RepCount);
X! 	printf("XFlag = %d, YFlag = %d, ZFlag = %d\n",
X! 		XFlag, YFlag, ZFlag);
X! 	if(newargc > 1) {
X! 		printf("%d remaining args: ", newargc-1);
X! 		argv++;
X! 		while(*argv) {
X! 			printf("%s", *argv++);
X! 			if(*argv)
X! 				putchar(' ');
X! 			else
X! 				putchar('\n');
X! 		}
X! 	}
X  	TRACE(1, 2, ("hello world\n"));
X  	exit(0);
X  }
Xuseful.h orig/useful.h differ: char 1894, line 117
X*** orig/useful.h
X--- useful.h
X***************
X*** 114,117 ****
X--- 114,122 ----
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 - strtol.c
sed 's/^X//' > strtol.c << '//END_OF_FILE'
X#ifndef BSD
X#include <ctype.h>
X
X#define XX 36
Xstatic char valof[128] = {
X	XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
X	XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
X	XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
X	 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, XX, XX, XX, XX, XX, XX,
X	XX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
X	25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, XX, XX, XX, XX, XX,
X	XX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
X	25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, XX, XX, XX, XX, XX
X};
X
Xlong strtol(str, ptr, base)
Xchar *str;
Xchar **ptr;
Xint base;
X{
X	long digit;
X	char *s;
X	char *p;
X	long val, sign;
X
X	s = str;
X	while(isspace(*s))
X		s++;
X
X	if(*s=='-') {
X		sign = -1L;
X		s++;
X	} else
X		sign = 1L;
X
X	if(base <= 0 || base > 36) {
X		if(s[0]=='0') {
X			if(s[1]=='x' || s[1]=='X')
X				base = 16;
X			else
X				base = 8;
X		} else
X			base = 10;
X	}
X
X	if(base==16 && s[0]=='0' && (s[1] == 'X' || s[1] == 'x'))
X		s += 2;
X
X	val = 0L;
X
X	if(*s > 0 && *s < 128) {
X		digit = valof[*s];
X		if(digit >= base) {
X			if(ptr) *ptr = str;
X			return 0L;
X		}
X	} else {
X		if(ptr) *ptr = str;
X		return 0L;
X	}
X
X	do {
X		val = val * base + digit;
X		s++;
X		digit = valof[*s];
X		if(digit >= base)
X			break;
X	} while(*s > 0 && *s < 128);
X
X	if(ptr) *ptr = s;
X	return val * sign;
X}
X#endif
//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?" `-_-'