brad@hcx1.ssd.csd.harris.com (Brad Appleton) (03/18/91)
Submitted-by: Brad Appleton <brad@hcx1.ssd.csd.harris.com> Posting-number: Volume 17, Issue 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.