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?" `-_-'