home *** CD-ROM | disk | FTP | other *** search
- From: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
- Newsgroups: comp.sources.misc
- Subject: v17i057: parseargs - functions to parse command line arguments, Part12/12
- Message-ID: <1991Mar18.155817.2348@sparky.IMD.Sterling.COM>
- Date: 18 Mar 91 15:58:17 GMT
- Approved: kent@sparky.imd.sterling.com
- X-Checksum-Snefru: a4640e70 c2c7ef08 c902a69f 568cc2ba
-
- Submitted-by: Brad Appleton <brad@hcx1.ssd.csd.harris.com>
- Posting-number: Volume 17, Issue 57
- Archive-name: parseargs/part12
-
- This is part 12 of parseargs
-
- #!/bin/sh
- # this is Part.12 (part 12 of a multipart archive)
- # do not concatenate these parts, unpack them in order with /bin/sh
- # file parseargs/xparse.c continued
- #
- if test ! -r _shar_seq_.tmp; then
- echo 'Please unpack part 1 first!'
- exit 1
- fi
- (read Scheck
- if test "$Scheck" != 12; 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/xparse.c'
- else
- echo 'x - continuing file parseargs/xparse.c'
- sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/xparse.c' &&
- **
- ** ^SIDE-EFECTS:
- ** 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__
- X static BOOL is_interactive( int fd )
- #endif
- {
- #ifdef unix
- X EXTERN int isatty ARGS((int));
- X extern int errno;
- X int saverr = errno; /* save errno */
- X
- X if ( isatty(fd) )
- X return TRUE;
- X else {
- X errno = saverr;
- X return FALSE;
- X }
- #else
- #ifdef vms
- X EXTERN int isatty ARGS((int));
- X int ret = isatty( fd );
- X
- X if ( ret == -1 ) /* error with fd */
- X syserr( "error on file descriptor #%d", fd );
- X else if ( ret )
- X return TRUE;
- X else
- X return FALSE;
- #else
- X return TRUE;
- #endif
- #endif
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: get_description - get the description portion of a string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static char *get_description( str )
- /*
- ** ^PARAMETERS:
- */
- X char *str;
- /* -- the string to parse for a description
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^DESCRIPTION:
- ** Get_description null terminates the first portion of the string and
- ** returns a pointer to the second portion.
- **
- ** Two "portions" must be either separated by whitespace or the second
- ** portion may be within "(),{},[], or <>" delimiters. The second
- ** portion is assumed to begin with the first alphabetic following
- ** separator.
- **
- ** ^REQUIREMENTS:
- ** str should be non-null and non-empty
- **
- ** ^SIDE-EFECTS:
- ** The characters which separated the two portions of <str> are
- ** replaced with a single '\0'.
- **
- ** ^RETURN-VALUE:
- ** Address of the description (or NULL if the string has no description).
- **
- ** ^ALGORITHM:
- ** - locate the end of the first portion by scanning for whitespace or
- ** balanced delimiters.
- ** - locate the beginning of the second portion by scanning for the first
- ** alpha-numeric following the end of the first portion.
- ** - return the address of the description.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- X static char *get_description( char *str )
- #endif
- {
- X register char *description = CHARNULL;
- X BOOL is_end = FALSE, is_balanced = FALSE;
- X char *p;
- X static CONST char whitespace[] = " \t\n\r\f\v";
- X static CONST char beg_portion[] = "(<{[";
- X static CONST char end_portion[] = ")>}]";
- X
- X description = strpbrk( str, whitespace );
- X if ( description ) {
- X is_end = TRUE;
- X *description++ = '\0'; /* null terminate the 1st portion */
- X while ( isspace(*description) ) ++description; /* trim leading ' ' */
- X }
- X
- X if ( !is_end ) {
- X p = strpbrk( str, beg_portion );
- X if ( p ) description = p;
- X }
- X
- X if ( description ) {
- X if ( !is_end ) { /* null terminate and skip leading '(' */
- X is_end = is_balanced = TRUE;
- X *description++ = '\0';
- X }
- X else if ( strchr(beg_portion, *description) ) {
- X is_balanced = TRUE;
- X ++description;
- X }
- X if ( is_balanced ) { /* remove trailing ')' */
- X p = description + (strlen( description ) - 1);
- X if ( strchr(end_portion, *p) ) *p = '\0';
- X }
- X }/*end-if*/
- X
- X if ( description && !is_balanced ) {
- X while ( !isalnum(*description) ) ++description;
- X }
- X
- X return description;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: init_args - Initialize the command object
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X VOID init_args( argd )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC argd[];
- /* -- the array of command-arguments to initialize.
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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__
- X void init_args( ARGDESC argd[] )
- #endif
- {
- X register ARGDESC *ad, *anchor;
- X int ad_count = 0;
- X BOOL old_style = FALSE;
- X char *description = (char *)NULL, *purpose = (char *)NULL;
- X
- X if ( !argd ) return;
- X
- X /* dont initialize if its already been done */
- X if ( CMD_isINIT(argd) ) return;
- X
- X if ( !ARG_isEND(argd) ) old_style = TRUE;
- X
- X /* determine the argument count and preprocess each ad-entry */
- X anchor = ( old_style ) ? argd : ARG_FIRST(argd);
- X for ( ad = anchor ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- X ad_count++;
- X
- X /* set-up any positional args that we know of */
- X if ( ARG_isPOSONLY(ad) ) BSET( arg_flags(ad), ARGPOS );
- X
- X /* set-up any default ARGNOVALs that we know of */
- X if (ARG_isBOOLEAN(ad) || ARG_isPSEUDOARG(ad))
- X BSET( arg_flags(ad), ARGNOVAL );
- X
- X description = get_description( (char *)arg_sname(ad) );
- X if ( description ) {
- X BSET(arg_flags(ad), ARGDESCRIBED);
- X strcpy( (char *)arg_sdesc(ad), description );
- X }
- X }
- X
- X /* shift all the entries down one to make room for a new 1st-entry
- X ** It would've been nice to just swap the 1st and last entries but
- X ** I have to preserve the order that all positional parameters are
- X ** given in.
- X */
- X if ( old_style && ad_count > 0 ) {
- X anchor = ad + 1; /* save this position */
- X for ( ; ad > argd ; ARG_RETREAT(ad) ) {
- X memcpy( (ARBPTR)ad, (ARBPTR)(ad - 1), sizeof(ARGDESC) );
- X }/*for*/
- X memcpy( (ARBPTR)argd, (ARBPTR)anchor, sizeof(ARGDESC) );
- X }
- X else anchor = ad;
- X
- X /* set default parse-flags */
- X cmd_flags(argd) = pa_DEFAULTS;
- X
- X /* if new-style, get the purpose from the command name */
- X if ( !old_style && cmd_name(argd) ) {
- X purpose = get_description( (char *)cmd_name(argd) );
- X }
- X
- X /* set the program name */
- X if ( ProgName && *ProgName && !cmd_name(argd) ) {
- X cmd_name(argd) = ProgName;
- X }
- X
- X /* set context */
- X if ( !cmd_context(argd) ) {
- X cmd_context(argd) = (CTXDESC *) anchor;
- X cmd_state(argd) = ( old_style ) ? ps_OLDSTYLE : (ps_flags_t) 0;
- X cmd_argv0(argd) = cmd_name(argd);
- X cmd_purpose(argd) = purpose;
- X cmd_ptrs(argd) = (ARGDPTRS *)malloc( sizeof(ARGDPTRS) );
- X if ( !cmd_ptrs(argd) ) {
- X syserr( "malloc failed in init_args()" );
- X }
- X if ( argd == Default_ArgDesc ) {
- X cmd_defargs(argd) = ARGDESCNULL;
- X }
- X else {
- X if ( !CMD_isINIT(Default_ArgDesc) ) init_args( Default_ArgDesc );
- X cmd_defargs(argd) = Default_ArgDesc;
- X }
- X cmd_list(argd) = ARGDESCNULL;
- #ifdef amiga_style
- X cmd_prev(argd) = ARGDESCNULL;
- #endif
- X }
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: reset_args - (re)set a command for parsing
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static VOID reset_args( argd )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC argd[];
- /* -- array or command-line arguments to be reset
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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
- ***^^**********************************************************************/
- X
- X /* arg-flags to be reset before parsing a new command-line */
- #define ARGDEFAULTS ( ARGGIVEN | ARGVALGIVEN | ARGKEYWORD | ARGVALSEP )
- X
- #ifdef __ANSI_C__
- X static void reset_args( ARGDESC argd[] )
- #endif
- {
- X register ARGDESC *args, *ad;
- X
- X if ( !CMD_isINIT(argd) ) init_args(argd);
- X
- X /* reset the command context */
- X BCLEAR( cmd_state(argd), (ps_NOFLAGS|ps_NOCMDENV|ps_NOPARSECNTL) );
- X cmd_list(argd) = ARGDESCNULL;
- #ifdef amiga_style
- X cmd_prev(argd) = ARGDESCNULL; /* No argument requested */
- #endif
- X
- X /* clear out any cruft in the argument descriptors */
- X for ( args = argd ; args ; args = cmd_defargs(args) ) {
- X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- X BCLEAR( arg_flags(ad), ARGDEFAULTS );
- X
- #if ( defined(vms_style) || defined(amiga_style) )
- X /* vms and amiga use keywords only */
- X BSET( arg_flags(ad), ARGKEYWORD );
- #endif
- X }/*for*/
- X }/*for*/
- }
- X
- #undef ARGDEFAULTS
- X
- X
- /***************************************************************************
- ** ^FUNCTION: prompt_user - prompt the user for a missing argument
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static BOOL prompt_user( ad, argname )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *ad;
- /* -- pointer to the argument to be supplied by the user
- */
- X char *argname;
- /* -- name of the argument to be supplied by the user
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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__
- X static BOOL prompt_user( ARGDESC *ad, const char *argname )
- #endif
- {
- X BOOL error = FALSE, first = TRUE;
- X int end;
- X char buf[ MAXLINE ];
- X
- X if ( !is_interactive(STDIN) ) return FALSE;
- X
- X if ( ARG_isMULTIVAL(ad) ) {
- X fprintf(stderr, "Enter one %s per-line ", argname);
- X fprintf(stderr, "(enter a blank line to stop).\n");
- X }
- X
- X do { /* need repeated prompting for an ARGLIST or an ARGVEC */
- X /* get argument from user */
- X *buf='\0';
- #ifdef vms_style
- X fprintf(stderr, "\r%s> ", argname);
- #else
- X fprintf(stderr, "\rEnter %s: ", argname);
- #endif
- X fflush( stderr );
- X if ( !fgets(buf, MAXLINE, stdin) ) *buf = '\0';
- X
- X /* discard the newline */
- X end = strlen(buf) - 1;
- X if ( end >= 0 && buf[end] == '\n' )
- X buf[ end ] = '\0';
- X
- X /* make sure we read something! */
- X if ( *buf == '\0' ) {
- X if ( first ) {
- X usrerr( "error - no %s given", argname );
- X error = TRUE;
- X }
- X continue;
- X }
- X
- X /* try to convert what we read (remember - buf is transient) */
- X if ( HANDLE( ad, buf, TRUE ) )
- X BSET(arg_flags(ad), ARGGIVEN | ARGVALGIVEN);
- X else
- X error = TRUE;
- X
- X first = FALSE;
- X } while ( !error && ARG_isMULTIVAL(ad) && *buf );
- X
- X return (error) ? FALSE : TRUE;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: verify_argreqs - check for any missing required arguments
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static BOOL verify_argreqs( cmd, parse_status )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *cmd;
- /* -- the argdesc-array of command-line arguments.
- */
- X int *parse_status;
- /* -- address of current parse_status
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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__
- X static BOOL verify_argreqs( ARGDESC *cmd, int *parse_status )
- #endif
- {
- X register ARGDESC *ad;
- X BOOL error;
- X argName_t s;
- X
- X if ( !CMD_isINIT(cmd) ) init_args(cmd);
- X
- X if ( *parse_status == pe_SYNTAX && BTEST(cmd_flags(cmd), pa_IGNORE) ) {
- X *parse_status = pe_SUCCESS;
- X }
- X error = ( *parse_status != pe_SUCCESS ) ? TRUE : FALSE;
- X
- X if ( !BTEST(cmd_flags(cmd), pa_NOCHECK) ) {
- X for (ad = ARG_FIRST(cmd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
- X if (arg_type(ad) == argDummy) continue;
- X
- X if ( ARG_isREQUIRED(ad) && !ARG_isGIVEN(ad) ) {
- X /* still didn't get a value... sigh */
- X if ( ARG_isPOSITIONAL(ad) ) {
- X (VOID) get_name( arg_sname(ad), s );
- X usrerr("%s required", s);
- X }
- X else {
- #ifdef amiga_style
- X (VOID) get_keyword( arg_sname(ad), s );
- X usrerr("argument required for %s keyword", s);
- #endif
- #ifdef ibm_style
- X (VOID) get_name( arg_sname(ad), s );
- X {
- X char c, *pfx = getenv( "SWITCHAR" );
- X c = ( pfx && *pfx ) ? *pfx : '/';
- X usrerr("%s required for %c%c switch", s, c, arg_cname(ad));
- X }
- #endif
- #ifdef unix_style
- X (VOID) get_name( arg_sname(ad), s );
- X usrerr("%s required for %c%c flag", s, c_OPT_PFX, arg_cname(ad));
- #endif
- #ifdef vms_style
- X (VOID) get_keyword( arg_sname(ad), s );
- X usrerr("value required for %c%s qualifier", *s_KWD_PFX, s);
- #endif
- X }
- X
- X if ( !error && BTEST(cmd_flags(cmd), pa_PROMPT) ) {
- X if ( !prompt_user(ad, s) ) error = TRUE;
- X }
- X else if ( !error ) {
- X error = TRUE;
- X }
- X }/*if*/
- X }/*for*/
- X }/*if*/
- X
- X return (error) ? FALSE : TRUE;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: read_flags - read bitmask-flags in a string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static argMask_t read_flags( str, c_tbl, m_tbl )
- /*
- ** ^PARAMETERS:
- */
- X char *str;
- /* -- the string to parse
- */
- X 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.
- */
- X 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__ */
- X
- /* ^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-EFECTS:
- ** 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
- ***^^**********************************************************************/
- X
- X /* 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
- X
- #ifdef __ANSI_C__
- X static argMask_t read_flags(
- X const char *str, const char c_tbl[], const argMask_t m_tbl[]
- X )
- #endif
- {
- X char *pos = CHARNULL;
- X BOOL negated = FALSE;
- X argMask_t mask = 0, flags = 0;
- X
- X while ( *str && *str != ';' ) {
- X mask = 0;
- X negated = FALSE;
- X
- X while ( IS_NEGCHAR(*str) ) {
- X TOGGLE(negated);
- X ++str;
- X SKIP_SEP(str);
- X }
- X
- X if ( (pos = strchr( c_tbl, TOLOWER(*str) )) != CHARNULL ) {
- X mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
- X }
- X else if ( (pos = strchr( c_tbl, TOUPPER(*str) )) != CHARNULL ) {
- X TOGGLE(negated);
- X mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
- X }
- X else {
- X negated = FALSE;
- X }
- X
- X NEXT_TOK(str);
- X
- X if ( mask || negated ) {
- X if ( negated ) BCLEAR(flags, mask);
- X else /* !negated */ BSET(flags, mask);
- X }/*if*/
- X }/*while*/
- X
- X return flags;
- }
- X
- #undef IS_NEGCHAR
- #undef SKIP_TOK
- #undef SKIP_SEP
- #undef NEXT_TOK
- #undef TOGGLE
- X
- X
- /***************************************************************************
- ** ^FUNCTION: get_usage_flags - determine user-defined usage-message format
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static argMask_t get_usage_flags( cmd )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *cmd;
- /* -- command-structure to determine usage-flags for
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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 `!'
- ** 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-EFECTS:
- ** 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__
- X static argMask_t get_usage_flags( const ARGDESC *cmd )
- #endif
- {
- X register char *usage_env;
- X argMask_t usg_ctl = 0;
- X static CONST char usage_tokens[] = {
- X 'n', 'q', 's',
- X 'T', 'v',
- X 'o',
- X 'l', 'k',
- X 'd',
- X 'p',
- X '\0'
- X };
- X static CONST argMask_t usage_masks[] = {
- X usg_NONE, usg_NONE, usg_NONE,
- X usg_VERBOSE, usg_VERBOSE,
- X usg_OPTS,
- X usg_LONGOPTS, usg_LONGOPTS,
- X usg_DESCRIPTION,
- X usg_PAGED
- X };
- X
- X usage_env = getenv("USAGECNTL");
- X if ( !usage_env ) {
- X usg_ctl = usg_VERBOSE;
- X }
- X else {
- X usg_ctl = read_flags( usage_env, usage_tokens, usage_masks );
- X }
- X
- X envfree( usage_env );
- X
- #ifdef unix_style
- X /* make sure we print at least one of options/keywords */
- X if ( !BTEST(usg_ctl, usg_OPTS|usg_LONGOPTS) ) {
- X if ( BTEST(cmd_flags(cmd), pa_KWDSONLY) ) {
- X BSET(usg_ctl, usg_LONGOPTS);
- X }
- X else {
- X BSET(usg_ctl, usg_OPTS);
- X }
- X }
- #endif /* unix_style */
- X
- X return usg_ctl;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: get_parse_flags - determine user-defined parsing behavior
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static VOID get_parse_flags( cmd )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *cmd;
- /* -- command-structure to determine parse-flags for
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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 an argument is unmatched
- ** (or is improperly specified), a usage message is
- ** printed 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 exam-
- ** ple, under UNIX, if this flag is SET then parseargs
- ** will consider the command line "cmd -x arg" to con-
- ** sist of one option and one positional argument;
- ** however the command line "cmd arg -x" would be con-
- ** sidered 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 cause 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
- ** parsing behavior set by the programmer is used. If the programmer has
- ** not explicitly used parsecntl(3) to modify the parsing behavior will
- ** be "!Prompt + !Ignore" for Unix and 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-EFECTS:
- ** 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.
- ***^^**********************************************************************/
- X
- X /* the combination of parse-flags that may be set via $PARSECNTL */
- #define pa_USRFLAGS \
- X (pa_PROMPT|pa_IGNORE|pa_ANYCASE|pa_OPTSONLY|pa_KWDSONLY|pa_FLAGS1ST)
- X
- #ifdef __ANSI_C__
- X static void get_parse_flags( ARGDESC *cmd )
- #endif
- {
- X register argMask_t flags = 0;
- X char *parse_env;
- X static CONST char parse_tokens[] = {
- X 'c',
- X 'd',
- X 'f',
- X 'i',
- X 'p',
- X 'o',
- X 'l','k',
- X '\0'
- X };
- X static CONST argMask_t parse_masks[] = {
- X pa_ANYCASE,
- X pa_DEFAULTS,
- X pa_FLAGS1ST,
- X pa_IGNORE,
- X pa_PROMPT,
- X pa_OPTSONLY,
- X pa_KWDSONLY, pa_KWDSONLY
- X };
- X
- X if ( !CMD_isINIT(cmd) ) init_args( cmd );
- X
- X if ( (parse_env = getenv("PARSECNTL")) && *parse_env ) {
- X flags = read_flags( parse_env, parse_tokens, parse_masks );
- X
- X /*
- X ** mask off all the flags that may be set by $PARSECNTL
- X ** (including any default flags).
- X */
- X BCLEAR( cmd_flags(cmd), pa_USRFLAGS );
- X
- X cmd_flags(cmd) |= flags;
- X }/*if*/
- X
- X envfree( parse_env );
- }
- X
- #undef pa_USRFLAGS
- X
- X
- /***************************************************************************
- ** ^FUNCTION: parse_user_defaults - parse user-supplied default arguments
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static VOID parse_user_defaults( cmd )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *cmd;
- /* -- the command-line object to parse
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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
- ***^^**********************************************************************/
- X
- #define ENV_SUFFIX "_ARGS" /* suffix for env-variable with default args */
- X
- #ifdef __ANSI_C__
- X static VOID parse_user_defaults( ARGDESC *cmd )
- #endif
- {
- X int rc;
- X char *env_args;
- X argName_t env_name;
- X VOID usage();
- X
- X if ( !CMD_isINIT(cmd) ) init_args( cmd );
- X
- X if ( BTEST(cmd_state(cmd), ps_NOCMDENV) ) return;
- X
- X strucpy( env_name, ProgName );
- X strcat( env_name, ENV_SUFFIX );
- X
- X env_args = getenv(env_name);
- X if ( env_args ) {
- X char **argv = (char **)NULL;
- X char *env_val = strdup( env_args );
- X argMask_t saveflags = cmd_flags(cmd);
- X
- X BSET( cmd_flags(cmd), pa_NOCHECK | pa_ARGV0 | pa_COPYF );
- X BSET( cmd_state(cmd), ps_NOCMDENV );
- X
- X /* split line up into whitespace separated tokens */
- X if ( !strsplit( &argv, env_val, (char *)NULL ) ) {
- X free( argv );
- X return;
- X }
- X
- X rc = parse_argv_style( argv, cmd );
- X free( argv );
- X cmd_list(cmd) = ARGDESCNULL; /* dont allow lists to continue on */
- #ifdef amiga_style
- X cmd_prev(cmd) = ARGDESCNULL;
- #endif
- X
- X if ( rc && !BTEST(cmd_flags(cmd), pa_IGNORE) ) {
- X eprintf( "%s: syntax-error in %s \"%s\".\n",
- X ProgName, USER_VARIABLE, env_name );
- X eprintf( "\t%s = \"%s\"\n\n", env_name, env_args );
- X free( env_val );
- X usage( cmd );
- X exit( 2 );
- X }
- X
- X free( env_val );
- X cmd_flags(cmd) = (saveflags | pa_CONTINUE);
- X }
- X
- X envfree( env_args );
- }
- X
- #undef ENV_SUFFIX
- X
- X
- /***************************************************************************
- ** ^FUNCTION: parse_init -- initialize an ARGDESC for parsing
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X static ARGDESC *parse_init( argdp )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *argdp[];
- /* -- pointer to the argument descriptor array.
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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__
- X static ARGDESC *parse_init( ARGDESC *argdp[] )
- #endif
- {
- X register ARGDESC *cmd;
- X BOOL unnamed = FALSE;
- X
- X /* allow null argument descriptor */
- X if ( !(*argdp) ) *argdp = Empty_ArgDesc;
- X
- X /* initialize command-structure */
- X if ( !CMD_isINIT(*argdp) ) init_args( *argdp );
- X cmd = *argdp;
- X
- X /* save the name of this program (for error messages) */
- X if ( cmd_argv0(cmd) && *(cmd_argv0(cmd)) ) {
- X ProgName = cmd_argv0(cmd);
- X }
- X else if ( cmd_name(cmd) && *(cmd_name(cmd)) ) {
- X ProgName = cmd_name(cmd);
- X }
- X else {
- X unnamed = TRUE;
- X }
- X
- X /* read PARSECNTL environment variable */
- X if ( !BTEST(cmd_state(cmd), ps_NOPARSECNTL) ) {
- X get_parse_flags(*argdp);
- X }
- X
- X /* reset argd if necessary */
- X if ( !BTEST(cmd_flags(cmd), pa_CONTINUE) ) {
- X reset_args( *argdp );
- X
- X /* get any options from <CMDNAME>_ARGS environment variable */
- X if ( !BTEST(cmd_flags(cmd), pa_NOCMDENV) && !unnamed ) {
- X parse_user_defaults( *argdp );
- X }
- X }
- X
- X return *argdp;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: usage -- print a usage message
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X VOID usage( argd )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC argd[];
- /* -- the description of expected arguments.
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** Prints on stderr.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - initialize the command-object
- ** - set ProgName
- ** - read $USAGECNTL
- ** - call <os>_usage()
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- X void usage( const ARGDESC argd[] )
- #endif
- {
- X register CONST ARGDESC *cmd;
- X argMask_t usg_ctl = 0;
- X
- X /* allow null argument descriptor */
- X if ( !argd ) argd = Empty_ArgDesc;
- X
- X if ( !CMD_isINIT(argd) ) init_args( (ARGDESC *)argd );
- X cmd = argd;
- X
- X if ( cmd_argv0(cmd) && *(cmd_argv0(cmd)) ) {
- X ProgName = cmd_argv0(cmd);
- X }
- X else if ( cmd_name(cmd) && *(cmd_name(cmd)) ) {
- X ProgName = cmd_name(cmd);
- X }
- X
- X usg_ctl = get_usage_flags(cmd);
- X if ( !BTEST(usg_ctl, usg_NONE) ) print_usage_style( argd, usg_ctl );
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: parsecntl - control various aspects of command-line parsing
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X int parsecntl( argd, cntl, mode, va_alist )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *argd;
- /* -- The command-object to operate upon
- */
- X parsecntl_t cntl;
- /* -- The control-code corresponding to the desired operation
- */
- X parsemode_t mode;
- /* -- The mode of the operation (read, write, or both)
- */
- X va_dcl
- /* -- any further args followed by the item to be read/written
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** 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
- ***^^**********************************************************************/
- X
- X /*
- X ** define some convenience macros to determine if we are
- X ** reading/writing from/to the argdesc-array.
- X */
- #define isREADING(pmode) (pmode == pc_READ || pmode == pc_RDWR)
- #define isWRITING(pmode) (pmode == pc_WRITE || pmode == pc_RDWR)
- X
- #ifdef __ANSI_C__
- X int parsecntl( ARGDESC *argd, parsecntl_t cntl, parsemode_t mode, ... )
- #endif
- {
- X register ARGDESC *cmd;
- X register int rc = pe_SUCCESS;
- X va_list ap;
- X
- X if ( !argd ) return rc;
- X if ( !CMD_isINIT(argd) ) init_args(argd);
- X cmd = argd;
- X VA_START( ap, mode ); /* get argument to match */
- X
- X /* now figure out what to do and go do it! */
- X switch( cntl ) {
- X case pc_ARGFLAGS :
- X {
- X register ARGDESC *ad, *args;
- X char *name = VA_ARG( ap, char * );
- X int *argflags;
- X BOOL is_match = FALSE;
- X
- X for (args = argd; !is_match && args; args = cmd_defargs(argd) ) {
- X for (ad = ARG_FIRST(args); !ARG_isEND(ad); ARG_ADVANCE(ad) ) {
- X if (arg_type(ad) == argDummy) continue;
- X if ( match(name, arg_sname(ad)) == 0 ) {
- X is_match = TRUE;
- X break;
- X }
- X }/*foreach arg*/
- X }/*foreach argdesc*/
- X
- X if ( !is_match ) {
- X VA_END(ap);
- X return pe_NOMATCH;
- X }
- X
- X if ( isREADING(mode) ) {
- X argflags = VA_ARG( ap, int * );
- X *argflags = (int) arg_flags(ad);
- X }
- X else {
- X rc = pe_BADMODE; /* parsecntl() wont set ARGFLAGS */
- X }
- X }/*block*/
- X break;
- X
- X case pc_PARSEFLAGS :
- X {
- X int *pflags, flags;
- X
- X if ( isREADING(mode) ) {
- X pflags = VA_ARG( ap, int * );
- X flags = (int) cmd_flags(cmd);
- X }
- X else {
- X flags = VA_ARG( ap, int );
- X pflags = &flags;
- X }
- X
- X if ( isWRITING(mode) ) cmd_flags(cmd) = (argMask_t) *pflags;
- X if ( isREADING(mode) ) *pflags = flags;
- X }/*block*/
- X break;
- X
- X case pc_DEFARGS :
- X {
- X ARGDESC **pdefargd, *defargd;
- X
- X if ( isREADING(mode) ) {
- X pdefargd = VA_ARG( ap, ARGDESC ** );
- X defargd = cmd_defargs(cmd);
- X }
- X else {
- X defargd = VA_ARG( ap, ARGDESC * );
- X pdefargd = &defargd;
- X }
- X
- X if ( isWRITING(mode) ) {
- X ARGDESC *args;
- X
- X if ( !CMD_isINIT(*pdefargd) ) init_args( *pdefargd );
- X
- X /* make sure we are not on the default-argdesc's
- X ** default-argument hierarchy (or an infinite loop
- X ** will result).
- X */
- X for ( args = *pdefargd; rc && args; args = cmd_defargs(args) ) {
- X if ( args == cmd ) rc = pe_DEFARGS;
- X }
- X if ( !rc ) cmd_defargs(cmd) = *pdefargd; /* set new defaults */
- X }/*if*/
- X
- X if ( isREADING(mode) ) *pdefargd = defargd;
- X }
- X break;
- X
- X case pc_NAME :
- X case pc_PURPOSE :
- X case pc_DESCRIPTION :
- X {
- X CONST char *str, **pstr;
- X
- X if ( isREADING(mode) ) {
- X pstr = VA_ARG( ap, CONST char ** );
- X if ( cntl == pc_NAME ) str = cmd_name(cmd);
- X else if ( cntl == pc_PURPOSE ) str = cmd_purpose(cmd);
- X else /* cntl == pc_DESCRIPTION */ str = cmd_description(cmd);
- X }
- X else {
- X str = VA_ARG( ap, CONST char * );
- X pstr = &str;
- X }
- X
- X if ( isWRITING(mode) ) {
- X if ( cntl == pc_NAME ) cmd_name(cmd) = *pstr;
- X else if ( cntl == pc_PURPOSE ) cmd_purpose(cmd) = *pstr;
- X else /* cntl == pc_DESCRIPTION */ cmd_description(cmd) = *pstr;
- X }
- X if ( isREADING(mode) ) *pstr = str;
- X }/*block*/
- X break;
- X
- X default :
- X rc = pe_BADCNTL;
- X break;
- X }/*switch*/
- X
- X VA_END( ap );
- X return rc;
- }
- X
- #undef isREADING
- #undef isWRITING
- X
- X
- /***************************************************************************
- ** ^FUNCTION: sparseargs - parse arguments in a string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X int sparseargs( str, argd )
- /*
- ** ^PARAMETERS:
- */
- X char *str;
- /* -- string to parse
- */
- X ARGDESC *argd;
- /* -- pointer to argument descriptor table
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^DESCRIPTION:
- ** Given a single string and an argdesc array, sparseargs will parse
- ** arguments from a string in much 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-EFECTS:
- ** <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__
- X int sparseargs( char *str, ARGDESC *argd )
- #endif
- {
- X argMask_t saveflags;
- X char **argv;
- X int rc = 0;
- X
- X if ( !argd ) return pe_SUCCESS;
- X
- X if ( !CMD_isINIT(argd) ) init_args(argd);
- X
- X /* save old flags & initialize set parse flags */
- X saveflags = cmd_flags(argd);
- X BSET(cmd_flags(argd), pa_ARGV0);
- X
- X /* split line up into whitespace separated tokens */
- X if ( !strsplit( &argv, str, (char *)NULL ) ) {
- X free( argv );
- X return rc;
- X }
- X
- X rc = parse_argv_style( argv, parse_init( &argd ) );
- X
- X /* reset previous parse flags */
- X cmd_flags(argd) = saveflags;
- X
- X /* scan for missing required arguments */
- X if ( SYNTAX_ERROR(rc, argd) ) {
- X fputc( '\n', stderr );
- X usage( argd );
- X exit( 2 );
- X }
- X
- X return rc;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: fparseargs - parse arguments from a file
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X int fparseargs( fp, argd )
- /*
- ** ^PARAMETERS:
- */
- X FILE *fp;
- /* -- pointer to file to read (must already be open)
- */
- X ARGDESC *argd;
- /* -- pointer to argument descriptor table
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^DESCRIPTION:
- ** Given a readable input stream and an argdesc array, fparseargs will
- ** parse arguments in a file in much 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-EFECTS:
- ** <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
- ***^^**********************************************************************/
- X
- #ifdef vms_style
- # define c_COMMENT '!'
- #else
- # define c_COMMENT '#'
- #endif
- X
- #ifdef __ANSI_C__
- X int fparseargs( FILE *fp, ARGDESC *argd )
- #endif
- {
- X int rc;
- X char text[ MAXLINE ];
- X argMask_t saveflags;
- X
- X if ( !argd ) return 0;
- X
- X if ( !CMD_isINIT(argd) ) init_args(argd);
- X
- X /* save old flags & initialize parse flags for first call */
- X saveflags = cmd_flags(argd);
- X BSET(cmd_flags(argd), pa_ARGV0 | pa_NOCHECK | pa_COPYF);
- X
- X while ( !feof( fp ) ) {
- X if ( !fgets( text, MAXLINE, fp ) ) {
- X if ( ferror( fp ) ) {
- X cmd_flags(argd) = saveflags;
- X return pe_SYSTEM;
- X }
- X }/*if*/
- X
- X /* trim leading and trailing whitespace and check for comments */
- X (VOID) strtrim( text, (char *)NULL );
- X if ( !text || !(*text) || *text == c_COMMENT ) continue;
- X
- X rc = sparseargs( text, argd );
- X
- X /* set up parseflags for next call */
- X BSET(cmd_flags(argd), pa_CONTINUE);
- X }/*while !EOF*/
- X
- X /* reset previous parse flags */
- X cmd_flags(argd) = saveflags;
- X
- X /* scan for missing required args */
- X if ( SYNTAX_ERROR(rc, argd) ) {
- X fputc('\n', stderr);
- X usage( argd );
- X exit( 2 );
- X }
- X
- X return rc;
- }
- X
- #undef c_COMMENT
- X
- X
- /***************************************************************************
- ** ^FUNCTION: lparseargs - parse arguments from a list
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X int lparseargs( argls, argd )
- /*
- ** ^PARAMETERS:
- */
- X ArgList *argls;
- /* -- linked list of args to parse
- */
- X ARGDESC *argd;
- /* -- pointer to argument descriptor table
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^DESCRIPTION:
- ** Given an ArgList and an argdesc array, lparseargs will parse arguments
- ** in a file in much the same manner as parseargs.
- **
- ** ^REQUIREMENTS:
- ** <argls> should be an ArgList of strings
- **
- ** ^SIDE-EFECTS:
- ** <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__
- X int lparseargs( ArgList *argls, ARGDESC *argd )
- #endif
- {
- X int i, argc = 0, rc = 0;
- X char **argv = (char **)NULL;
- X argMask_t saveflags;
- X register ArgList *ls;
- X
- X if ( !argd ) return 0;
- X if ( !CMD_isINIT(argd) ) init_args(argd);
- X
- X /* make 1st pass to count the args */
- X for ( ls = argls; ls ; ls = L_NEXT(ls) ) {
- X argc++;
- X }
- X
- X /* allocate a NULL terminated arg-vector */
- X argv = (char **)malloc( (argc + 1) * sizeof(char *) );
- X if ( !argv ) return pe_SYSTEM;
- X argv[ argc ] = (char *)NULL;
- X
- X /* make 2nd pass to assign the elements of the vector */
- X for ( ls = argls, i = 0 ; ls ; ls = L_NEXT(ls), i++ ) {
- X argv[i] = L_STRING(ls);
- X }
- X
- X saveflags = cmd_flags(argd);
- X BSET(cmd_flags(argd), pa_ARGV0);
- X rc = parse_argv_style( argv, parse_init( &argd ) );
- X
- X /* reset previous parse-flags */
- X cmd_flags(argd) = saveflags;
- X
- X /* scan for missing required arguments */
- X if ( SYNTAX_ERROR(rc, argd) ) {
- X fputc( '\n', stderr );
- X usage( argd );
- X exit( 2 );
- X }
- X
- X return rc;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: vparseargs - parse a variable-argument list
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X int vparseargs( argd, argc, va_alist )
- /*
- ** ^PARAMETERS:
- */
- X ARGDESC *argd;
- /* --
- */
- X int argc;
- /* -- number of arguments to parse
- */
- X va_dcl
- /* -- the variable-list of arguments to parse
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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 much 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-EFECTS:
- ** <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__
- X int vparseargs( ARGDESC *argd, int argc, ... )
- #endif
- {
- X register char *arg;
- X int i, rc = 0;
- X argMask_t saveflags;
- X char **argv = (char **)NULL;
- X va_list ap;
- X
- X if ( !argd ) return 0;
- X if ( !CMD_isINIT(argd) ) init_args(argd);
- X
- X saveflags = cmd_flags(argd);
- X BSET(cmd_flags(argd), pa_ARGV0 | pa_COPYF);
- X
- X /* allocate a NULL terminated arg-vector */
- X argv = (char **) malloc( (argc + 1) * sizeof(char *) );
- X if ( !argv ) return pe_SYSTEM;
- X argv[ argc ] = (char *)NULL;
- X
- X VA_START(ap, argc);
- X for ( i = 0; i < argc && (arg = VA_ARG(ap, char *)) ; i++ ) {
- X argv[i] = arg;
- X }
- X VA_END(ap);
- X
- X rc = parse_argv_style( argv, parse_init( &argd ) );
- X
- X /* reset previous parse-flags */
- X cmd_flags(argd) = saveflags;
- X
- X /* scan for missing required arguments */
- X if ( SYNTAX_ERROR(rc, argd) ) {
- X fputc( '\n', stderr );
- X usage( argd );
- X exit( 2 );
- X }
- X
- X return rc;
- }
- X
- X
- /***************************************************************************
- ** ^FUNCTION: parseargs -- parse an argument vector
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- X int parseargs( argv, argd )
- /*
- ** ^PARAMETERS:
- */
- X char *argv[];
- /* -- pointer to the argument vector as passed to main().
- */
- X ARGDESC argd[];
- /* -- the argument descriptor array.
- */
- #endif /* !__ANSI_C__ */
- X
- /* ^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-EFECTS:
- ** <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__
- X int parseargs( char *argv[], ARGDESC argd[] )
- #endif
- {
- X register ARGDESC *cmd;
- X register char **av = argv;
- X int rc = pe_SUCCESS;
- X argMask_t saveflags;
- X
- X /* allow null argument descriptor */
- X if ( !argd ) argd = Empty_ArgDesc;
- X
- X /* initialize command-structure */
- X if ( !CMD_isINIT(argd) ) init_args( argd );
- X cmd = argd;
- X saveflags = cmd_flags(cmd);
- X
- X if ( argv && !BTEST(pa_ARGV0, cmd_flags(cmd)) ) {
- X cmd_argv0(cmd) = basename( *av++ );
- X }/*if*/
- X
- X rc = parse_argv_style( av, parse_init( &argd ) );
- X
- X /* reset previous parse-flags */
- X cmd_flags(cmd) = saveflags;
- X
- X /* scan for missing required arguments */
- X if ( SYNTAX_ERROR(rc, argd) ) {
- X fputc( '\n', stderr );
- X usage( argd );
- X exit( 2 );
- X }
- X
- X return rc;
- }
- SHAR_EOF
- echo 'File parseargs/xparse.c is complete' &&
- chmod 0664 parseargs/xparse.c ||
- echo 'restore of parseargs/xparse.c failed'
- Wc_c="`wc -c < 'parseargs/xparse.c'`"
- test 70930 -eq "$Wc_c" ||
- echo 'parseargs/xparse.c: original size 70930, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- rm -f _shar_seq_.tmp
- echo You have unpacked the last part
- 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.
-