brad@hcx1.ssd.csd.harris.com (Brad Appleton) (03/18/91)
Submitted-by: Brad Appleton <brad@hcx1.ssd.csd.harris.com> Posting-number: Volume 17, Issue 47 Archive-name: parseargs/part02 This is part 2 of parseargs #!/bin/sh # this is Part.02 (part 2 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file parseargs/README continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 2; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping parseargs/README' else echo 'x - continuing file parseargs/README' sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/README' && X X The cmd_xxx macros are included only if PARSEARGS_PRIVATE is #defined. X X Similarly, there are also some macros (some public, some private) to X query certain attributes of an argument descriptor (or assist in its X manipulation). The public macros are documented in the manual pages, X private ones are listed here: X X ARG_isBOOLEAN(ad) -- is this arg an argBool, or an arg[STU]Bool type? X ARG_isPSEUDOARG(ad) -- is this arg an argDummy or an argUsage type? X ARG_FIRST(argd) -- return the first argument-entry X ARG_LAST(argd) -- return the first last-entry X ARG_isEND(ad) -- are we at the end of the argument list? X ARG_ADVANCE(ad) -- return the next argument entry. X ARG_RETREAT(ad) -- return the previous argument entry. X X X These last five macros are for traversing all the entries in the X argument descriptor array that correspond to actual command-line X arguments (i.e. the 2nd thru 2nd-to-last entries in the array): X X for ( ad = ARG_FIRST(argd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) ... X or X for ( ad = ARG_LAST(argd) ; !ARG_isEND(ad) ; ARG_RETREAT(ad) ) ... X X X OPERATING SYSTEM DEPENDENCIES X ============================= X I also added "-D${os}_style" to the CFLAGS macro used in the Makefile. X It is used to conditionally include OS dependent code in the parseargs X files. I tried to keep "#ifdef <os>_style" sections of code to a X minimum. X X I tried to make a distinction between portions of the code that depend X upon the underlying OS and portions of the code that depend on the X desired argument parsing style so that - in theory - one could compile X for any of the existing styles on any of the supported systems. Thus, X just because "unix_style" is #defined does not necessarily imply that X the underlying OS is unix. This would only be implied if "unix" was X #defined. X X It is assumed that the following constants are #defined for the X following operating systems: X X NAME OS X ------------------ ------------------------ X unix Unix (BSD or AT&T) X BSD, ucb_universe BSD Unix (only one need be present) X MANX, AZTEC AmigaDOS X MSDOS MS-DOS for IBM PCs X OS2 OS/2 for IBM PCs X vms VAX/VMS X X As of this writing, as long as the above constants are defined for the X corresponding OS, parseargs compiles without errors on both BSD and X AT&T Unix Systems using both ANSI and non-ANSI C Compilers. X X X IBM-PC VERSION OF parseargs(3) X ============================== X I also added ibm_args.c for MS-DOS and OS/2. X X IBM_ARGS.C HAS NOT BEEN TESTED ON AN IBM-PC! I did not have one to X test it on. X X The ibm-pc version is VERY similar to the unix version. The difference X is that ibm_args.c will look in $SWITCHAR for the option character(s) X to use. If the option character is '-', it behaves just like X unix_args.c, if the option character is something else or $SWITCHAR is X undefined then it behaves more like normal MS-DOS stuff. The main X difference is that if the ibm-pc version is NOT emulating unix, than X all option arguments MUST be in the same argument as the option itself X and they must be separated by an '=' character (so "/S=str" is fine X but "/Sstr" and "/S str" are not). X X By default, if SWITCHAR is undefined then both the long and short X option prefix character are '/'. One is able to distinguish an option X from a long-option in this case because the third character of an X option-arg will always be '=' or ' '. Hence, using the test program X with the defaults, both "ibm_test foo /D=directory" and "ibm_test X /DIR=directory" are equivalent. X X X VAX/VMS VERSION OF parseargs(3) X =============================== X I also added vms_args.c for VAX/VMS. X X VMS_ARGS.C HAS NOT BEEN TESTED ON A VMS SYSTEM!!! I did not have one X to test it on. It should accept command-line arguments as described in X the "Grammar Rules" section of the VAX manual but I cant guarantee X anything so you'll have to test it out for yourself. X X ARGLIST and ARGVEC are comma-separated lists in the VMS version of X parseargs (not whitespace separated lists). In order to preserve a X one-to-one mapping between UNIX & AmigaDOS whitespace separated lists X with VMS comma-separated lists, a VMS ARGLIST or ARGVEC that X corresponds to a positional parameter may use both commas and white- X space to separate its arguments. This avoids having VMS command lines X like the following: X X cmdname file1,file2,file3 directory1,directory2 X X for which there exists no corresponding command-line for UNIX or or X AmigaDOS programs without changing the standard command-line syntax X for these systems. X X In addition to a help option in the default argument descriptor, The X VMS version of parseargs(3) also has /OUTPUT, /INPUT, and /ERROR X qualifiers in the standard default argument-descriptor array. These X all serve to redirect stdin, stdout, or stderr to or from a file (many X thanks to posters from comp.os.vms for giving me a clue on how to do X this). As a result of this, under VAX/VMS, there are two new argtype X functions "argInput" and "argOutput": each requires that ad->ad_valp X be a file pointer (not a pointer to a file pointer as in "__ &stdin" X but an actual file pointer as in "__ stdin"). ArgInput and argOutput X close the stream associated with the given file-pointer and reconnect X the stream to the named file for input or output (so the effect is to X redirect input (output) from (to) the stream to the named file. If X redirection fails, the original stream remains closed (sorry -- its a X side-effect of freopen()). X X One can implement a "negatable" vms qualifier by using two entries in X the argument descriptor table as follows: X X 'F', ARGOPT, argSBool, __ &myflag, "FLAG {set flag}", X 'f', ARGOPT, argUBool, __ &myflag, "NOFLAG {unset flag}", X X so that /FLAG will turn the flag on (via argBool or argSBool) and X /NOFLAG will turn it off (via argUBool). X X I did not know what to do (if anything) to add the VAX/VMS shell (DCL) X into the parseargs command-line interface (parseargs(1)) so it is not X currently implemented. I will leave the task of configuring X parseargs(1) for DCL programmers to some other brave soul who knows X more than I about DCL! I was thinking that for DCL, the parseargs X command could directly set the value of a symbol (with the proper X scope of course) at execution time instead of printing something on X stdout that would be evaluated later (as the UNIX version does). X X Anyone who uses VAX/VMS is strongly encouraged to test vms_args.c on X their system (and make changes if need be) and to modify the usage and X parsing functions accordingly!!! It would probably be a good idea to X use some builtin VMS system call to replace the method used for X finding the basename of a file (basename() in strfuncs.c) when "#ifdef X vms" is true. There are also some command-line parsing routines X available through DCL that could replace a lot of the guts of X vms_args.c as well. X X X LITERATE PROGRAMMING X ==================== X If you look at the source code you will notice that it contains lots X of funny looking comments with sections that have titles enclosed X between '^' and ':' characters. This is my own feeble attempt at X literate programming. I have a Unix-shell script which will extract X certain portions of these "structured" comments so that I may dump X them directly into the documentation (and thus attempt to keep the X documentation up-to-date at the same rate as the source). If anyone X is interested in my script(s) please let me know and I will gladly e- X mail them to the interested parties. X X X ACKNOWLEDGEMENTS X ================ X I was in constant contact with Peter Da Silva during the entire period X that I implemented all of the above modifications and would like to X thank him for his time and his sage advice. X X Thanx also to Jim Barbour for helping me with some VMS specific things X (like getting the original, unparsed command-line from DCL and X retreiving the value of a symbol), and to Tom Christiansen and Raymond X Chen for their help in getting parseargs(1) to work for perl scripts. X X Lastly, thanks to all those who use and will continue to improve X parseargs, all I ask is that you keep me updated of your efforts (so I X can keep my own version of parseargs up-to-date). I am always eager X to discuss possible changes or enhancements with any interested X parties. X SHAR_EOF echo 'File parseargs/README is complete' && chmod 0664 parseargs/README || echo 'restore of parseargs/README failed' Wc_c="`wc -c < 'parseargs/README'`" test 41278 -eq "$Wc_c" || echo 'parseargs/README: original size 41278, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/amiga_args.c ============== if test -f 'parseargs/amiga_args.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/amiga_args.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/amiga_args.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/amiga_args.c' && /************************************************************************* ** ^FILE: amiga_args.c - parse AmigaDOS argument vectors ** ** ^DESCRIPTION: ** This file contains the routines used to parse AmigaDOS argument ** vectors and to print AmigaDOS usage messages. ** ** ^HISTORY: ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com> ** - Added structured block comments ** - Added optional arguments to keywords ** ** --/--/-- Peter da Silva <peter@ferranti.com> Created ***^^**********************************************************************/ X #include <ctype.h> #include <useful.h> #include "strfuncs.h" #include "pgopen.h" X #define PARSEARGS_PRIVATE /* include private definitions */ #include "parseargs.h" X EXTERN VOID syserr ARGS((const char *, ...)); EXTERN VOID usrerr ARGS((const char *, ...)); EXTERN char *getenv ARGS((const char *)); EXTERN VOID get_winsize ARGS((int, int *, int *)); X VERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $"); X /*************************************************************************** ** ^GLOBAL-VARIABLE: Usage_Requested ** ** ^VISIBILITY: ** static-global (visible to all functions in this file). ** ** ^DESCRIPTION: ** Indicates whether a usage message was requested by the user ** (as opposed to triggerred by a syntax error). If the message ** is requested by the user then it is always printed in verbose ** mode and does not return an error-status-code. ***^^**********************************************************************/ static BOOL Usage_Requested = FALSE; X X /*************************************************************************** ** ^FUNCTION: amiga_parse - parse Amiga_DOS arg-vectors ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X int amiga_parse( argv, argd ) /* ** ^PARAMETERS: */ X char *argv[]; /* -- the vector of string arguments from the command-line */ X ARGDESC argd[]; /* -- the programmer description of the command and its args */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Amiga_parse will parse the arguments in the given vector of strings, ** assign the corresponding values to the command-line arguments specified ** in argd, and check the syntax of the command-line. ** ** ^REQUIREMENTS: ** The final element in argv must be a NULL pointer. ** ** ^SIDE-EFECTS: ** argd is modified according to the command-line description and parameters ** ** ^RETURN-VALUE: ** pe_SUCCESS (0) if no errors are encountered ** pe_SYSTEM (-1) if a system error is encountered ** pe_SYNTAX if a syntax error is encountered ** ** ^ALGORITHM: ** - for each command-line argument ** - attempt to match the argument as a keyword ** - if it is a keyword argument ** - record and convert its value (if any) ** else it is a positional parameter ** - record and convert its value (if any) ** else there are too many arguments ** - return pe_SYNTAX ** end-if ** end-for ***^^**********************************************************************/ #ifdef __ANSI_C__ X int amiga_parse( char **argv, ARGDESC argd[] ) #endif { X register ARGDESC *cmd, *args, *ad = ARGDESCNULL; X register char **av; X register char *p = CHARNULL; X argName_t keyword; X argMask_t flags; X int parse_error = pe_SUCCESS; X BOOL is_match = FALSE; X X if ( !argd ) return parse_error; X X /* initialize command-structure */ X if ( !CMD_isINIT(argd) ) init_args( argd ); X cmd = argd; X cmd_prev(cmd) = ARGDESCNULL; X X /* run through the argument vector */ X for ( av = argv ; *av ; av++ ) { X char c = '\0'; X X /* If looking for keywords, see if this is one */ X if( !BTEST(cmd_state(cmd), ps_NOFLAGS) ) { X p = strpbrk(*av, s_ARG_SEP); X if ( p ) { X c = *p; X *p++ = '\0'; /* skip past arg-separator character */ X } X X is_match = FALSE; X for ( args = argd ; args && !is_match ; args = cmd_defargs(args) ) { X for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) { X if (arg_type(ad) == argDummy) continue; X X if (!ARG_isPOSONLY(ad) && match(*av, arg_sname(ad)) == 0) { X is_match = TRUE; X break; X }/*if*/ X } X } X X if ( !is_match ) ad = ARGDESCNULL; X }/*if !NOFLAGS*/ X X if (c) *(p-1) = c; /* restore the equal sign */ X X /* If we have a keyword here */ X if( !BTEST(cmd_state(cmd), ps_NOFLAGS) && ad) { X if ( cmd_prev(cmd) ) { /* a value may have been given but wasnt */ X if ( ARG_isVALOPTIONAL(cmd_prev(cmd)) ) { X BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN ); X } X else { /* value was required */ X (VOID)get_keyword( arg_sname(cmd_prev(cmd)), keyword ); X usrerr( "value required for %s keyword", keyword ); X parse_error = pe_SYNTAX; X } X cmd_prev(cmd) = ARGDESCNULL; X } X X if ( cmd_list(cmd) ) { /* end of list */ X cmd_list(cmd) = ARGDESCNULL; X } X X flags = arg_flags(ad); /* save flags */ X if ( ARG_isGIVEN(ad) ) /* reset flags for this appearance */ X BCLEAR( arg_flags(ad), ARGVALGIVEN | ARGVALSEP ); X X if ( p ) { /* matched NAME=VALUE */ X if ( ARG_isMULTIVAL(ad) ) X cmd_list(cmd) = ad; X else X cmd_list(cmd) = ARGDESCNULL; X X /* try to convert the type */ X if ( !HANDLE(ad, p, cmd_flags(cmd)) ) { X arg_flags(ad) = flags; /* restore flags */ X parse_error = pe_SYNTAX; X } X else X BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN ); X ad = ARGDESCNULL; X } X else { X if (arg_type(ad) == argUsage) { X Usage_Requested = TRUE; X usage(argd); X exit(1); X } X else if (arg_type(ad) == argEnd) { X BSET( cmd_state(cmd), ps_NOFLAGS ); X BSET( arg_flags(ad), ARGGIVEN ); X } X else if ( ARG_isVALTAKEN(ad) ) { X cmd_prev(cmd) = ad; X } X else if ( !HANDLE(ad, CHARNULL, cmd_flags(cmd)) ) { X arg_flags(ad) = flags; /* restore flags */ X parse_error = pe_SYNTAX; X } X else { X BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN ); X } X ad = ARGDESCNULL; X }/*else*/ X } X else if (cmd_prev(cmd)) { X flags = arg_flags(cmd_prev(cmd)); /* save flags */ X if ( ARG_isGIVEN(cmd_prev(cmd)) ) /* reset flags */ X BCLEAR( arg_flags(cmd_prev(cmd)), ARGVALGIVEN | ARGVALSEP ); X X /* previous value may have required a keyword */ X BSET( arg_flags(cmd_prev(cmd)), ARGVALSEP ); X X if ( ARG_isMULTIVAL(cmd_prev(cmd)) ) X cmd_list(cmd) = cmd_prev(cmd); X else X cmd_list(cmd) = ARGDESCNULL; X X /* try to convert the type */ X if ( !HANDLE(cmd_prev(cmd), *av, cmd_flags(cmd)) ) { X arg_flags(cmd_prev(cmd)) = flags; /* restore flags */ X parse_error = pe_SYNTAX; X } X else X BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN | ARGVALGIVEN ); X X ad = ARGDESCNULL; X cmd_prev(cmd) = ARGDESCNULL; X continue; X } X else { /* it's a positional argument or a list item */ X if (cmd_list(cmd)) { /* its a list item */ X flags = arg_flags(cmd_list(cmd)); /* save flags */ X if ( ARG_isGIVEN(cmd_list(cmd)) ) /* reset flags */ X BCLEAR( arg_flags(cmd_list(cmd)), ARGVALGIVEN | ARGVALSEP ); X X BSET( arg_flags(cmd_list(cmd)), ARGVALSEP ); X X if ( !HANDLE(cmd_list(cmd), *av, cmd_flags(cmd)) ) { X arg_flags(cmd_list(cmd)) = flags; /* restore flags */ X parse_error = pe_SYNTAX; X } X X BSET( arg_flags(cmd_list(cmd)), ARGGIVEN | ARGVALGIVEN ); X continue; X } X else { /* its a positional argument */ X if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) ) X BSET( cmd_state(cmd), ps_NOFLAGS ); X X is_match = FALSE; X for ( args = argd; args && !is_match; args = cmd_defargs(args) ) { X for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) { X if (arg_type(ad) == argDummy) continue; X X if ( ARG_isPOSITIONAL(ad) && X (!ARG_isGIVEN(ad) || ARG_isMULTIVAL(ad)) ) { X is_match = TRUE; X break; X }/*if*/ X } X } X X if ( !is_match ) { X usrerr("too any arguments"); X parse_error = pe_SYNTAX; X ad = ARGDESCNULL; X } X else { X flags = arg_flags(ad); /* save flags */ X if ( ARG_isGIVEN(ad) ) /* reset flags for this appearance */ X BCLEAR( arg_flags(ad), ARGVALGIVEN | ARGVALSEP ); X X BSET( arg_flags(ad), ARGVALSEP ); X X /* try to convert */ X if ( !HANDLE(ad, *av, cmd_flags(cmd)) ) { X arg_flags(ad) = flags; /* restore flags */ X parse_error = pe_SYNTAX; X } X else X BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN ); X ad = ARGDESCNULL; X } X }/*else positional*/ X }/*else not keyword*/ X }/*while*/ X X /* If last argument was a keyword and required an option X ** then complain about it X */ X if ( cmd_prev(cmd) ) { /* a value may have been given but wasnt */ X if ( ARG_isVALOPTIONAL(cmd_prev(cmd)) ) { X BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN ); X } X else { /* value was required */ X (VOID)get_keyword( arg_sname(cmd_prev(cmd)), keyword ); X usrerr( "value required for %s keyword", keyword ); X parse_error = pe_SYNTAX; X } X cmd_prev(cmd) = ARGDESCNULL; X } X X return parse_error; } X X /*************************************************************************** ** ^FUNCTION: fmtarg - format command-argument syntax ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static int fmtarg(ad, buf) /* ** ^PARAMETERS: */ X ARGDESC *ad; /* -- pointer to the argument to format */ X char *buf; /* -- character buffer to hold the formatted result */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Fmtarg will determine the proper command-line syntax for the ** given argument and write the result to the given buffer. ** ** ^REQUIREMENTS: ** buf must be large enough to hold the formatted result (100 characters ** should do the trick). ** ** ^SIDE-EFECTS: ** buf is overwritten. ** ** ^RETURN-VALUE: ** The number of printable characters in the argument-syntax-string ** ** ^ALGORITHM: ** Print argument usage based on whether or not the argument is ** positional, hidden, multi-valued (list or vector), etc .... ** Optional arguments and values are enclosed in square braces. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static int fmtarg( const ARGDESC *ad, char *buf ) #endif { X /* buf must already be large enough */ X char * pos; X argName_t keyword, name; X X (VOID) get_name(arg_sname(ad), name); X X if ( ARG_isPOSITIONAL(ad) ) { X sprintf( buf, "<%s>", name ); X } X else { X (VOID) get_keyword(arg_sname(ad), keyword); X (VOID) strcpy( buf, keyword ); X pos = buf + strlen(buf); X X if ( ARG_isVALTAKEN(ad) && !ARG_isBOOLEAN(ad) && !ARG_isPSEUDOARG(ad) ) { X if ( ARG_isVALOPTIONAL(ad) ) X sprintf( pos, " [<%s>]", name ); X else X sprintf( pos, " <%s>", name ); X } X }/*else*/ X X return strlen(buf); } X X /*************************************************************************** ** ^FUNCTION: amiga_usage - print a usage message ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X VOID amiga_usage( argd, usage_flags ) /* ** ^PARAMETERS: */ X ARGDESC *argd; /* -- the command-descriptor array */ X argMask_t usage_flags; /* -- flags set by $USAGECNTL */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Amiga_usage will print the AmigaDOS command-line usage of the given ** command on standard diagnostic output (stderr). The content of the ** usage message is controlled by the bitmasks in usage_flags which ** correspond to the settings in the user's USAGECNTL variable. ** ** ^REQUIREMENTS: ** argd should be a non-null command-line argument-descriptor array ** ** ^SIDE-EFECTS: ** Prints on stderr. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - if no usage is desired then exit ** - if paging is requested print to the pager instead of stderr ** - print the command-line syntax ** - if the description is requested print it ** - if verbose mode is requested, print the description of each argument ***^^**********************************************************************/ #ifdef __ANSI_C__ X void amiga_usage( const ARGDESC *argd, argMask_t usage_flags ) #endif { X register CONST ARGDESC *ad, *args, *cmd; X int max_cols = 80, max_lines = 24; X int margin, ll, pl, keywords, longest, positionals; X BOOL first = TRUE; X FILE *fp; X X if ( !argd ) return; X X /* initialize command-structure */ X if ( !CMD_isINIT(argd) ) init_args( (ARGDESC *)argd ); X cmd = argd; X X /* force verbose-mode if requested */ X if ( Usage_Requested ) BSET( usage_flags, usg_VERBOSE ); X X if ( BTEST(usage_flags, usg_NONE) ) return; X X fp = ( BTEST(usage_flags, usg_PAGED) ) X ? pgopen( stderr, getenv("USAGE_PAGER") ) X : stderr; X X /* get screen size */ X get_winsize( fileno(stderr), &max_lines, &max_cols ); X X fprintf(fp, "Format: %s", ProgName); X ll = strlen( ProgName ) + 8; X margin = ll + 1; X longest = 0; X X for ( positionals = 0 ; positionals < 2 ; positionals++ ) { X for ( args = argd ; args ; args = cmd_defargs(args) ) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X argName_t buf; X X /* don't display hidden arguments */ X if ( ARG_isHIDDEN(ad) ) continue; X if ( !positionals && ARG_isPOSITIONAL(ad) ) continue; X if ( positionals && !ARG_isPOSITIONAL(ad) ) continue; X X /* figure out how wide this parameter is (for printing) */ X pl = fmtarg(ad, buf); X X if ( pl > longest) longest = pl; X X if ( !ARG_isREQUIRED(ad) ) { X pl += 2; /* [] */ X } X if ( ARG_isMULTIVAL(ad) ) { X strcat( buf, "..." ); X pl += 3; X } X X /* see if this will fit */ X if ( (ll + pl + 1) > (max_cols - first) ) { X /* no... start a new line */ X fprintf(fp, "\n%*s", margin, ""); X ll = margin; X } X else { X /* yes... just throw in a space */ X fputc(' ', fp); X ++ll; X } X ll += pl; X X /* show the argument */ X if ( !ARG_isREQUIRED(ad) ) fputc('[', fp); X fprintf(fp, buf); X if ( !ARG_isREQUIRED(ad) ) fputc(']', fp); X X first = FALSE; /* not first line anymore */ X }/*for each ad */ X }/* for each argd */ X }/* for each parm-type */ X X fputc('\n', fp); X X if ( BTEST(usage_flags, usg_DESCRIPTION) ) { X CONST char *description = cmd_description(cmd); X X if ( description && *description ) { X fprintf( fp, "Description:\n" ); X indent_para(fp, max_cols, 8, "", 0, description); X fputc( '\n', fp ); X } X }/*if*/ X X if ( !BTEST(usage_flags, usg_VERBOSE) ) { X if ( pgactive(fp) ) (VOID) pgclose( fp ); X return; X } X X keywords = 0; X for ( positionals = 0 ; positionals < 2 ; positionals++ ) { X for ( args = argd ; args ; args = cmd_defargs(args) ) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X argName_t buf; X X /* don't display hidden arguments */ X if ( ARG_isHIDDEN(ad) ) continue; X if ( !positionals && ARG_isPOSITIONAL(ad) ) continue; X if ( positionals && !ARG_isPOSITIONAL(ad) ) continue; X X if( !(keywords++) ) fprintf(fp, "Keywords/Arguments:\n"); X (VOID) fmtarg(ad, buf); X indent_para(fp, max_cols, 8, buf, longest+2, arg_description(ad)); X }/*for each ad */ X }/* for each argd */ X }/* for each parm-type */ X X if ( pgactive(fp) ) (VOID) pgclose( fp ); } SHAR_EOF chmod 0664 parseargs/amiga_args.c || echo 'restore of parseargs/amiga_args.c failed' Wc_c="`wc -c < 'parseargs/amiga_args.c'`" test 16890 -eq "$Wc_c" || echo 'parseargs/amiga_args.c: original size 16890, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/arglist.c ============== if test -f 'parseargs/arglist.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/arglist.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/arglist.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/arglist.c' && /************************************************************************* ** ^FILE: arglist.c - argList manipulation routines. ** ** ^DESCRIPTION: ** This file contains routines to add an item to the end of an arglist, ** and to delete all items in an arglist. ** ** ^HISTORY: ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com> ** - Added structured comments ** - Changed arglists to always be kept in FIFO order (hence ** reverse_list() and cleanup_list() were no longer needed). ** The lists are maintained in FIFO order by forcing the very ** first item of the list to maintain an additional link to the ** last item of the list (to make it easy to append an item). ** - Added listFree() function ** ** --/--/-- Peter da Silva <peter@ferranti.com> Created ***^^**********************************************************************/ X #include <stdio.h> #include <ctype.h> #include <useful.h> #include "strfuncs.h" X #define PARSEARGS_NARGTYPES /* exclude arg-type externs */ #include "parseargs.h" X EXTERN VOID syserr ARGS((const char *, ...)); EXTERN VOID usrerr ARGS((const char *, ...)); X /*************************************************************************** ** ^FUNCTION: listStr - string-list argument translation routine ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X BOOL listStr( ad, vp, copyf ) /* ** ^PARAMETERS: */ X ARGDESC *ad; /* -- the argument descriptor for this parameter. */ X char *vp; /* -- a pointer to the string input value. */ X BOOL copyf; /* -- if TRUE, the value will be destroyed later, and so should ** be copied if it will be retained (as for a string). */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** ListStr converts a string-parameter value into its internal form, ** including validity checking. If <copyf> is TRUE then <vp> is copied ** to the end of the list, otherwise <vp> itself is placed at the end. ** ** ^REQUIREMENTS: ** <ad> must point to the argdesc element corresponding to the matched ** string-list argument. The ad_valp field of ad MUST be either NULL or ** point to a valid arglist-head structure. ** ** ^SIDE-EFECTS: ** If successful, arglist pointed to by arg_valp(ad) is appended with ** the given string, <vp> is unchanged. ** ** ^RETURN-VALUE: ** TRUE -- if the conversion was successful. The actual ** value should be added appended to the list. ** FALSE -- if the conversion failed. The reason for failure ** should be diagnosed using usrerr(). ** ** ^ALGORITHM: ** - verify the validity of <vp> as a string argument ** - if ( isEmpty(arglist) ) ** - allocate a listhead structure ** - assign the item pointer to <vp> (or a copy of <vp>) ** - assign the NEXT and LAST elements to NULL ** - else ** - allocate a new arglist structure and append it after ** listhead.LAST ** - assign listhead.LAST to the address of the new item ** - assign the NEXT element of the new item to NULL ** end-if ***^^**********************************************************************/ #ifdef __ANSI_C__ X BOOL listStr( ARGDESC *ad, char *vp, BOOL copyf ) #endif { X char *cp; X BOOL badalloc = FALSE; X argName_t argname; X ArgListHead *nl; X ArgList *nd; X X (VOID) get_name( arg_sname(ad), argname ); X if (copyf) { X register int i; X X i = strlen(vp) + 1; X cp = (char *) malloc(i * sizeof(char)); X if(!cp) { X usrerr("out of memory saving string %s", argname ); X return FALSE; X } X memcpy(cp, vp, i); X } X else { X cp = vp; X } X X /* if list is empty - need to new up a listhead, X ** otherwise just use a normal link. X */ X nl = *((ArgListHead **) arg_valp(ad)); X if ( nl ) { X nd = (ArgList *) malloc( sizeof(ArgList) ); X if ( !nd ) badalloc = TRUE; X } X else { X nl = (ArgListHead *) malloc( sizeof(ArgListHead) ); X nd = (ArgList *)NULL; X if ( !nl ) badalloc = TRUE; X } X X if ( badalloc ) { X usrerr("out of memory saving arg %s", arg_sname(ad)); X if(copyf) free(cp); X return FALSE; X } X X if ( nd ) { X nl -> nl_tail -> nl_next = (ArgList *)nd; X nl -> nl_tail = (ArgList *)nd; X nd -> nl_next = (ArgList *)NULL; X nd -> nl_val = (ARBPTR)cp; X nd -> nl_flags = arg_flags(ad); X if ( copyf ) BSET( nd->nl_flags, ARGCOPYF ); X } X else { X *((ArgListHead **) arg_valp(ad)) = nl; X nl -> nl_tail = (ArgList *)nl; X nl -> nl_next = (ArgList *)NULL; X nl -> nl_val = (ARBPTR)cp; X nl -> nl_flags = arg_flags(ad); X if ( copyf ) BSET( nl->nl_flags, ARGCOPYF ); X } X X return (TRUE); } X X /*************************************************************************** ** ^FUNCTION: listFree - free the items in an arglist ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X VOID listFree( argls ) /* ** ^PARAMETERS: */ X ArgList *argls; /* -- the list to be freed */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** ListFree will free storage for each node in <argls>. Furthermore, ** if <copyf> is true then the actual storage for each item in the ** list is also released. ** ** ^REQUIREMENTS: ** argls must point to a valid arglist-head structure. ** ** ^SIDE-EFECTS: ** each item in argls is removed, argls itself should be set to NULL ** after this routine is invoked. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - For each node in argls ** - if ( copyf ) free the item storage ** - free the node ** end-for ***^^**********************************************************************/ #ifdef __ANSI_C__ X void listFree( ArgList *argls ) #endif { X register ArgList *ls = argls; X ArgList *nd; X X if ( !ls ) return; X X while ( ls ) { X nd = L_NEXT(ls); X if ( BTEST(L_FLAGS(ls), ARGCOPYF) ) free( ls->nl_val ); X free( ls ); X ls = nd; X }/*while*/ } X SHAR_EOF chmod 0664 parseargs/arglist.c || echo 'restore of parseargs/arglist.c failed' Wc_c="`wc -c < 'parseargs/arglist.c'`" test 5961 -eq "$Wc_c" || echo 'parseargs/arglist.c: original size 5961, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/argtype.c ============== if test -f 'parseargs/argtype.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/argtype.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/argtype.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/argtype.c' && /************************************************************************* ** ^FILE: argtype.c - argument type definitions for parseargs(3) ** ** ^DESCRIPTION: ** This file implements the argument conversion functions for ** converting string, character, integer, floating-point, ** boolean, and pseudo-arguments, from command-line strings. ** ** ^HISTORY: ** 01/03/91 Brad Appleton <brad@ssd.csd.harris.com> ** - Added structured block comments ** - Added argUsage & argDummy dummy-functions ** - Added argSBool, argTBool, and argUBool ** - Added ARGVEC handling to all necessary functions ** - Added argInput & argOutput for VMS ** - put floating-point routines (argFloat & argDouble) into ** this file (they may be excluded by #defining NOFLOAT) ** - changed routines to return negative values (where appropriate) ** ** --/--/-- Peter da Silva <peter@ferranti.com> ** ** --/--/-- Eric P. Allman <eric@Berkeley.EDU> Created ***^^**********************************************************************/ X #include <ctype.h> #include <useful.h> #include "strfuncs.h" X #define PARSEARGS_NARGTYPES /* exclude arg-type externs */ #include "parseargs.h" X #ifdef __ANSI_C__ # define PARMS(ad,vp,copyf) \ X ( register ARGDESC *ad, register char *vp, BOOL copyf ) #else # define PARMS(ad,vp,copyf) \ X ( ad, vp, copyf ) register ARGDESC *ad; register char *vp; BOOL copyf; #endif X #define REALLOC(ptr,size) (( ! ptr ) ? malloc(size) : realloc(ptr, size) ) EXTERN VOID syserr ARGS((const char *, ...)); EXTERN VOID usrerr ARGS((const char *, ...)); EXTERN long strtol ARGS((char *, char **, int)); EXTERN double strtod ARGS((const char *, char **)); X X /*************************************************************************** ** ^FUNCTION: argtype -- argument translation routines. ** ** ^SYNOPSIS: ** BOOL argUsage( ad, vp, copyf ) ** BOOL argEnd( ad, vp, copyf ); ** BOOL argDummy( ad, vp, copyf ); ** BOOL argBool( ad, vp, copyf ); ** BOOL argSBool( ad, vp, copyf ); ** BOOL argUBool( ad, vp, copyf ); ** BOOL argTBool( ad, vp, copyf ); ** BOOL argChar( ad, vp, copyf ); ** BOOL argStr( ad, vp, copyf ); ** BOOL argInt( ad, vp, copyf ); ** BOOL argShort( ad, vp, copyf ); ** BOOL argLong( ad, vp, copyf ); ** BOOL argFloat( ad, vp, copyf ); ** BOOL argDouble( ad, vp, copyf ); ** BOOL argInput( ad, vp, copyf ); ** BOOL argOutput( ad, vp, copyf ); ** ** ^PARAMETERS: ** ARGDESC *ad; ** -- the argument descriptor for this parameter. ** ** char *vp; ** -- a pointer to the string input value. ** ** BOOL copyf; ** -- if TRUE, the value will be destroyed later, and so should be copied ** if it will be retained (as for a string). ** ** ^DESCRIPTION: ** Each of these converts a parameter value to the internal form, includ- ** ing validity checking. Their parameters and return values all behave ** similarly. One of these routines are called when an argunent of that ** particular type is matched by one of the argument parsing function in ** parseargs(3). When such an argument is matched, its argument transla- ** tion routines is invoked and is passed (1) the address of the argument ** descriptor for the matched argument, (2) the possible argument string ** for that matched argument, and (3) a boolean filed that is TRUE only ** if the second parameter points to temporary storage (indicating that ** some copying may need to be done instead of just pointing to the same ** object). ** ** Once the argument translation routine is invoked, it is responsible ** for converting the argument string to the desired internal form ** (perhaps a number), and assigning the resultant value to the ** arg_valp(ad) field of the argument descriptor (this includes handling ** any necessary (re)allocation if the matched argument has the ARGVEC ** flag enabled). If the argument is an ARGVEC or ARGLIST then the rou- ** tine is responsible for allocating any space, copying the arg-flags to ** the value-specific flags, and setting the ARGCOPYF flag for the value ** if it needs to be allocated as well. ** ** ^REQUIREMENTS: ** ARGKEYWORD should be set if the argument was matched via its ** string name (as opposed to by its character name). ** ** ARGVALSEP should be set is the argument value was in a separate ** argv element from the argument string-name (or character name). ** ** ^SIDE-EFFECTS: ** The value used should be stored in the location indicated by arg_valp(ad). ** ** ^RETURN-VALUE: ** TRUE : if the conversion was successful and the entire value was used. ** ** FALSE : if the conversion failed. The reason for failure should be ** diagnosed using usrerr(). ** ** -N : if the conversion was successful but only N characters of the value ** were used, the remaining characters may still match other arguments. ** ** ^ALGORITHM: ** Function-specific, but the basic idea is as follows: ** ** - convert the value-string into the desired type ** - if the value is invalid call usrerr and return FALSE ** end-if ** - if this ad is an ARGVEC ** - expand the vector and insert the new item at the end ** - update the item count ** - else ** - set *ad_valp to the converted value ** end-if ** - return TRUE if we used the whole arg, -N if we only used N-characters ***^^**********************************************************************/ X /* vector types and defines */ #define BLOCKSIZE 5 /* number of items to allocate at once */ #define VEC_SIZE(vec,el_typ) ( sizeof(el_typ *) * (BLOCKSIZE + vec->count) ) typedef ARGVEC_T(char *) strvec_t; typedef ARGVEC_T(char) charvec_t; typedef ARGVEC_T(int) intvec_t; typedef ARGVEC_T(short) shortvec_t; typedef ARGVEC_T(long) longvec_t; typedef ARGVEC_T(float) floatvec_t; typedef ARGVEC_T(double) doublevec_t; X X /*************************************************************************** ** ^SECTION: PSEUDO-TYPES -- argUsage, argEnd, argDummy ** ArgUsage is used to specify an argument that causes the command ** usage to be printed. ** ** ArgDummy is used to force an item to show up in usage-messages but ** the item itself is never matched against any argumenmts from the ** command-line. ** ** ArgEnd is used by amiga_args.c and vms_args.c to indicate an argument ** that forces all remaining arguments to be considered positional args. ** ** These three are dummy functions. The routines themselves do nothing ** of importance, we just need to have their addresses available for ** identification in the corresponding <os>_args.c file. ***^^**********************************************************************/ X /*ARGSUSED*/ BOOL argDummy PARMS(ad, vp, copyf) { X return FALSE; } X X /*ARGSUSED*/ BOOL argEnd PARMS(ad, vp, copyf) { X return (FALSE); } X X /*ARGSUSED*/ BOOL argUsage PARMS(ad, vp, copyf) { X return FALSE; } X X /*************************************************************************** ** ^SECTION: STRING-TYPES -- argStr ** ArgStr is one of the few argument translation routines that actually ** uses the <copyf> flag. If <copyf> is true then the string is duplicated. ** ** ArgStr assigns the given string (or a copy of it) to the value referenced ** by arg_valp(unless the argument is a vector in which case the given string ** is appended to the end). ** ** ArgStr ensures that the very last item in a vector of strings (the one ** accessed as vec.array[ vec.count ]) will always be NULL. ***^^**********************************************************************/ X /*ARGSUSED*/ BOOL argStr PARMS(ad, vp, copyf) { X char *cp; X argName_t argname; X X (VOID) get_name( arg_sname(ad), argname ); X if (copyf) { X register int i; X X i = strlen(vp) + 1; X cp = (char *) malloc(i * sizeof(char)); X if (!cp) { X usrerr("out of memory parsing %s", argname); X return FALSE; X } X memcpy(cp, vp, i); X } X else X cp = vp; X X if ( ARG_isVEC(ad) ) { X strvec_t *vec = (strvec_t *)arg_valp(ad); X X if ( (vec->count % BLOCKSIZE) == 0 ) { X vec->array = (char **) REALLOC(vec->array, 1+VEC_SIZE(vec, char *)); X if ( !vec->array ) { X if ( copyf ) free(cp); X syserr("out of memory saving arg %s", argname); X } X vec->flags = (argMask_t *) REALLOC(vec->flags, VEC_SIZE(vec, argMask_t)); X if ( !vec->flags ) { X syserr("out of memory saving arg %s", argname); X } X } X X vec->flags[ vec->count ] = arg_flags(ad); X if ( copyf ) BSET( vec->flags[vec->count], ARGCOPYF ); X vec->array[ (vec->count)++ ] = cp; X vec->array[ vec->count ] = (char *)NULL; X } X else X *(char **) arg_valp(ad) = cp; X X return (TRUE); } X X /*************************************************************************** ** ^SECTION: CHARACTER-TYPES -- argChar ** ArgChar assigns the given character to the value referenced by ad_valp ** (unless the argument is a vector in which case the given character ** is appended to the end). ** ** If an argChar argument is matched as a single character option, then ** the immediately following character will be considered its argument ** (but the characters after it may still be processed as option-letters). ** ** ArgChar ensures that the very last item in a vector of character (the ** one accessed as vec.array[ vec.count ]) will always be a NUL byte so ** that the resulting vector may also be used as a NULL terminated string. ** ** Unlike argStr, argChar will translate character escape sequences such ** as '\n' and '\012'. ***^^**********************************************************************/ X /*ARGSUSED*/ BOOL argChar PARMS(ad, vp, copyf) { X auto char *vpp; X argName_t argname; X int status = FALSE; X char c; X X (VOID) get_name( arg_sname(ad), argname ); X if (!vp || !*vp) { X status = FALSE; X } X if (strlen(vp) == 2 && vp[0]=='^') { X c = vp[1] ^ '@'; X status = TRUE; X } X else if (strlen(vp) > 1 && vp[0]=='\\') { X c = (int) strtol(&vp[1], &vpp, 8); X if (*vpp == '\0') X status = TRUE; X } X else if (strlen(vp) == 1) { X c = *vp; X status = TRUE; X } X else if ( !BTEST(arg_flags(ad), ARGVALSEP | ARGKEYWORD) ) { X c = *vp; X status = TRUE; X } X X if ( status ) { X if ( ARG_isVEC(ad) ) { X charvec_t *vec = (charvec_t *)arg_valp(ad); X X if ( (vec->count % BLOCKSIZE) == 0 ) { X vec->array = (char *) REALLOC(vec->array, 1+VEC_SIZE(vec, char)); X if (!vec->array) syserr("out of memory saving arg %s", argname); X } X vec->flags = (argMask_t *) REALLOC(vec->flags, VEC_SIZE(vec, argMask_t)); X if ( !vec->flags ) { X syserr("out of memory saving arg %s", argname); X } X X vec->flags[ vec->count ] = arg_flags(ad); X vec->array[ (vec->count)++ ] = c; X vec->array[ vec->count ] = '\0'; X } X else X *(char *) arg_valp(ad) = c; X } X else { X usrerr("invalid character argument '%s' for %s", X vp, argname); X } X return (status) ? (BOOL) -1 : FALSE; } X X /*************************************************************************** ** ^SECTION: INTEGER-TYPES -- argInt, argShort, argLong ** Each of these functions converts the given string to the desired ** integral type. The value may be specified as an octal number by ** specifying the first digit to be 0. Similarly, If the first two ** characters are '0x' then the number is treated as hexadecimal. ***^^**********************************************************************/ X X /* X ** macro to define an integral argtype function X ** X ** NOTE : do NOT use a terminating semicolon when invoking this macro! X */ #define INTEGRAL_ARGTYPE(name,num_t,ls_t) \ BOOL name PARMS(ad, vp, copyf) \ { \ X auto char *vpp; \ X argName_t argname; \ X num_t value; \ X \ X (VOID) get_name( arg_sname(ad), argname ); \ X value = (num_t) strtol(vp, &vpp, 0); \ X if (*vpp != '\0') { \ X usrerr("invalid integer argument '%s' for %s", vp, argname); \ X return (FALSE); \ X } \ X else { \ X if ( ARG_isVEC(ad) ) { \ X ls_t *vec = (ls_t *)arg_valp(ad); \ X \ X if ( (vec->count % BLOCKSIZE) == 0 ) { \ X vec->array = (num_t *) REALLOC(vec->array, VEC_SIZE(vec, num_t)); \ X if ( !vec->array ) \ X syserr("out of memory saving arg %s", argname); \ X \ X vec->flags = (argMask_t *) REALLOC(vec->flags, VEC_SIZE(vec, argMask_t)); \ X if ( !vec->flags ) \ X syserr("out of memory saving arg %s", argname); \ X } \ X \ X vec->flags[ vec->count ] = arg_flags(ad); \ X vec->array[ (vec->count)++ ] = value; \ X } \ X else \ X *(num_t *) arg_valp(ad) = value; \ X \ X return (TRUE); \ X } \ } X X /* define argInt() */ INTEGRAL_ARGTYPE( argInt, int, intvec_t ) X /* define argShort() */ INTEGRAL_ARGTYPE( argShort, short, shortvec_t ) X /* define argLong() */ INTEGRAL_ARGTYPE( argLong, long, longvec_t ) X X #ifndef NOFLOAT X /*************************************************************************** ** ^SECTION: FLOATING-POINT-TYPES -- argFloat, argDouble ** Each of these functions converts the given string to the desired ** floating-point type. ***^^**********************************************************************/ X X /* X ** macro to define a decimal argtype function X ** X ** NOTE : do NOT use a terminating semicolon when invoking this macro! X */ #define DECIMAL_ARGTYPE(name,dec_t,ls_t) \ BOOL name PARMS(ad, vp, copyf) \ { \ X auto char *vpp; \ X argName_t argname; \ X dec_t value; \ X \ X (VOID) get_name( arg_sname(ad), argname ); \ X value = (dec_t) strtod(vp, &vpp); \ X if (*vpp != '\0') { \ X usrerr("invalid decimal argument '%s' for %s", vp, argname); \ X return (FALSE); \ X } \ X else { \ X if ( ARG_isVEC(ad) ) { \ X ls_t *vec = (ls_t *)arg_valp(ad); \ X \ X if ( (vec->count % BLOCKSIZE) == 0 ) { \ X vec->array = (dec_t *) REALLOC(vec->array, VEC_SIZE(vec, dec_t)); \ X if (!vec->array) \ X syserr("out of memory saving arg %s", argname); \ X \ X vec->flags = (argMask_t *) REALLOC(vec->flags, VEC_SIZE(vec, argMask_t)); \ X if (!vec->flags) \ X syserr("out of memory saving arg %s", argname); \ X } \ X \ X vec->flags[ vec->count ] = arg_flags(ad); \ X vec->array[ (vec->count)++ ] = value; \ X } \ X else \ X *(dec_t *) arg_valp(ad) = value; \ X \ X return (TRUE); \ X } \ } X /* define argFloat */ DECIMAL_ARGTYPE( argFloat, float, floatvec_t ) X /* define argLong */ DECIMAL_ARGTYPE( argDouble, double, doublevec_t ) X #endif /* NOFLOAT */ X X /************************************************************************* ** ^SECTION: BOOLEAN-TYPES -- argBool, argSBool, argUBool, argTBool ** ArgBool and argSBool set a boolean value (if no value is given). ** ArgUBool unsets a boolean value (if no value is given). ArgTBool ** toggles a boolean value (if no value is given). If a value is ** supplied to any of these routines, then the string is looked up ** in a table and assigned the corresponding value. ** ** If a value is supplied for an argument that was matched via its ** single character name and is part of the same argv element as the ** argument-name (so that both ARGKEYWORD and ARGVALSEP are not set), ** then only the first character of the value is used (unless it is ** not found in our table, in which case the value is ignored and the ** default action is taken). ** ** The only possible arguments for single-character options are the ** following: ** ** 1, + set the flag ** 0, - unset the flag ** ^, ~ toggle the flag ** ** The possible argument strings for long-options (keywords) are as ** follows (case-insensitive): */ X X /* define a structure for an item in our boolean-lookup table */ struct booltab { X char *bname; /* string to match against */ X char bneedmatch; /* number of characters that must match */ X BOOL bval; /* value to use */ }; X X /* define the boolean-lookup table */ STATIC struct booltab _BoolTab[] = { X "1", 1, TRUE, X "0", 1, FALSE, X "+", 1, TRUE, X "-", 1, FALSE, 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 X /* X ** NOTE: Lists and vectors of Boolean types are not supported!!! X ** (same goes for argEnd, argInput, & argOutput) X */ X /*ARGSUSED*/ BOOL argBool PARMS(ad, vp, copyf) { X register struct booltab *b; X register char *cp; X argName_t argname; X int len; X X (VOID) get_name( arg_sname(ad), argname ); X X /* ARGVECs are not supported for this Boolean arg-types */ X if ( ARG_isVEC(ad) ) X syserr( "Error in '%s' arg-entry! Boolean argvecs are not supported!", X argname ); X X /* if vp is NULL, just set to TRUE X ** (needed for backward compatibility) X */ X if ( !vp || !*vp ) { X *(BOOL *) arg_valp(ad) = TRUE; X return (TRUE); X } X X /* allow single character arguments for non-keywords */ X if ( !BTEST(arg_flags(ad), ARGKEYWORD | ARGVALSEP) ) { X if ( *vp == '+' || *vp == '1' ) { X *(BOOL *) arg_valp(ad) = TRUE; X return (BOOL) -1; X } X if ( *vp == '-' || *vp == '0' ) { X *(BOOL *) arg_valp(ad) = FALSE; X return (BOOL) -1; X } X if ( *vp == '~' || *vp == '^' ) { X *(BOOL *) arg_valp(ad) = (*(BOOL *) arg_valp(ad)) ? FALSE : TRUE; X return (BOOL) -1; X } X X /* unmatched value, return FALSE for non-argBool (so the caller X ** can use whatever default) and return TRUE for argBool. X */ X if ( arg_type(ad) == argBool ) { X *(BOOL *) arg_valp(ad) = TRUE; X return TRUE; X } X return FALSE; X }/* if single char option */ X X /* copy input & convert to lower case */ X cp = strlwr( strdup(vp) ); X len = strlen( cp ); X X /* search for a match in the table */ X for (b = _BoolTab; b->bname ; b++) { X /* if too short, don't even bother trying */ X if (len < b->bneedmatch) X continue; X X if ( memcmp(cp, b->bname, len) == 0) { X /* got a match */ X *(BOOL *) arg_valp(ad) = b->bval; X free( cp ); X return (TRUE); X } X }/*if match*/ X X free( cp ); X usrerr("invalid Boolean argument '%s' for %s", vp, argname); X return (FALSE); } X X /*ARGSUSED*/ BOOL argSBool PARMS(ad, vp, copyf) { X argName_t argname; X BOOL retval; X X (VOID) get_name( arg_sname(ad), argname ); X X /* ARGVECs are not supported for this Boolean arg-types */ X if ( ARG_isVEC(ad) ) X syserr( "Error in '%s' arg-entry! Boolean argvecs are not supported!", X argname ); X X /* if vp is NULL, just set to TRUE */ X if ( !vp || !*vp || !(retval = argBool(ad, vp, copyf)) ) { X *(BOOL *) arg_valp(ad) = TRUE; X return (TRUE); X } X else X return retval; } X /*ARGSUSED*/ BOOL argUBool PARMS(ad, vp, copyf) { X argName_t argname; X BOOL retval; X X (VOID) get_name( arg_sname(ad), argname ); X X /* ARGVECs are not supported for this Boolean arg-types */ X if ( ARG_isVEC(ad) ) X syserr( "Error in '%s' arg-entry! Boolean argvecs are not supported!", X argname ); X X /* if vp is NULL, just set to FALSE */ X if ( !vp || !*vp || !(retval = argBool(ad, vp, copyf)) ) { X *(BOOL *) arg_valp(ad) = FALSE; X return (TRUE); X } X else X return retval; } X /*ARGSUSED*/ BOOL argTBool PARMS(ad, vp, copyf) { X argName_t argname; X BOOL retval; X X (VOID) get_name( arg_sname(ad), argname ); X X /* ARGVECs are not supported for this Boolean arg-types */ X if ( ARG_isVEC(ad) ) X syserr( "Error in '%s' arg-entry! Boolean argvecs are not supported!", X argname ); X X /* if vp is NULL, just toggle value */ X if ( !vp || !*vp || !(retval = argBool(ad, vp, copyf)) ) { X *(BOOL *) arg_valp(ad) = (*(BOOL *) arg_valp(ad)) ? FALSE : TRUE ; X return (TRUE); X } X else X return retval; } X X #ifdef vms_style X /*************************************************************************** ** ^SECTION: I/O-REDIRECTION-TYPES -- argInput, argOutput ** ArgInput attempts to redirect the file-pointer addressed by ad_valp ** to the file named by the given value. The file is opened for reading. ** ** ArgOutput attempts to redirect the file-pointer addressed by ad_valp ** to the file named by the given value. The file is opened for writing. ** ** In either case, ad_valp should be of type (FILE *) (and not a pointer ** to a file pointer as in (FILE **)!!! ** ** If the given files cannot be opened, then an error message is printed ** and the associated input/output streams are closed. ***^^**********************************************************************/ X /*ARGSUSED*/ BOOL argInput PARMS(ad, vp, copyf) { X /* note that ad_valp is a file pointer X ** (so dont use &fp in the arg-desc) X */ X FILE *fp = (FILE *)arg_valp(ad); X BOOL error = FALSE; X X /* redirect file pointer to read from file */ X if ( !vp ) { X usrerr( "Error: no file name given" ); X error = TRUE; X } X X if ( !error && !fp ) X error = TRUE; X else if ( !error && !freopen(vp, "r", fp) ) X error = TRUE; X X if ( error ) { X usrerr( "Error: unable to redirect input to file \"%s.\"", vp ); X return (FALSE); X } X return (TRUE); } X X /*ARGSUSED*/ BOOL argOutput PARMS(ad, vp, copyf) { X /* note that ad_valp is a file pointer X ** (so dont use &fp in the arg-desc) X */ X FILE *fp = (FILE *)arg_valp(ad); X BOOL error = FALSE; X X /* redirect file pointer to write to file */ X if ( !vp ) { X usrerr( "Error: no file name given" ); X error = TRUE; X } X X if ( !error && !fp ) X error = TRUE; X else if ( !error && !freopen(vp, "a", fp) ) X error = TRUE; X X if ( error ) { X usrerr( "Error: unable to redirect output to file \"%s.\"", vp ); X return (FALSE); X } X return (TRUE); } X #endif /* vms_style */ SHAR_EOF chmod 0664 parseargs/argtype.c || echo 'restore of parseargs/argtype.c failed' Wc_c="`wc -c < 'parseargs/argtype.c'`" test 22590 -eq "$Wc_c" || echo 'parseargs/argtype.c: original size 22590, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/argtype3.txt ============== if test -f 'parseargs/argtype3.txt' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/argtype3.txt (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/argtype3.txt (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/argtype3.txt' && X X X ARGTYPE(3) ARGTYPE(3) X X X NAME X argtype - argument type functions used by parseargs(3) X SYNOPSIS X #include <parseargs.h> X X BOOL argUsage( argdesc, argstr, copyf ); X BOOL argEnd( argdesc, argstr, copyf ); X BOOL argDummy( argdesc, argstr, copyf ); X BOOL argBool( argdesc, argstr, copyf ); X BOOL argSBool( argdesc, argstr, copyf ); X BOOL argUBool( argdesc, argstr, copyf ); X BOOL argTBool( argdesc, argstr, copyf ); X BOOL argChar( argdesc, argstr, copyf ); X BOOL argStr( argdesc, argstr, copyf ); X BOOL argInt( argdesc, argstr, copyf ); X BOOL argShort( argdesc, argstr, copyf ); X BOOL argLong( argdesc, argstr, copyf ); X BOOL argFloat( argdesc, argstr, copyf ); X BOOL argDouble( argdesc, argstr, copyf ); X BOOL listStr( argdesc, argstr, copyf ); X void listFree( arglist ); X void vecFree( argvec, type ); X void vecDeepFree( argvec, type ); X X ARGDESC *argdesc; X char *argstr; X BOOL copyf; X ArgList *arglist; X DESCRIPTION X Each of these converts a parameter value to the internal X form, including validity checking. Their parameters and X return values all behave similarly. One of these routines X are called when an argunent of that particular type is X matched by one of the argument parsing function in par- X seargs(3). When such an argument is matched, its argument X translation routines is invoked and is passed (1) the X address of the argument descriptor for the matched argument, X (2) the possible argument string for that matched argument, X and (3) a boolean filed that is TRUE only if the second X parameter points to temporary storage (indicating that some X copying may need to be done instead of just pointing to the X same object). X X Once the argument translation routine is invoked, it is X responsible for converting the argument string to the X desired internal form (perhaps a number), and assigning the X resultant value to the arg_valp(ad) field of the argument X descriptor (this includes handling any necessary X (re)allocation if the matched argument has the ARGVEC flag X enabled). If the argument is an ARGVEC or ARGLIST then the X X X Page 1 X X X X X X ARGTYPE(3) ARGTYPE(3) X X X X routine is responsible for allocating any space, copying the X arg-flags to the value-specific flags, and setting the X ARGCOPYF flag for the value if it needs to be allocated as X well. X X RETURN VALUE X TRUE The conversion was successful and the entire value X was used. X X X FALSE The conversion failed. The reason for failure X should be diagnosed using usrerr(3). X X X -N The conversion was successful but only N characters X of the value were used, the remaining characters may X still match other arguments. X X PSEUDO-TYPES X ArgUsage is used to specify an argument that causes the com- X mand usage to be printed. X X ArgDummy is used to force an item to show up in usage- X messages but the item itself is never matched against any X argumenmts from the command-line. X X ArgEnd is used by Amiga style command-lines to indicate an X argument that forces all remaining arguments to be con- X sidered positional args. X X These three are dummy functions. The routines themselves do X nothing of importance, we just need to have their addresses X available for identification in the corresponding command- X line styles. X STRING-TYPES X ArgStr is one of the few argument translation routines that X actually uses the copyf flag. If copyf is true then the X string is duplicated. X X ArgStr assigns the given string (or a copy of it) to the X value referenced by arg_valp(ad) (unless the argument is a X vector in which case the given string is appended to the SHAR_EOF true || echo 'restore of parseargs/argtype3.txt failed' fi echo 'End of part 2' echo 'File parseargs/argtype3.txt is continued in part 3' echo 3 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.