v17i057: parseargs - functions to parse command line arguments, Part12/12
Brad Appleton
brad at hcx1.ssd.csd.harris.com
Tue Mar 19 01:58:17 AEST 1991
Submitted-by: Brad Appleton <brad at 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 at 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 at uunet.uu.net.
More information about the Comp.sources.misc
mailing list