home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- ** ^FILE: xparse.c - library functions for the parsargs(3) package
- **
- ** ^DESCRIPTION:
- ** This file implements the following functions in the parseargs library:
- **
- ** init_args() -- constructor for a command-object
- ** usage() -- pretty-print the command-usage
- ** parsecntl() -- control parsing, get/set command-attributes
- ** parseargs() -- parse arguments from a string vector
- ** fparseargs() -- parse arguments from a file pointer
- ** lparseargs() -- parse arguments from an arglist
- ** sparseargs() -- parse arguyments in a string
- ** vparseargs() -- parse arguments from a variable argument list
- **
- ** It should be noted that sparseargs() splits the given string up into
- ** a whitespace separated series of tokens, whereas vparseargs assumes
- ** that each parameter is already a single token (hence performs no
- ** token splitting).
- **
- ** Each of these functions returns 0 upon success and non-zero otherwise
- ** (except for usage() & init_args, which have no return value).
- **
- ** ^SIDE-EFFECTS:
- ** Each of the functions in the parseargs library will set the external
- ** character string ProgName to be the name of the last command that was
- ** operated upon by any of the library routines.
- **
- ** When an argument-descriptor array is first encountered by any of the
- ** parseargs library routines, it is initially compiled into an inter-
- ** mediate form that is more convenient to manipulate. As a direct
- ** result, it is not advisable to attempt to index directly into the
- ** array to manipulate one of the argument descriptors (because the
- ** argdesc that you thought was there may actually be somewhere else).
- ** After the array has been given its initial value(s), only parsecntl(3)
- ** should be used to manipulate or query the attributes of an argument
- ** descriptor.
- **
- ** ^FILES:
- ** <useful.h>
- ** <parseargs.h>
- **
- ** ^SEE_ALSO:
- ** argtype(3), parseargs(1), parseargs(3)
- **
- ** ^CAVEATS:
- ** Because of the way argument parsing is implemented under UNIX, MS-DOS
- ** and OS/2, option arguments which contain a leading dash (`-') (or
- ** whatever the option prefix character is defined to be) may not be
- ** specified as a separate argument on the command line, it must be part
- ** of the same argument. That is to say that if a program has a -f option
- ** that requires a string argument, then the following:
- ** -f-arg
- **
- ** will properly assign the string "-arg" to the option whereas the
- ** following:
- ** -f -arg
- **
- ** will be interpreted by parseargs as two option strings: the first of
- ** which ("-f") is missing a required argument and the second of which
- ** ("-arg") will most likely be flagged as an invalid option.
- **
- ** Similarly, if the user requires an ARGLIST option to take multiple
- ** arguments with leading dashes then the following method must be used:
- ** It is a "feature" of parseargs that ARGLIST arguments are always
- ** appended to the current list of arguments for the given option. Thus,
- ** if "-f" is an option taking a list of arguments, then the following
- ** are all equivalent:
- ** -farg1 arg2
- ** -f arg1 arg2
- ** -farg1 -farg2
- ** -f arg1 -f arg2
- **
- ** Hence multiple "leading dash" arguments may specified as follows:
- ** -f-dash_arg1 -f-dash_arg2 ...
- **
- ** ^BUGS:
- ** When a non-multivalued argument appears more than once on the
- ** command-line then only the last value supplied is used. A problem
- ** occurs however in the following scenario: suppose `-s' is an option
- ** that takes an optional string argument (and suppose `-x' is some
- ** boolean flag). Then if the following command-line is issued:
- **
- ** command -s string -x -s
- **
- ** then, the argument flags will properly correspond to the second
- ** instance of the `-s' option (namely ARGGIVEN will be set but ARGVAL-
- ** GIVEN will be unset) but the value associated with the option will be
- ** "string" (because the first instance overwrote the default).
- ** Because of this, it may be safest to reassign the default value if
- ** ARGGIVEN is set but ARGVALGIVEN is unset.
- **
- ** ^HISTORY:
- ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com> Created
- **
- ** 04/03/91 Brad Appleton <brad@ssd.csd.harris.com>
- ** - fixed bug in [fvsl]parseargs() and parseargs().
- ** previous parse-flags should not be reset until AFTER required
- ** arguments are checked for, otherwise we may forget to prompt
- ** for them if $PARSECNTL asked us to do so.
- **
- ** 08/27/91 Earl Chew <cechew@bruce.cs.monash.edu.au>
- ** - split out get_description().
- ** - add ProgNameLen
- ** - support for non-writable strings
- ** - allow sparseargs() to parse empty strings like parseargs()
- **
- ** 04/03/91 Brad Appleton <brad@ssd.csd.harris.com>
- ** - changed vms_style stuff to use ps_NOTCMDLINE
- ***^^**********************************************************************/
-
- #include <stdio.h>
- #include <ctype.h>
- #include <useful.h>
- #include "strfuncs.h"
- #include "exit_codes.h"
-
- #define PARSEARGS_PRIVATE /* include private definitions */
- #define PARSEARGS_NEXTERNS /* exclude external declarations */
- #include "parseargs.h"
-
- #ifdef amiga_style
- # define pa_DEFAULTS 0x000
- # define parse_argv_style amiga_parse
- # define print_usage_style amiga_usage
- EXTERN int amiga_parse ARGS(( char **, ARGDESC * ));
- EXTERN VOID amiga_usage ARGS(( const ARGDESC *, argMask_t ));
- #endif
- #ifdef ibm_style
- # define pa_DEFAULTS pa_ANYCASE
- # define parse_argv_style ibm_parse
- # define print_usage_style ibm_usage
- EXTERN int ibm_parse ARGS(( char **, ARGDESC * ));
- EXTERN VOID ibm_usage ARGS(( const ARGDESC *, argMask_t ));
- #endif
- #ifdef unix_style
- # define pa_DEFAULTS pa_FLAGS1ST
- # define parse_argv_style unix_parse
- # define print_usage_style unix_usage
- EXTERN int unix_parse ARGS(( char **, ARGDESC * ));
- EXTERN VOID unix_usage ARGS(( const ARGDESC *, argMask_t ));
- #endif
- #ifdef vms_style
- # define pa_DEFAULTS pa_PROMPT
- # define parse_argv_style vms_parse
- # define print_usage_style vms_usage
- EXTERN int vms_parse ARGS(( char **, ARGDESC * ));
- EXTERN VOID vms_usage ARGS(( const ARGDESC *, argMask_t ));
- #endif
-
-
- #ifdef vms
- # define USER_VARIABLE "symbol"
- #else
- # define USER_VARIABLE "environment variable"
- #endif
-
-
- /***************************************************************************
- ** ^MACRO: SYNTAX_ERROR - check for syntax errors & missing required arguments
- **
- ** ^SYNOPSIS:
- ** SYNTAX_ERROR(status, argd)
- **
- ** ^PARAMETERS:
- ** status
- ** -- current parsing status returned by last xparsexxx() call
- **
- ** argd
- ** --argdesc-array
- **
- ** ^RETURN-VALUE:
- ** Evaluates to TRUE if need to exit, FALSE otherwise
- **
- ** ^ALGORITHM:
- ** - if (!pa_NOCHECK) and (verify_argreqs == error) then return TRUE
- ** - else return FALSE
- ***^^**********************************************************************/
- #define SYNTAX_ERROR(status, argd) ( !verify_argreqs(argd, &status) )
-
-
- /***************************************************************************
- ** ^GLOBAL-VARIABLE: ProgName, ProgNameLen
- **
- ** ^VISIBILITY:
- ** external global (visible to functions in all files)
- **
- ** ^DESCRIPTION:
- ** ProgName (which is initially NULL) will be used to point to the
- ** command-name (specified on the command-line) of the command that
- ** has most recently invoked a function in the parseargs library.
- ** ProgNameLen will be set to the length of the string.
- ***^^**********************************************************************/
- CONST char *ProgName = CHARNULL;
- int ProgNameLen = 0;
-
- EXTERN VOID syserr ARGS((const char *, ...));
- EXTERN VOID usrerr ARGS((const char *, ...));
- EXTERN VOID eprintf ARGS((const char *, ...));
-
- #ifdef vms_style
- EXTERN BOOL argInput ARGS((ARGDESC *, char *, BOOL));
- EXTERN BOOL argOutput ARGS((ARGDESC *, char *, BOOL));
- #endif
-
- #define MAXLINE 256
-
-
- /* override argument descriptor, if none given by user */
- static ARGDESC Empty_ArgDesc[] = { START_ARGUMENTS, END_ARGUMENTS };
-
- /***************************************************************************
- ** ^SECTION: DEFAULT-ARGUMENTS
- ** Each argdesc-array has an initial default argument list (which may be
- ** reset using the pc_DEFARGS function code with parsecntl). This initial
- ** default argument-list contains `?' and `H' which may be used as single
- ** character keywords to display command-usage for all command-line
- ** styles. Similarly, "?", "H", and "Help" may be used as long-keywords
- ** to display command-usage for all command-line styles. In Addition,
- ** for VMS style commands, the qualifiers /INPUT=file, /OUTPUT=file, and
- ** /ERROR=file, may be used to redirect stdin, stdout, and stderr
- ** (respectively) to a file. For AmigaDOS style commands, the keyword
- ** "ENDKWDS" may be used to disable parsing for any more keywords on
- ** the command-line.
- ***^^**********************************************************************/
- static ARGDESC Default_ArgDesc[] = {
- START_ARGUMENTS,
-
- /* <name> <flags> <type> <valp> <prompt> */
- { '?', ARGHIDDEN, argUsage, __ NULL, "? (print usage and exit)" },
- { 'H', ARGHIDDEN, argUsage, __ NULL, "Help (print usage and exit)" },
-
- #ifdef amiga_style
- { '-', ARGHIDDEN, argEnd, __ NULL, "ENDKeyWorDS" },
- #endif
-
- #ifdef vms_style
- # ifdef vms
- { '<', ARGHIDDEN, argInput, __ &stdin, "sysINPUT (redirect SYS$INPUT)" },
- { '>', ARGHIDDEN, argOutput, __ &stdout, "sysOUTPUT (redirect SYS$OUTPUT)" },
- { '%', ARGHIDDEN, argOutput, __ &stderr, "sysERROR (redirect SYS$ERROR)" },
- # else
- { '<', ARGHIDDEN, argInput, __ stdin, "sysINPUT (redirect SYS$INPUT)" },
- { '>', ARGHIDDEN, argOutput, __ stdout, "sysOUTPUT (redirect SYS$OUTPUT)" },
- { '%', ARGHIDDEN, argOutput, __ stderr, "sysERROR (redirect SYS$ERROR)" },
- # endif
- #endif
-
- END_ARGUMENTS
- };
-
-
- #ifdef AmigaDOS
- # define getenv(s) CHARNULL
- # define envfree(s) s = CHARNULL
- #endif
-
- #if ( defined(MSDOS) || defined(OS2) )
- EXTERN char *getenv ARGS(( const char * ));
- # define envfree(s) s = CHARNULL
- #endif
-
- #ifdef unix
- EXTERN char *getenv ARGS(( const char * ));
- # define envfree(s) s = CHARNULL
- #endif
-
- #ifdef vms
- # define getenv(s) get_symbol(s)
- # define envfree(s) (s) = ( !(s) ) ? CHARNULL : (free(s), CHARNULL)
- # include <descrip.h>
- # include <libdef.h>
-
- # define MAXLEN 255
-
- /***************************************************************************
- ** ^FUNCTION: get_symbol - retrieve the value of a VMS symbol
- **
- ** ^SYNOPSIS:
- */
- # ifndef __ANSI_C__
- char *get_symbol( sym_name )
- /*
- ** ^PARAMETERS:
- */
- char *sym_name;
- /* -- name of the symbol to retrieve
- */
- # endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Get_symbol will lookup the named symbol and return its value
- ** as a string.
- **
- ** ^REQUIREMENTS:
- ** sym_name should correspond to the name of a pre-defined symbol.
- **
- ** ^SIDE-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** NULL if the symbol is not found, otherwise it copies the symbol
- ** value and returns the address of the copy (which may later be
- ** deallocated using free()).
- **
- ** ^ACKNOWLEDGEMENTS:
- ** Thanx to Jim Barbour for writing most of this code. --BDA
- **
- ** ^ALGORITHM:
- ** Trivial - just use the LIB$GET_SYMBOL system service.
- ***^^**********************************************************************/
- # ifdef __ANSI_C__
- char *get_symbol( const char *sym_name )
- # endif
- {
- unsigned long stat, lib$get_symbol();
- unsigned short buflen;
- char sym_value[ MAXLEN ];
- $DESCRIPTOR( sym_name_d, sym_name );
- $DESCRIPTOR( sym_value_d, sym_value );
-
- sym_value_d.dsc$w_length = MAXLEN;
- sym_name_d.dsc$w_length = strlen( sym_name );
- stat = lib$get_symbol( &sym_name_d, &sym_value_d, &buflen, (void *)0 );
- if ( stat == LIB$_NOSUCHSYM ) {
- return CHARNULL;
- }
- else if ( ! (stat & 1) ) {
- exit(stat);
- }
- sym_value[ buflen ] = '\0';
- return strdup( sym_value );
- }
- #endif /* vms */
-
-
- /***************************************************************************
- ** ^FUNCTION: is_interactive - determine if a stream is "interactive"
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static BOOL is_interactive( fd )
- /*
- ** ^PARAMETERS:
- */
- int fd;
- /* -- file descriptor associated with the input stream
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Is_interactive determines whether or not the given i/o stream is
- ** associated with a terminal.
- **
- ** ^REQUIREMENTS:
- ** Fd must correspond to a valid, open, file-descriptor.
- **
- ** ^SIDE-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** TRUE if fd is associated with a terminal, FALSE otherwise.
- **
- ** ^ALGORITHM:
- ** Trivial - just use isatty and restore errno if necessary
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static BOOL is_interactive( int fd )
- #endif
- {
- #ifdef unix
- EXTERN int isatty ARGS((int));
- extern int errno;
- int saverr = errno; /* save errno */
-
- if ( isatty(fd) )
- return TRUE;
- else {
- errno = saverr;
- return FALSE;
- }
- #else
- #ifdef vms
- EXTERN int isatty ARGS((int));
- int ret = isatty( fd );
-
- if ( ret == -1 ) /* error with fd */
- syserr( "error on file descriptor #%d", fd );
- else if ( ret )
- return TRUE;
- else
- return FALSE;
- #else
- return TRUE;
- #endif
- #endif
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: init_args - Initialize the command object
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- VOID init_args( argd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC argd[];
- /* -- the array of command-arguments to initialize.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Init_args performs all the actions that are required to prepare an
- ** argdesc-array for use by any of the parseargs functions. Storrage
- ** is allocated and initialized and argument descriptions are compiled.
- **
- ** ^REQUIREMENTS:
- ** <argd> must point to an array that has been declared using the CMD_XXXX
- ** macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
- **
- ** ^SIDE-EFFECTS:
- ** The argd is initialized for use.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - compile the command name, purpose, and description if they were given
- ** - if ENDOFARGS was used without STARTOFARGS, then shift each item in
- ** the array down by one position.
- ** - initialize the parse-flags to the default settings
- ** - initialize the default argument search-list
- ** - allocate and initialize the command-context
- ** - for each command-line argument in argd
- ** - compile the ad_prompt field and set the default initial ARGXXX flags
- ** end-for
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- void init_args( ARGDESC argd[] )
- #endif
- {
- register ARGDESC *ad, *anchor;
- int ad_count = 0;
- BOOL old_style = FALSE;
- char *description = CHARNULL, *purpose = CHARNULL;
- int desclen;
-
- if ( !argd ) return;
-
- /* dont initialize if its already been done */
- if ( CMD_isINIT(argd) ) return;
-
- if ( !ARG_isEND(argd) ) old_style = TRUE;
-
- /* determine the argument count and preprocess each ad-entry */
- anchor = ( old_style ) ? argd : ARG_FIRST(argd);
- for ( ad = anchor ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- ad_count++;
-
- /* set-up any positional args that we know of */
- if ( ARG_isPOSONLY(ad) ) BSET( arg_flags(ad), ARGPOS );
-
- /* set-up any default ARGNOVALs that we know of */
- if (ARG_isBOOLEAN(ad) || ARG_isPSEUDOARG(ad))
- BSET( arg_flags(ad), ARGNOVAL );
-
- if ( get_argdesc( (char *)arg_sname(ad), &desclen ) )
- BSET(arg_flags(ad), ARGDESCRIBED);
- }
-
- /* shift all the entries down one to make room for a new 1st-entry
- ** It would've been nice to just swap the 1st and last entries but
- ** I have to preserve the order that all positional parameters are
- ** given in.
- */
- if ( old_style && ad_count > 0 ) {
- anchor = ad + 1; /* save this position */
- for ( ; ad > argd ; ARG_RETREAT(ad) ) {
- memcpy( (ARBPTR)ad, (ARBPTR)(ad - 1), sizeof(ARGDESC) );
- }/*for*/
- memcpy( (ARBPTR)argd, (ARBPTR)anchor, sizeof(ARGDESC) );
- }
- else anchor = ad;
-
- /* set default parse-flags */
- #ifndef ibm_style
- cmd_flags(argd) = pa_DEFAULTS;
- #else
- {
- char *pfx = getenv( "SWITCHAR" );
- if ( pfx && *pfx == '-' )
- cmd_flags(argd) = pa_FLAGS1ST;
- else
- cmd_flags(argd) = pa_DEFAULTS;
- }
- #endif
-
- if ( cmd_name(argd) ) cmd_name(argd) = strdup( cmd_name(argd) );
- /* if new-style, get the purpose from the command name */
- if ( !old_style && cmd_name(argd) ) {
- purpose = cmd_name(argd);
- }
-
- /* set the program name */
- if ( ProgName && *ProgName && !cmd_name(argd) ) {
- cmd_name(argd) = ProgName;
- }
-
- /* set context */
- if ( !cmd_context(argd) ) {
- cmd_context(argd) = (CTXDESC *) anchor;
- cmd_state(argd) = ( old_style ) ? ps_OLDSTYLE : (ps_flags_t) 0;
- cmd_argv0(argd) = cmd_name(argd);
- cmd_purpose(argd) = purpose;
- cmd_ptrs(argd) = (ARGDPTRS *)malloc( sizeof(ARGDPTRS) );
- if ( !cmd_ptrs(argd) ) {
- syserr( "malloc failed in init_args()" );
- }
- if ( argd == Default_ArgDesc ) {
- cmd_defargs(argd) = ARGDESCNULL;
- }
- else {
- if ( !CMD_isINIT(Default_ArgDesc) ) init_args( Default_ArgDesc );
- cmd_defargs(argd) = Default_ArgDesc;
- }
- cmd_list(argd) = ARGDESCNULL;
- #ifdef amiga_style
- cmd_prev(argd) = ARGDESCNULL;
- #endif
- }
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: reset_args - (re)set a command for parsing
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID reset_args( argd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC argd[];
- /* -- array or command-line arguments to be reset
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Reset_args will prepare a command for parsing. The command-state is
- ** initialized and each argument is reset to "unmatched".
- **
- ** ^REQUIREMENTS:
- ** <argd> must point to an array that has been declared using the CMD_XXXX
- ** macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
- **
- ** ^SIDE-EFFECTS:
- ** resets the ARG flags of each argument and the command-state of argd.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - reset the command-context to be (as of yet) unparsed
- ** - reset the ARG flags of the programmer & default argument descriptors
- ***^^**********************************************************************/
-
- /* arg-flags to be reset before parsing a new command-line */
- #define ARGDEFAULTS ( ARGGIVEN | ARGVALGIVEN | ARGKEYWORD | ARGVALSEP )
-
- #ifdef __ANSI_C__
- static void reset_args( ARGDESC argd[] )
- #endif
- {
- register ARGDESC *args, *ad;
-
- if ( !CMD_isINIT(argd) ) init_args(argd);
-
- /* reset the command context */
- BCLEAR( cmd_state(argd), (ps_NOFLAGS|ps_NOCMDENV|ps_NOPARSECNTL) );
- cmd_list(argd) = ARGDESCNULL;
- #ifdef amiga_style
- cmd_prev(argd) = ARGDESCNULL; /* No argument requested */
- #endif
-
- /* clear out any cruft in the argument descriptors */
- for ( args = argd ; args ; args = cmd_defargs(args) ) {
- for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- BCLEAR( arg_flags(ad), ARGDEFAULTS );
-
- #if ( defined(vms_style) || defined(amiga_style) )
- /* vms and amiga use keywords only */
- BSET( arg_flags(ad), ARGKEYWORD );
- #endif
- }/*for*/
- }/*for*/
- }
-
- #undef ARGDEFAULTS
-
-
- /***************************************************************************
- ** ^FUNCTION: prompt_user - prompt the user for a missing argument
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static BOOL prompt_user( ad, argname )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *ad;
- /* -- pointer to the argument to be supplied by the user
- */
- char *argname;
- /* -- name of the argument to be supplied by the user
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Prompt_user will attempt to prompt the user to supply (on standard input)
- ** the value for the given argument. If the argument is a list, then each
- ** item of the list should be given on a separate line (with a blank line
- ** terminating the list).
- **
- ** No special "escaping" or translation is performed on the resulting user
- ** input, hence any whitespace ro quotes will be considered as part of the
- ** argument value.
- **
- ** If stdin is NOT associated with a terminal then no prompting is performed
- ** and FALSE is returned. If the user enters in invalid value for the
- ** argument then the value is discarded and FALSE is returned (so you
- ** better get it right the first time).
- **
- ** ^REQUIREMENTS:
- ** Only the first 255 characters of user input is used.
- **
- ** ^SIDE-EFFECTS:
- ** Modifies <ad> accordingly.
- **
- ** ^RETURN-VALUE:
- ** FALSE if there is an error, TRUE otherwise.
- **
- ** ^ALGORITHM:
- ** - if stdin is not connected to a terminal return FALSE
- ** - if argument is not a list then
- ** - get string from user
- ** - attempt to convert the string
- ** - return TRUE upon success and FALSE upon failure
- ** - else (the argument is a list)
- ** - while (user has not entered a blank line) do
- ** - get string from user
- ** - attempt to convert the string
- ** - return FALSE upon failure (otherwise continue to loop)
- ** end-while
- ** end-if
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static BOOL prompt_user( ARGDESC *ad, const char *argname )
- #endif
- {
- BOOL error = FALSE, first = TRUE;
- int end;
- char buf[ MAXLINE ];
-
- if ( !is_interactive(STDIN) ) return FALSE;
-
- if ( ARG_isMULTIVAL(ad) ) {
- fprintf(stderr, "Enter one %s per-line ", argname);
- fprintf(stderr, "(enter a blank line to stop).\n");
- }
-
- do { /* need repeated prompting for an ARGLIST or an ARGVEC */
- /* get argument from user */
- *buf='\0';
- #ifdef vms_style
- fprintf(stderr, "\r%s> ", argname);
- #else
- fprintf(stderr, "\rEnter %s: ", argname);
- #endif
- fflush( stderr );
- if ( !fgets(buf, MAXLINE, stdin) ) *buf = '\0';
-
- /* discard the newline */
- end = strlen(buf) - 1;
- if ( end >= 0 && buf[end] == '\n' )
- buf[ end ] = '\0';
-
- /* make sure we read something! */
- if ( *buf == '\0' ) {
- if ( first ) {
- usrerr( "error - no %s given", argname );
- error = TRUE;
- }
- continue;
- }
-
- /* try to convert what we read (remember - buf is transient) */
- if ( (*arg_type(ad))(ad, buf, TRUE) )
- BSET(arg_flags(ad), ARGGIVEN | ARGVALGIVEN);
- else
- error = TRUE;
-
- first = FALSE;
- } while ( !error && ARG_isMULTIVAL(ad) && *buf );
-
- return (error) ? FALSE : TRUE;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: verify_argreqs - check for any missing required arguments
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static BOOL verify_argreqs( cmd, parse_status )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *cmd;
- /* -- the argdesc-array of command-line arguments.
- */
- int *parse_status;
- /* -- address of current parse_status
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Verify_argreqs will reset parse_status to a success-value if it
- ** previously indicated a syntax error but pa_IGNORE is set to ignore
- ** syntax error. Verify_argreqs will then verify that any required
- ** command-line arguments have been supplied (if they were not and
- ** pa_PROMPT is set, then the missing values will be prompted for).
- **
- ** ^REQUIREMENTS:
- ** <cmd> must point to an array that has been declared using the CMD_XXXX
- ** macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
- **
- ** parse_status should be a pointer to the result of a previous call to
- ** {f,l,s,v,}parseargs.
- **
- ** ^SIDE-EFFECTS:
- ** The arg-descs for missing arguments may be modified by prompt_user.
- ** parse_status will be modified to indicate whether or not a syntax
- ** error really has occurred (it will be pe_SUCCESS if all is hunky-dory).
- **
- ** ^RETURN-VALUE:
- ** FALSE if there is an error, TRUE otherwise.
- **
- ** ^ALGORITHM:
- ** - if parse_status is a syntax error but pa_IGNORE is set
- ** - ignore the error by resetting the status to pe_SUCCESS
- ** end-if
- ** - if pa_NOCHECK is not set then check for any missing required
- ** arguments (using prompt_user to get the values if pa_PROMPT is set).
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static BOOL verify_argreqs( ARGDESC *cmd, int *parse_status )
- #endif
- {
- register ARGDESC *ad;
- BOOL error;
- argName_t s;
-
- if ( !CMD_isINIT(cmd) ) init_args(cmd);
-
- if ( *parse_status == pe_SYNTAX && BTEST(cmd_flags(cmd), pa_IGNORE) ) {
- *parse_status = pe_SUCCESS;
- }
- error = ( *parse_status != pe_SUCCESS ) ? TRUE : FALSE;
-
- if ( !BTEST(cmd_flags(cmd), pa_NOCHECK) ) {
- for (ad = ARG_FIRST(cmd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
- if (arg_type(ad) == argDummy) continue;
-
- if ( ARG_isREQUIRED(ad) && !ARG_isGIVEN(ad) ) {
- /* still didn't get a value... sigh */
- if ( ARG_isPOSITIONAL(ad) ) {
- (VOID) get_argname( arg_sname(ad), s );
- usrerr("%s required", s);
- }
- else {
- #ifdef amiga_style
- (VOID) get_kwdname( arg_sname(ad), s );
- usrerr("argument required for %s keyword", s);
- #endif
- #ifdef ibm_style
- (VOID) get_argname( arg_sname(ad), s );
- {
- char c, *pfx = getenv( "SWITCHAR" );
- c = ( pfx && *pfx ) ? *pfx : '/';
- usrerr("%s required for %c%c switch", s, c, arg_cname(ad));
- }
- #endif
- #ifdef unix_style
- (VOID) get_argname( arg_sname(ad), s );
- usrerr("%s required for %c%c flag", s, c_OPT_PFX, arg_cname(ad));
- #endif
- #ifdef vms_style
- (VOID) get_kwdname( arg_sname(ad), s );
- usrerr("value required for %c%s qualifier", *s_KWD_PFX, s);
- #endif
- }
-
- if ( !error && BTEST(cmd_flags(cmd), pa_PROMPT) ) {
- if ( !prompt_user(ad, s) ) error = TRUE;
- }
- else if ( !error ) {
- error = TRUE;
- }
- }/*if*/
- }/*for*/
- }/*if*/
-
- return (error) ? FALSE : TRUE;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: read_flags - read bitmask-flags in a string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static argMask_t read_flags( str, c_tbl, m_tbl )
- /*
- ** ^PARAMETERS:
- */
- char *str;
- /* -- the string to parse
- */
- char c_tbl[];
- /* -- a (NUL-terminated) array of alpha-numeric characters Each character
- ** is the first letter/number of a token. If the character is lowercase,
- ** then the corresponding mask is set, if it is uppercase, the corresponding
- ** mask is cleared.
- */
- argMask_t m_tbl[];
- /* -- a table of bitmasks for the given character-table. The mask to use
- ** for c_tbl[i] is m_tbl[i].
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Read_flags will parse the given string for any flags present in c_tbl[].
- ** When a flag is matched, its corresponding entry in m_tbl[] is set (or
- ** cleared) in the currently matched set of flags.
- **
- ** When the given string has been completely scanned, the resulting
- ** combination of masks that were matched is returned.
- **
- ** ^REQUIREMENTS:
- ** A '\0' or a ';' indicates the end of parsing. A token/mask may be negated
- ** by preceding it with one of '!', '^', or '~'. Tokens must be separated by
- ** one or more non-alphanumerics (other than '!', '~', and '^').
- **
- ** ^SIDE-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** The combination of bitmasks indicated by the given string.
- **
- ** ^ALGORITHM:
- ** - set masks = 0
- ** - for each "token" in str
- ** - if token doesnt match any entries in c_tbl then continue
- ** - let i = index of matched token in c_tbl
- ** - if c_tbl[i] is lowercase then OR masks with m_tbl[i]
- ** - if c_tbl[i] is uppercase then AND masks with NOT(m_tbl[i])
- ** end-for
- ** - return masks
- ***^^**********************************************************************/
-
- /* macros for parsing flags */
- #define IS_NEGCHAR(c) ( c == '!' || c == '^' || c == '~' )
- #define SKIP_TOK(s) while ( isalnum(*s) || *s == '_' || *s == '-' ) s++
- #define SKIP_SEP(s) while ( *s && !IS_NEGCHAR(*s) && !isalnum(*s) ) s++
- #define NEXT_TOK(s) SKIP_TOK(s) ; SKIP_SEP(s)
- #define TOGGLE(x) x = (x) ? FALSE : TRUE
-
- #ifdef __ANSI_C__
- static argMask_t read_flags(
- const char *str, const char c_tbl[], const argMask_t m_tbl[]
- )
- #endif
- {
- char *pos = CHARNULL;
- BOOL negated = FALSE;
- argMask_t mask = 0, flags = 0;
-
- while ( *str && *str != ';' ) {
- mask = 0;
- negated = FALSE;
-
- while ( IS_NEGCHAR(*str) ) {
- TOGGLE(negated);
- ++str;
- SKIP_SEP(str);
- }
-
- if ( (pos = strchr( c_tbl, TOLOWER(*str) )) != CHARNULL ) {
- mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
- }
- else if ( (pos = strchr( c_tbl, TOUPPER(*str) )) != CHARNULL ) {
- TOGGLE(negated);
- mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
- }
- else {
- negated = FALSE;
- }
-
- NEXT_TOK(str);
-
- if ( mask || negated ) {
- if ( negated ) BCLEAR(flags, mask);
- else /* !negated */ BSET(flags, mask);
- }/*if*/
- }/*while*/
-
- return flags;
- }
-
- #undef IS_NEGCHAR
- #undef SKIP_TOK
- #undef SKIP_SEP
- #undef NEXT_TOK
- #undef TOGGLE
-
-
- /***************************************************************************
- ** ^FUNCTION: get_usage_flags - determine user-defined usage-message format
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static argMask_t get_usage_flags( cmd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *cmd;
- /* -- command-structure to determine usage-flags for
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Through the use of an environment variable (or a VMS symbol), the user
- ** may control the syntax and the verbosity of the command-usage messages
- ** that are printed by parseargs. The desired level of verbosity may be
- ** set by defining the environment variable "USAGECNTL" to be a com-
- ** bination of strings (case insensitive). The value of each string con-
- ** trols one of three different "modes" of behavior in the displaying
- ** of usage messages: The first "mode" is "verbose" mode, which con-
- ** trols whether or not a detailed description of each argument should
- ** accompany the usual command-line sysnopsis. If verbose mode is
- ** "off", then only a command-line synopsis is printed (this is also
- ** refferred to as "terse" mode). The other two "modes" control the
- ** displaying of option syntax and long-option syntax. A mode may be
- ** explicitly disabled by preceding its corresponding string with the `!'
- ** or `-' character. The "modes" which correspond to the possible values of
- ** the "USAGECNTL" environment variable are given by the following table.
- **
- ** "Quiet"
- ** No usage message of any kind is displayed.
- **
- ** "Silent"
- ** Same as Quiet.
- **
- ** "Paged"
- ** The usage message is piped to a pager. The pager used is named by
- ** the "USAGE_PAGER" environment variable. If this variable is unset
- ** or empty (or is not the name of an executable program), the pager
- ** named by the "PAGER" environment variable us used. If this variable
- ** is unset or empty (or is not the name of an executable program) then
- ** the program "/usr/ucb/more" is used.
- **
- ** "Description"
- ** The command description is printed.
- **
- ** "Terse"
- ** Terse mode, just print command-line synopsis.
- **
- ** "Verbose"
- ** Verbose mode, print descriptions for each argument
- **
- ** "Options"
- ** Option syntax is displayed.
- **
- ** "LongOpts"
- ** Long-option syntax is displayed.
- **
- ** "KeyWords"
- ** Same as "LongOpts".
- **
- ** If the environment variable "USAGECNTL" is empty or undefined, then
- ** the default usage level (which is presently "Verbose + Options")
- ** will be used.
- **
- ** ^REQUIREMENTS:
- ** <cmd> must point to an array that has been declared using the CMD_XXXX
- ** macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
- **
- ** ^SIDE-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** The usage-flags corresponding to the value of $USAGECNTL
- **
- ** ^ALGORITHM:
- ** - If $USAGECNTL is NULL or empty, return the default flags
- ** - get the value of $USAGECNTL and call read_flags to parse it.
- ** - return the resulting flags.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static argMask_t get_usage_flags( const ARGDESC *cmd )
- #endif
- {
- register char *usage_env;
- argMask_t usg_ctl = 0;
- static CONST char usage_tokens[] = {
- 'n', 'q', 's',
- 'T', 'v',
- 'o',
- 'l', 'k',
- 'd',
- 'p',
- '\0'
- };
- static CONST argMask_t usage_masks[] = {
- usg_NONE, usg_NONE, usg_NONE,
- usg_VERBOSE, usg_VERBOSE,
- usg_OPTS,
- usg_LONGOPTS, usg_LONGOPTS,
- usg_DESCRIPTION,
- usg_PAGED
- };
-
- usage_env = getenv("USAGECNTL");
- if ( !usage_env ) {
- usg_ctl = usg_VERBOSE;
- }
- else {
- usg_ctl = read_flags( usage_env, usage_tokens, usage_masks );
- }
-
- envfree( usage_env );
-
- #ifdef unix_style
- /* make sure we print at least one of options/keywords */
- if ( !BTEST(usg_ctl, usg_OPTS|usg_LONGOPTS) ) {
- if ( BTEST(cmd_flags(cmd), pa_KWDSONLY) ) {
- BSET(usg_ctl, usg_LONGOPTS);
- }
- else {
- BSET(usg_ctl, usg_OPTS);
- }
- }
- #endif /* unix_style */
-
- return usg_ctl;
- }
-
-
- #ifndef LITE
-
- /***************************************************************************
- ** ^FUNCTION: get_parse_flags - determine user-defined parsing behavior
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID get_parse_flags( cmd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *cmd;
- /* -- command-structure to determine parse-flags for
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** The programmer may control parsing behavior through the use of
- ** parsecntl(3). The user may set this behavior through the use of the
- ** PARSECNTL environment variable. By indicating any number of flags
- ** (possibly negated) the user will directly modify the behavior of the
- ** parseargs library. Flags may be combined by placing a `+' or `|'
- ** character in between flags. A switch is negated by immediately preceding
- ** it with a `!' or `-' character. The possible "flags" are given by
- ** the following table. Flags are case-insensitive.
- **
- ** "Prompt"
- ** Prompt the user for any missing arguments that are required on the
- ** command-line. No special escaping or quoting is performed on the
- ** user input. Required arguments that expect a list of values will
- ** be repeatedly prompted for (one item per line) until a blank line
- ** (followed by a carriage return) is entered.
- **
- ** "Ignore"
- ** Ignore any unrecognized or improperly specified command-line arguments
- ** and continue execution of the program. Normally, if a required
- ** argument is unmatched (or an argument is improperly specified), a
- ** usage message is printed and program execution is terminated.
- **
- ** "OptsOnly"
- ** Under UNIX, setting this flag will disable the parsing of long-option
- ** syntax. This will cause all arguments starting with '+' to always be
- ** treated as a positional parameter (instead of a long-option).
- **
- ** "KwdsOnly"
- ** Under UNIX, setting this flag disables the parsing of single-character
- ** options. This will cause all arguments starting with '-' to always be
- ** treated as a positional parameter (instead of an option).
- **
- ** "LoptsOnly"
- ** Same as KwdsOnly.
- **
- ** "Flags1st"
- ** Setting this flag causes the parseargs library to force any and all
- ** non-positional arguments to be specified before any positional ones.
- ** As an example, under UNIX, if this flag is SET then parseargs will
- ** consider the command line "cmd -x arg" to consist of one option and
- ** one positional argument; however the command line "cmd arg -x" would
- ** be considered to consist of two positional arguments (the -x option
- ** will be unmatched).
- **
- ** If this flag is UNSET, then both of the previous examples are
- ** considered to consist of one option and one positional argument.
- **
- ** "CaseIgnore"
- ** Setting this flag causes character-case to be ignored when attempting
- ** to match single-character argument names (i.e. causes "-i" and "-I"
- ** will be considered equivalent).
- **
- ** If the environment variable "PARSECNTL" is empty or undefined, then the
- ** parsing behavior set by the programmer is used. If the programmer has
- ** not explicitly used parsecntl(3) to modify the parsing behavior, then
- ** the default behavior will be "Flags1st" for Unix Systems,
- ** "!Prompt + !Ignore" for AmigaDOS systems, "Prompt" for VMS systems,
- ** and "CaseIgnore" for MS-DOS and OS/2 systems.
- **
- ** ^REQUIREMENTS:
- ** <cmd> must point to an array that has been declared using the CMD_XXXX
- ** macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
- **
- ** ^SIDE-EFFECTS:
- ** Modifies the parse-flags of <cmd> is $PARSECNTL is non-null & non-empty.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - If $PARSECNTL is NULL or empty, return
- ** - Else turn off all flags that may be set by $PARSECNTL
- ** - get the value of $PARSECNTL and call read_flags to parse it.
- ** - set the returned flags in the cmd-structure.
- ** - return the resulting flags.
- ***^^**********************************************************************/
-
- /* the combination of parse-flags that may be set via $PARSECNTL */
- #define pa_USRFLAGS \
- (pa_PROMPT|pa_IGNORE|pa_ANYCASE|pa_OPTSONLY|pa_KWDSONLY|pa_FLAGS1ST)
-
- #ifdef __ANSI_C__
- static void get_parse_flags( ARGDESC *cmd )
- #endif
- {
- register argMask_t flags = 0;
- char *parse_env;
- static CONST char parse_tokens[] = {
- 'c',
- 'd',
- 'f',
- 'i',
- 'p',
- 'o',
- 'l','k',
- '\0'
- };
- static CONST argMask_t parse_masks[] = {
- pa_ANYCASE,
- pa_DEFAULTS,
- pa_FLAGS1ST,
- pa_IGNORE,
- pa_PROMPT,
- pa_OPTSONLY,
- pa_KWDSONLY, pa_KWDSONLY
- };
-
- if ( !CMD_isINIT(cmd) ) init_args( cmd );
-
- if ( (parse_env = getenv("PARSECNTL")) && *parse_env ) {
- flags = read_flags( parse_env, parse_tokens, parse_masks );
-
- /*
- ** mask off all the flags that may be set by $PARSECNTL
- ** (including any default flags).
- */
- BCLEAR( cmd_flags(cmd), pa_USRFLAGS );
-
- cmd_flags(cmd) |= flags;
- }/*if*/
-
- envfree( parse_env );
- }
-
- #undef pa_USRFLAGS
-
-
- /***************************************************************************
- ** ^FUNCTION: parse_user_defaults - parse user-supplied default arguments
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID parse_user_defaults( cmd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *cmd;
- /* -- the command-line object to parse
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Programs that use parseargs may be given default arguments under
- ** Unix and PCs through the use of environment variables (symbols
- ** are used for VMS systems). If a C-program or shell-script uses
- ** parseargs to implement a command named "cmd" then the environment
- ** variable CMD_ARGS will be parsed for any "default" arguments before
- ** the command-line is parsed. The command-line will over-ride any
- ** options that are specified in this environment variable (except that
- ** ARGLISTs and ARGVECs set in "CMD_ARGS" will be appended from the
- ** command-line if they are selected).
- **
- ** It is important to note that the contents of the CMD_ARGS environ-
- ** ment variable are NOT expanded by the shell and hence any special
- ** characters (such as quotes or back-slashes) will NOT be escaped or
- ** removed by parseargs. Furthermore, it will not be possible to try and
- ** use a tab, space, or newline character in the environment variable as
- ** anything other than an argument separator.
- **
- ** Lastly, parts of an option specification in CMD_ARGS may NOT be
- ** continued on the command-line. As an example, if -f requires an
- ** argument and CMD_ARGS="-f", then the command-line "cmd bah" will
- ** NOT assign "bah" as the argument to -f but will instead complain
- ** about a missing argument for -f. Similarly, if -l takes a list of
- ** arguments and CMD_ARGS="-l item1 item2", then the command-line
- ** "cmd bah", will NOT assign "bah" to the end of the list containing
- ** "item1" and "item2" but will instead treat "bah" as the first
- ** positional parameter on the command-line.
- **
- ** ^REQUIREMENTS:
- ** <cmd> should be an array of ARGDESCs declared using the CMD_XXXX macros
- ** or with the ENDOFARGS (and possible STARTOFARGS) macros.
- **
- ** ^SIDE-EFFECTS:
- ** Any matched arguments have their ARGDESCs modified accordingly.
- ** Also, the command-state is changed to reflect the fact that the
- ** environment variable has been parsed. Also, after parsing the
- ** variable, the command is partially parsed. Hence the pa_CONTINUE
- ** is set to prevent arguments from being reset when the actual command
- ** line is parsed.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - Get the value of the environment variable
- ** - if the variable is null or empty then return.
- ** - turn OFF the VARIABLE-NOT-YET-PARSED flag
- ** - turn on the NOCHECK flag so we dont check for missing required
- ** arguments in the variable (there may be more on the command-line).
- ** - parse the string and convert any arguments matched
- ** - set the NOCHECK flag back to its previous setting
- ** - set the CONTINUE flag since the command-line is now partially parsed.
- ** - return
- ***^^**********************************************************************/
-
- #define ENV_SUFFIX "_ARGS" /* suffix for env-variable with default args */
-
- #ifdef __ANSI_C__
- static VOID parse_user_defaults( ARGDESC *cmd )
- #endif
- {
- int rc;
- char *env_args;
- argName_t env_name;
- VOID usage();
-
- if ( !CMD_isINIT(cmd) ) init_args( cmd );
-
- /* if ignoring the <CMD>_ARGS variable then just return */
- if ( BTEST(cmd_state(cmd), ps_NOCMDENV) ) return;
-
- /* build the name of the environment variable */
- strncpy( env_name, ProgName, ProgNameLen );
- env_name[ProgNameLen] = 0;
- (VOID) strupr( env_name );
- strcat( env_name, ENV_SUFFIX );
-
- /* get the value of the environment variable,
- ** split it up into tokens, and parse it.
- */
- env_args = getenv(env_name);
- if ( env_args ) {
- char **argv = (char **)NULL;
- char *env_val = strdup( env_args );
- argMask_t saveflags = cmd_flags(cmd);
-
- BSET( cmd_flags(cmd), pa_NOCHECK | pa_ARGV0 | pa_COPYF );
- BSET( cmd_state(cmd), ps_NOCMDENV );
-
- /* split line up into whitespace separated tokens */
- if ( !strsplit( &argv, env_val, CHARNULL ) ) {
- free( argv );
- return;
- }
-
- #ifdef vms_style
- BSET( cmd_state(cmd), ps_NOTCMDLINE );
- #endif
-
- rc = parse_argv_style( argv, cmd );
- free( argv );
- cmd_list(cmd) = ARGDESCNULL; /* dont allow lists to continue on */
- #ifdef amiga_style
- cmd_prev(cmd) = ARGDESCNULL;
- #endif
-
- #ifdef vms_style
- BCLEAR( cmd_state(cmd), ps_NOTCMDLINE );
- #endif
-
- /* check for errors */
- if ( rc && !BTEST(cmd_flags(cmd), pa_IGNORE) ) {
- eprintf( "%.*s: syntax-error in %s \"%s\".\n",
- ProgNameLen, ProgName, USER_VARIABLE, env_name );
- eprintf( "\t%s = \"%s\"\n\n", env_name, env_args );
- free( env_val );
- usage( cmd );
- exit( exit_SYNTAX );
- }
-
- free( env_val );
- cmd_flags(cmd) = (saveflags | pa_CONTINUE);
- }
-
- envfree( env_args );
- }
-
- #undef ENV_SUFFIX
-
- #endif /* ! LITE */
-
- /***************************************************************************
- ** ^FUNCTION: parse_init -- initialize an ARGDESC for parsing
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static ARGDESC *parse_init( argdp )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *argdp[];
- /* -- pointer to the argument descriptor array.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Parse_init will perform the usual pre-parsing actions such as checking
- ** $PARSECNTL, parsing $<NAME>_ARGS, and resetting the command-line.
- **
- ** ^REQUIREMENTS:
- ** <argdp> should point to an array of ARGDESCs declared using the CMD_XXXX
- ** macros or with the ENDOFARGS (and possible STARTOFARGS) macros.
- **
- ** ^SIDE-EFFECTS:
- ** Initialize argd and parses any default arguments.
- **
- ** ^RETURN-VALUE:
- ** pointer to the arg-descriptors
- **
- ** ^ALGORITHM:
- ** - check for a NULL argdesc-array
- ** - initialize the command-object if necessary
- ** - set ProgName
- ** - read $PARSECNTL
- ** - reset the command-line if necessary (and parse $<NAME>_ARGS)
- ** - return the command-object
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static ARGDESC *parse_init( ARGDESC *argdp[] )
- #endif
- {
- register ARGDESC *cmd;
- BOOL unnamed = FALSE;
-
- /* allow null argument descriptor */
- if ( !(*argdp) ) *argdp = Empty_ArgDesc;
-
- /* initialize command-structure */
- if ( !CMD_isINIT(*argdp) ) init_args( *argdp );
- cmd = *argdp;
-
- /* save the name of this program (for error messages) */
- if ( cmd_argv0(cmd) && *(cmd_argv0(cmd)) ) {
- ProgName = cmd_argv0(cmd);
- ProgNameLen = get_argpfx(ProgName);
- }
- else if ( cmd_name(cmd) && *(cmd_name(cmd)) ) {
- ProgName = cmd_name(cmd);
- ProgNameLen = get_argpfx(ProgName);
- }
- else {
- unnamed = TRUE;
- }
-
- #ifndef LITE
- /* read PARSECNTL environment variable */
- if ( !BTEST(cmd_state(cmd), ps_NOPARSECNTL) ) {
- get_parse_flags(*argdp);
- }
- #endif
-
- /* reset argd if necessary */
- if ( !BTEST(cmd_flags(cmd), pa_CONTINUE) ) {
- reset_args( *argdp );
-
- #ifndef LITE
- /* get any options from <CMDNAME>_ARGS environment variable */
- if ( !BTEST(cmd_flags(cmd), pa_NOCMDENV) && !unnamed ) {
- parse_user_defaults( *argdp );
- }
- #endif
- }
-
- return *argdp;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: usage -- print a usage message
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- VOID usage( argd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC argd[];
- /* -- the description of expected arguments.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Given an argdesc array, usage will print the usage for the given
- ** command in the format specified by the user's USAGECNTL environment
- ** variable.
- **
- ** ^REQUIREMENTS:
- ** <argd> should be an array of ARGDESCs declared using the CMD_XXXX macros
- ** or with the ENDOFARGS (and possible STARTOFARGS) macros.
- **
- ** ^SIDE-EFFECTS:
- ** Prints on stderr.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - initialize the command-object
- ** - set ProgName
- ** - read $USAGECNTL
- ** - call <os>_usage()
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- void usage( const ARGDESC argd[] )
- #endif
- {
- register CONST ARGDESC *cmd;
- argMask_t usg_ctl = 0;
-
- /* allow null argument descriptor */
- if ( !argd ) argd = Empty_ArgDesc;
-
- if ( !CMD_isINIT(argd) ) init_args( (ARGDESC *)argd );
- cmd = argd;
-
- if ( cmd_argv0(cmd) && *(cmd_argv0(cmd)) ) {
- ProgName = cmd_argv0(cmd);
- ProgNameLen = get_argpfx(ProgName);
- }
- else if ( cmd_name(cmd) && *(cmd_name(cmd)) ) {
- ProgName = cmd_name(cmd);
- ProgNameLen = get_argpfx(ProgName);
- }
-
- usg_ctl = get_usage_flags(cmd);
- if ( !BTEST(usg_ctl, usg_NONE) ) print_usage_style( argd, usg_ctl );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: parsecntl - control various aspects of command-line parsing
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int parsecntl( argd, cntl, mode, va_alist )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *argd;
- /* -- The command-object to operate upon
- */
- parsecntl_t cntl;
- /* -- The control-code corresponding to the desired operation
- */
- parsemode_t mode;
- /* -- The mode of the operation (read, write, or both)
- */
- va_dcl
- /* -- any further args followed by the item to be read/written
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Parsecntl will read and/or write the desired attributes of the given
- ** command-object. The attributes to be operated upon are specified by
- ** the second parameter to parsecntl. The desired mode (read, write, or
- ** both) are specified by the third parameter to parsecntl. If the
- ** operation to be performed is pc_ARGFLAGS, then the fourth argument to
- ** parsecntl should be the keyword name of the argument whose flags are to
- ** be retrieved. The last parameter to parsecntl is always the object to
- ** contain the attribute(s) to be read/written. If the attribute(s) are
- ** to be read (regardless of whether or not they are also being changed)
- ** then the last argument should be a pointer to an object, otherwise the
- ** last argument should be the object itself.
- **
- ** If mode is pc_READ, then the desired attributes are copied into the
- ** object pointed to by the last parameter. If the mode is pc_WRITE, then
- ** the attributes from the last parameter are copied to the command-
- ** object. If mode is pc_RDWR, then the attributes pointed to by the last
- ** parameter are copied to the command-object, and then the previous
- ** value of these attributes (before they were overwritten) is copied
- ** into the object pointed to by the last parameter.
- **
- ** If cntl is pc_ARGFLAGS, then the only valid mode pc_READ. All other
- ** attributes may be written or read by parsecntl.
- **
- ** ^REQUIREMENTS:
- ** If mode is READ or READ+WRITE then the last argument should be the
- ** address of the desired object, otherwise it should be the object itself.
- **
- ** ^SIDE-EFFECTS:
- ** None if the mode is READ, otherwise, the desired attibutes are (re)set
- **
- ** ^RETURN-VALUE:
- ** pe_SYSTEM
- ** -- If a system error occurred
- **
- ** pe_SUCCESS
- ** -- success, no errors encountered.
- **
- ** pe_DEFARGS
- ** -- an attempt (using parsecntl()) was made to change the
- ** default arg-search list of a command to point to an argdesc-array
- ** which already has the given command on its default arg-search list
- ** (which would cause an infinite loop when attempting to match an
- ** unknown command-line argument).
- **
- ** pe_NOMATCH
- ** -- unable to match the named argument for the pc_ARGFLAGS cntl
- **
- ** pe_BADMODE
- ** -- bad mode for given command in parsecntl()
- **
- ** pe_BADCNTL
- ** -- bad command for parsecntl
- **
- ** ^ALGORITHM:
- ** - if cntl is pc_ARGFLAGS
- ** - if mode is pc_WRITE or pc_RDWR then return pe_BADMODE
- ** - get the argument name and try to match it.
- ** - if no match, then return pe_NOMATCH
- ** - copy the flags for the matched argument
- ** - return pe_SUCCESS
- ** - else if cntl is pc_PARSEFLAGS
- ** - save the current parseflags.
- ** - if requested, (re)set the current flags
- ** - if requested, copy the old flags
- ** - return pe_SUCCESS
- ** - else if cntl is pc_DEFARGS
- ** - save the current default-args
- ** - make sure the given default-args does not already contain argd
- ** - if the above assertion failed, return pe_DEFARGS
- ** - if requested, (re)set the default-args
- ** - if requested, copy the old default-args
- ** - return pe_SUCCESS
- ** - else ( cntl must be in {pc_NAME, pc_PURPOSE, pc_DESCRIPTION} )
- ** - save the current setting of the attribute-string
- ** - if requested, (re)set the current attribute-string
- ** - if requested, copy the old attribute-string
- ** - return pe_SUCCESS
- ** endif
- ***^^**********************************************************************/
-
- /*
- ** define some convenience macros to determine if we are
- ** reading/writing from/to the argdesc-array.
- */
- #define isREADING(pmode) (pmode == pc_READ || pmode == pc_RDWR)
- #define isWRITING(pmode) (pmode == pc_WRITE || pmode == pc_RDWR)
-
- #ifdef __ANSI_C__
- int parsecntl( ARGDESC *argd, parsecntl_t cntl, parsemode_t mode, ... )
- #endif
- {
- register ARGDESC *cmd;
- register int rc = pe_SUCCESS;
- va_list ap;
-
- if ( !argd ) return rc;
- if ( !CMD_isINIT(argd) ) init_args(argd);
- cmd = argd;
- VA_START( ap, mode ); /* get argument to match */
-
- /* now figure out what to do and go do it! */
- switch( cntl ) {
- case pc_ARGFLAGS : /* get/set arg-flags */
- {
- register ARGDESC *ad, *args;
- char *name = VA_ARG( ap, char * );
- argMask_t *argflags;
- BOOL is_match = FALSE;
-
- /* first we have to find the argument whose flags we need */
- for (args = argd ; args && !is_match ; args = cmd_defargs(args)) {
- for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
- if ( arg_type(ad) == argDummy ) continue;
-
- if ( match(name, arg_sname(ad)) == 0 ) {
- is_match = TRUE;
- break;
- }/*if*/
- }/*foreach arg*/
- }/*foreach argdesc*/
-
- if ( !is_match ) {
- VA_END(ap);
- return pe_NOMATCH;
- }
-
- /* now that we found it - retrieve the argument flags */
- if ( isREADING(mode) ) {
- argflags = VA_ARG( ap, argMask_t * );
- *argflags = arg_flags(ad);
- }
- else {
- rc = pe_BADMODE; /* parsecntl() wont set ARGFLAGS */
- }
- }/*block*/
- break;
-
- case pc_PARSEFLAGS : /* get/set the parse-flags */
- {
- argMask_t *pflags, flags;
-
- /* get value from call-stack (dependent on the mode) */
- if ( isREADING(mode) ) {
- pflags = VA_ARG( ap, argMask_t * );
- flags = cmd_flags(cmd);
- }
- else {
- flags = (argMask_t) VA_ARG( ap, int );
- pflags = &flags;
- }
-
- /* perform the desired action(s) */
- if ( isWRITING(mode) ) cmd_flags(cmd) = *pflags;
- if ( isREADING(mode) ) *pflags = flags;
- }/*block*/
- break;
-
- case pc_DEFARGS : /* get/set the default arguments */
- {
- ARGDESC **pdefargd, *defargd;
-
- /* get value from call-stack (dependent on the mode) */
- if ( isREADING(mode) ) {
- pdefargd = VA_ARG( ap, ARGDESC ** );
- defargd = cmd_defargs(cmd);
- }
- else {
- defargd = VA_ARG( ap, ARGDESC * );
- pdefargd = &defargd;
- }
-
- if ( isWRITING(mode) ) {
- ARGDESC *args;
-
- if ( !CMD_isINIT(*pdefargd) ) init_args( *pdefargd );
-
- /* make sure we are not on the default-argdesc's
- ** default-argument hierarchy (or an infinite loop
- ** will result).
- */
- for ( args = *pdefargd; rc && args; args = cmd_defargs(args) ) {
- if ( args == cmd ) rc = pe_DEFARGS;
- }
- if ( !rc ) cmd_defargs(cmd) = *pdefargd; /* set new defaults */
- }/*if*/
-
- if ( isREADING(mode) ) *pdefargd = defargd;
- }
- break;
-
- case pc_NAME : /* get/set name */
- case pc_PURPOSE : /* get/set purpose */
- case pc_DESCRIPTION : /* get/set description */
- {
- CONST char *str, **pstr;
-
- /* get value from call-stack (dependent on the mode) */
- if ( isREADING(mode) ) {
- pstr = VA_ARG( ap, CONST char ** );
- if ( cntl == pc_NAME ) {
- if ( !BTEST(cmd_state(cmd), ps_USERNAME|ps_FREENAME) ) {
- if ( cmd_name(argd) ) {
- int n;
- char *p;
-
- n = get_argpfx( (char *)cmd_name(argd) );
- p = (char *)malloc( n+1 );
- if ( !p ) {
- syserr( "malloc failed in parsecntl()" );
- }
- strncpy(p, (char *)cmd_name(argd), n);
- p[n] = 0;
- cmd_name(argd) = p;
- BSET(cmd_state(cmd), ps_FREENAME);
- }
- }
- str = cmd_name(cmd);
- }
- else if ( cntl == pc_PURPOSE ) {
- if ( !BTEST(cmd_state(cmd), ps_USERPURPOSE|ps_FREEPURPOSE) ) {
- if ( cmd_purpose(argd) ) {
- int n;
- char *p, *q;
-
- p = get_argdesc( (char *)cmd_purpose(argd), &n );
- if ( p ) {
- q = (char *)malloc( n+1 );
- if ( !q ) {
- syserr( "malloc failed in parsecntl()" );
- }
- strncpy(q, p, n);
- q[n] = 0;
- p = q;
- BSET(cmd_state(cmd), ps_FREEPURPOSE);
- }
- cmd_purpose(cmd) = p;
- }
- }
- str = cmd_purpose(cmd);
- }
- else /* cntl == pc_DESCRIPTION */ str = cmd_description(cmd);
- }
- else {
- str = VA_ARG( ap, CONST char * );
- pstr = &str;
- }
-
- /* perform the desired action(s) */
- if ( isWRITING(mode) ) {
- if ( cntl == pc_NAME ) {
- if ( BTEST(cmd_state(cmd), ps_FREENAME) )
- free( cmd_name(cmd) );
- BCLEAR( cmd_state(cmd), ps_FREENAME );
- BSET( cmd_state(cmd), ps_USERNAME );
- cmd_name(cmd) = *pstr;
- }
- else if ( cntl == pc_PURPOSE ) {
- if ( BTEST(cmd_state(cmd), ps_FREEPURPOSE) ) {
- free( cmd_purpose(cmd) );
- }
- BCLEAR( cmd_state(cmd), ps_FREEPURPOSE );
- BSET( cmd_state(cmd), ps_USERPURPOSE );
- cmd_purpose(cmd) = *pstr;
- }
- else /* cntl == pc_DESCRIPTION */ cmd_description(cmd) = *pstr;
- }
- if ( isREADING(mode) ) *pstr = str;
- }/*block*/
- break;
-
- default :
- rc = pe_BADCNTL;
- break;
- }/*switch*/
-
- VA_END( ap );
- return rc;
- }
-
- #undef isREADING
- #undef isWRITING
-
-
- #ifndef LITE
-
- /***************************************************************************
- ** ^FUNCTION: sparseargs - parse arguments in a string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int sparseargs( str, argd )
- /*
- ** ^PARAMETERS:
- */
- char *str;
- /* -- string to parse
- */
- ARGDESC *argd;
- /* -- pointer to argument descriptor table
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Given a single string and an argdesc array, sparseargs will parse
- ** arguments from a string in the same manner as parseargs.
- ** Sparseargs will split the given string up into a vector of whitespace
- ** separated tokens and then attempt to parse the resultant vector as if
- ** it were given as argv[] on the command-line. NO special treatment is
- ** given to characters such as single-quotes, double-quotes, or anything
- ** else. Sparseargs will always assume that any whitespace characters are
- ** intended as argument separators.
- **
- ** ^REQUIREMENTS:
- ** <str> should be non-NULL and non-empty
- **
- ** ^SIDE-EFFECTS:
- ** <str> is modified by strsplit().
- ** <argd> is modified accordingly as arguments are matched.
- **
- ** ^RETURN-VALUE:
- ** pe_SYSTEM
- ** -- If a system error occurred
- **
- ** pe_SUCCESS
- ** -- success, no errors encountered.
- **
- ** pe_SYNTAX
- ** -- If a syntax error occurs in <str>
- **
- ** ^ALGORITHM:
- ** - save current parse-flags
- ** - add pa_ARGV0 to current parse-flags
- ** - split string up into a vector of tokens
- ** - call parse init and then parse the arguments
- ** - if syntax-error, print usage and exit
- ** - restore parse-flags
- ** - return the status from parsing the vector
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int sparseargs( char *str, ARGDESC *argd )
- #endif
- {
- argMask_t saveflags;
- char **argv;
- int rc = 0;
-
- if ( !argd ) return pe_SUCCESS;
-
- if ( !CMD_isINIT(argd) ) init_args(argd);
-
- /* save old flags & initialize set parse flags */
- saveflags = cmd_flags(argd);
- BSET(cmd_flags(argd), pa_ARGV0);
-
- /* split line up into whitespace separated tokens */
- (void) strsplit( &argv, str, CHARNULL );
-
- #ifdef vms_style
- BSET( cmd_state(argd), ps_NOTCMDLINE );
- #endif
-
- rc = parse_argv_style( argv, parse_init( &argd ) );
- free( argv );
-
- #ifdef vms_style
- BCLEAR( cmd_state(argd), ps_NOTCMDLINE );
- #endif
-
- /* scan for missing required arguments */
- if ( SYNTAX_ERROR(rc, argd) ) {
- fputc( '\n', stderr );
- usage( argd );
- exit( exit_SYNTAX );
- }
-
- /* reset previous parse flags */
- cmd_flags(argd) = saveflags;
-
- return rc;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: fparseargs - parse arguments from a file
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int fparseargs( fp, argd )
- /*
- ** ^PARAMETERS:
- */
- FILE *fp;
- /* -- pointer to file to read (must already be open)
- */
- ARGDESC *argd;
- /* -- pointer to argument descriptor table
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Given a readable input stream and an argdesc array, fparseargs will
- ** parse arguments in a file in the same manner as parseargs. A
- ** maximum-line length of 255 characters is imposed. NO "escaping" of
- ** any kind is performed. Comments of a limited form are permitted: if
- ** the first non-whitespace character on a line is a '#' (or '!' for VMS)
- ** then that entire line is considered a comment and is ignored. If a
- ** value is provided for an argument that is NOT a list or a vector, then
- ** the value MUST be on the same line as the argument (in other words,
- ** "-v val" is fine but "-v\nval" is a not).
- **
- ** ^REQUIREMENTS:
- ** <fp> should be non-NULL, already opened-for-reading, file-pointer
- **
- ** ^SIDE-EFFECTS:
- ** <argd> is modified accordingly as arguments are matched.
- **
- ** ^RETURN-VALUE:
- ** pe_SYSTEM
- ** -- If a system error occurred
- **
- ** pe_SUCCESS
- ** -- success, no errors encountered.
- **
- ** pe_SYNTAX
- ** -- if a syntax error occurs in the file
- **
- ** ^ALGORITHM:
- ** - save current parse-flags
- ** - add pa_ARGV0 to current parse-flags
- ** - add pa_NOCHECK to current parse-flags (dont check until eof)
- ** - call parse init
- ** - parse the first line of the file
- ** - add pa_CONTINUE to current parse-flags
- ** - parse the rest of the file
- ** - restore parse-flags
- ** - now check for missing args if required
- ** - if syntax-error, print usage and exit
- ** - return
- ***^^**********************************************************************/
-
- #ifdef vms_style
- # define c_COMMENT '!'
- #else
- # define c_COMMENT '#'
- #endif
-
- #ifdef __ANSI_C__
- int fparseargs( FILE *fp, ARGDESC *argd )
- #endif
- {
- int rc;
- char text[ MAXLINE ];
- argMask_t saveflags;
-
- if ( !argd ) return 0;
-
- if ( !CMD_isINIT(argd) ) init_args(argd);
-
- /* We want the following scenario for the various calls to sparseargs():
- ** assume there are N lines in the input file. Then there will be N
- ** calls to sparseargs(). For all calls, we want pa_ARGV0 and pa_COPYF
- ** to be ON. Then for the ALL but the very last call, we want pa_NOCHECK
- ** to be off. For all but the very first call - we want pa_CONTINUE to
- ** be turned ON. So we have the following table:
- **
- ** Parse || invocation of sparseargs
- ** Flag || 0 | 1 | 2 .. N-1 | N
- ** ============++========+===========+=================+===============
- ** ARGV0 || on | on | on | on
- ** ------------++--------+-----------+-----------------+---------------
- ** COPYF || on | on | on | on
- ** ------------++--------+-----------+-----------------+---------------
- ** NOCHECK || on | on | on | OFF
- ** ------------++--------+-----------+-----------------+---------------
- ** CONTINUE || OFF | on | on | on
- ** ============++========+===========+=================+===============
- */
-
- /* save old flags & initialize parse flags for first call */
- saveflags = cmd_flags(argd);
- BSET(cmd_flags(argd), pa_ARGV0 | pa_NOCHECK | pa_COPYF);
-
- while ( !feof( fp ) ) {
- if ( !fgets( text, MAXLINE, fp ) ) {
- if ( ferror( fp ) ) {
- cmd_flags(argd) = saveflags;
- return pe_SYSTEM;
- }
- }/*if*/
-
- /* trim leading and trailing whitespace and check for comments */
- (VOID) strtrim( text, CHARNULL );
- if ( !text || !(*text) || *text == c_COMMENT ) continue;
-
- rc = sparseargs( text, argd );
-
- /* set up parseflags for next call */
- if ( !BTEST(cmd_flags(argd), pa_CONTINUE) ) {
- BSET(cmd_flags(argd), pa_CONTINUE);
- }
- }/*while !EOF*/
-
- if ( !BTEST(saveflags, pa_NOCHECK) ) BCLEAR(cmd_flags(argd), pa_NOCHECK);
-
- /* scan for missing required args */
- if ( SYNTAX_ERROR(rc, argd) ) {
- fputc('\n', stderr);
- usage( argd );
- exit( exit_SYNTAX );
- }
-
- /* reset previous parse flags */
- cmd_flags(argd) = saveflags;
-
- return rc;
- }
-
- #undef c_COMMENT
-
-
- /***************************************************************************
- ** ^FUNCTION: lparseargs - parse arguments from a list
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int lparseargs( argls, argd )
- /*
- ** ^PARAMETERS:
- */
- ArgList *argls;
- /* -- linked list of args to parse
- */
- ARGDESC *argd;
- /* -- pointer to argument descriptor table
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Given an ArgList and an argdesc array, lparseargs will parse arguments
- ** in an ArgList in the same manner as parseargs.
- **
- ** ^REQUIREMENTS:
- ** <argls> should be an ArgList of strings
- **
- ** ^SIDE-EFFECTS:
- ** <argd> is modified accordingly as arguments are matched.
- **
- ** ^RETURN-VALUE:
- ** pe_SYSTEM
- ** -- If a system error occurred
- **
- ** pe_SUCCESS
- ** -- success, no errors encountered.
- **
- ** pe_SYNTAX
- ** -- if a syntax error occurs
- **
- ** ^ALGORITHM:
- ** - save current parse-flags
- ** - add pa_ARGV0 to current parse-flags
- ** - make a vector out of the ArgList
- ** - call parse init
- ** - restore parse-flags
- ** - if syntax-error, print usage and exit
- ** - return
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int lparseargs( ArgList *argls, ARGDESC *argd )
- #endif
- {
- int i, argc = 0, rc = 0;
- char **argv = (char **)NULL;
- argMask_t saveflags;
- register ArgList *ls;
-
- if ( !argd ) return 0;
- if ( !CMD_isINIT(argd) ) init_args(argd);
-
- /* make 1st pass to count the args */
- for ( ls = argls; ls ; ls = L_NEXT(ls) ) {
- argc++;
- }
-
- /* allocate a NULL terminated arg-vector */
- argv = (char **)malloc( (argc + 1) * sizeof(char *) );
- if ( !argv ) return pe_SYSTEM;
- argv[ argc ] = CHARNULL;
-
- /* make 2nd pass to assign the elements of the vector */
- for ( ls = argls, i = 0 ; ls ; ls = L_NEXT(ls), i++ ) {
- argv[i] = L_STRING(ls);
- }
-
- #ifdef vms_style
- BSET( cmd_state(argd), ps_NOTCMDLINE );
- #endif
-
- /* parse the list */
- saveflags = cmd_flags(argd);
- BSET(cmd_flags(argd), pa_ARGV0);
- rc = parse_argv_style( argv, parse_init( &argd ) );
- free( argv );
-
- #ifdef vms_style
- BCLEAR( cmd_state(argd), ps_NOTCMDLINE );
- #endif
-
- /* scan for missing required arguments */
- if ( SYNTAX_ERROR(rc, argd) ) {
- fputc( '\n', stderr );
- usage( argd );
- exit( exit_SYNTAX );
- }
-
- /* reset previous parse-flags */
- cmd_flags(argd) = saveflags;
-
- return rc;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: vparseargs - parse a variable-argument list
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int vparseargs( argd, argc, va_alist )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *argd;
- /* --
- */
- int argc;
- /* -- number of arguments to parse
- */
- va_dcl
- /* -- the variable-list of arguments to parse
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Vparseargs takes an argdesc array, the number of arguments to parse,
- ** and a (possibly NULL terminated) list of argument-strings and parses
- ** them in the same manner as parseargs. Unlike sparseargs,
- ** vparseargs assumes that all parameters are already split up into
- ** tokens, hence any whitespace characters contained in any of the
- ** string-parameters are used as is (and will be considered a part of
- ** an argument name or value).
- **
- **
- ** ^REQUIREMENTS:
- ** argc must contain the number of arguments to be parsed (NOT including
- ** any terminating NULL pointer). If a NULL pointer is given as one of
- ** the arguments, and this NULL pointer appears before argc indicated
- ** the last argument would appear, then the NULL pointer will end the
- ** the list of arguments and argc is ignored.
- **
- ** ^SIDE-EFFECTS:
- ** <argd> is modified accordingly as arguments are matched.
- **
- ** ^RETURN-VALUE:
- ** pe_SYSTEM
- ** -- If a system error occurred
- **
- ** pe_SUCCESS
- ** -- success, no errors encountered.
- **
- ** pe_SYNTAX
- ** -- if a syntax error occurs
- **
- ** ^ALGORITHM:
- ** - save current parse-flags
- ** - add pa_ARGV0 to current parse-flags
- ** - make a vector out of the variable list
- ** - call parse init
- ** - restore parse-flags
- ** - if syntax-error, print usage and exit
- ** - return
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int vparseargs( ARGDESC *argd, int argc, ... )
- #endif
- {
- register char *arg;
- int i, rc = 0;
- argMask_t saveflags;
- char **argv = (char **)NULL;
- va_list ap;
-
- if ( !argd ) return 0;
- if ( !CMD_isINIT(argd) ) init_args(argd);
-
- saveflags = cmd_flags(argd);
- BSET(cmd_flags(argd), pa_ARGV0 | pa_COPYF);
-
- /* allocate a NULL terminated arg-vector */
- argv = (char **) malloc( (argc + 1) * sizeof(char *) );
- if ( !argv ) return pe_SYSTEM;
- argv[ argc ] = CHARNULL;
-
- /* assign the string into the array */
- VA_START(ap, argc);
- for ( i = 0; i < argc && (arg = VA_ARG(ap, char *)) ; i++ ) {
- argv[i] = arg;
- }
- VA_END(ap);
-
- #ifdef vms_style
- BSET( cmd_state(argd), ps_NOTCMDLINE );
- #endif
-
- /* parse the arguments */
- rc = parse_argv_style( argv, parse_init( &argd ) );
- free( argv );
-
- #ifdef vms_style
- BCLEAR( cmd_state(argd), ps_NOTCMDLINE );
- #endif
-
- /* scan for missing required arguments */
- if ( SYNTAX_ERROR(rc, argd) ) {
- fputc( '\n', stderr );
- usage( argd );
- exit( exit_SYNTAX );
- }
-
- /* reset previous parse-flags */
- cmd_flags(argd) = saveflags;
-
- return rc;
- }
-
- #endif /* ! LITE */
-
-
- /***************************************************************************
- ** ^FUNCTION: parseargs -- parse an argument vector
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- int parseargs( argv, argd )
- /*
- ** ^PARAMETERS:
- */
- char *argv[];
- /* -- pointer to the argument vector as passed to main().
- */
- ARGDESC argd[];
- /* -- the argument descriptor array.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Given a vector of string-valued arguments such as that passed to main
- ** and a vector describing the possible arguments, parseargs matches
- ** actual arguments to possible arguments, converts values to the
- ** desired type, and diagnoses problems such as missing arguments, extra
- ** arguments, and argument values that are syntactically incorrect.
- **
- ** ^REQUIREMENTS:
- ** <argv> must be non-NULL and have a NULL pointer as its last item.
- **
- ** ^SIDE-EFFECTS:
- ** <argd> is modified accordingly as arguments are matched.
- **
- ** ^RETURN-VALUE:
- ** pe_SYSTEM
- ** -- If a system error occurred
- **
- ** pe_SUCCESS
- ** -- success, no errors encountered.
- **
- ** pe_SYNTAX
- ** -- if a syntax error occurs
- **
- ** ^ALGORITHM:
- ** - call parse init
- ** - if syntax-error, print usage and exit
- ** - return
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- int parseargs( char *argv[], ARGDESC argd[] )
- #endif
- {
- register ARGDESC *cmd;
- register char **av = argv;
- int rc = pe_SUCCESS;
- argMask_t saveflags;
-
- /* allow null argument descriptor */
- if ( !argd ) argd = Empty_ArgDesc;
-
- /* initialize command-structure */
- if ( !CMD_isINIT(argd) ) init_args( argd );
- cmd = argd;
- saveflags = cmd_flags(cmd);
-
- if ( argv && !BTEST(pa_ARGV0, cmd_flags(cmd)) ) {
- cmd_argv0(cmd) = basename( *av++ );
- }/*if*/
-
- rc = parse_argv_style( av, parse_init( &argd ) );
-
- /* scan for missing required arguments */
- if ( SYNTAX_ERROR(rc, argd) ) {
- fputc( '\n', stderr );
- usage( argd );
- exit( exit_SYNTAX );
- }
-
- /* reset previous parse-flags */
- cmd_flags(cmd) = saveflags;
-
- return rc;
- }
-