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 50 Archive-name: parseargs/part05 This is part 5 of parseargs #!/bin/sh # this is Part.05 (part 5 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file parseargs/ibm_args.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 5; 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/ibm_args.c' else echo 'x - continuing file parseargs/ibm_args.c' sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/ibm_args.c' && ** an option switch on the command-line. ** ** KwdPrefix contains the single character prefix used to precede ** a keyword switch on the command-line. ***^^**********************************************************************/ static char OptPrefix='/'; static char KwdPrefix='/'; X #define isUNIXISH ( OptPrefix == '-' ) X X /*************************************************************************** ** ^GLOBAL-VARIABLE: Usage_Requested ** ** ^VISIBILITY: ** static-global (visible to all functions in this file). ** ** ^DESCRIPTION: ** Indicates whether a usage message was requested by the user ** (as opposed to triggerred by a syntax error). If the message ** is requested by the user then it is always printed in verbose ** mode and does not return an error-status-code. ***^^**********************************************************************/ static BOOL Usage_Requested = FALSE; X X X /* macros to detect an option/keyword -- watch out for side effects!! */ #define isOPT(s) \ X ( !BTEST(cmd_flags(cmd), pa_KWDSONLY) && \ X !BTEST(cmd_state(cmd), ps_NOFLAGS) && \ X *s == OptPrefix && *(s+1) \ X ) X #define isKWD(s) \ X ( !BTEST(cmd_flags(cmd), pa_OPTSONLY) && \ X !BTEST(cmd_state(cmd), ps_NOFLAGS) && \ X *s == KwdPrefix && *(s+1) \ X ) X X /*************************************************************************** ** ^FUNCTION: get_prefixes - determine short and long keyword prefixes ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID get_prefixes() #endif /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Get_prefixes will determine the prefixes to used to denote option ** switches and keyword switches on the command-line. The prefixes ** are determined by the $SWITCHAR environment varaible. The first ** character of the variable is the option-switch prefix and the second ** character is the keyword-switch prefix. ** ** If The option-switch prefix is '-' then Unix-style command-line parsing ** is performed, otherwise MS-DOS style command-line parsing is used. ** ** ^REQUIREMENTS: ** None. ** ** ^SIDE-EFECTS: ** Sets the global variables "OptPrefix" and "KwdPrefix'. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - If $SWITCHAR is NULL or empty ** - use the defaults ('/' and '/'). ** - Else ** - set the OptPrefix to the first character in SWITCHAR ** End-if ** ** - If there is a second character in SWITCHAR ** - assign it to KwdPrefix ** - Else if OptPrefix is '-' ** - then use '+' as the default KwdPrefix ** - Else ** - use '/' as the default KwdPrefix ** End-if ***^^**********************************************************************/ #ifdef __ANSI_C__ X static VOID get_prefixes( void ) #endif { X char *prefixes = getenv( "SWITCHAR" ); X X if ( prefixes && *prefixes ) { X OptPrefix = *prefixes; X KwdPrefix = *(prefixes + 1); X if ( !KwdPrefix ) KwdPrefix = (( OptPrefix == '-' ) ? '+' : '/'); X } X else { X OptPrefix = '/'; X KwdPrefix = '/'; X } } X X /*************************************************************************** ** ^FUNCTION: ibm_parse - parse MS-DOS and OS/2 arg-vectors ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X int ibm_parse( argv, argd ) /* ** ^PARAMETERS: */ X char *argv[]; /* -- the vector of string arguments from the command-line */ X ARGDESC argd[]; /* -- the programmer description of the command and its args */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Ibm_parse will parse the arguments in the given vector of strings, ** assign the corresponding values to the command-line arguments specified ** in argd, and check the syntax of the command-line. ** ** ^REQUIREMENTS: ** The final element in argv must be a NULL pointer. ** ** ^SIDE-EFECTS: ** argd is modified according to the command-line description and parameters ** ** ^RETURN-VALUE: ** pe_SUCCESS (0) if no errors are encountered ** pe_SYSTEM (-1) if a system error is encountered ** pe_SYNTAX if a syntax error is encountered ** ** ^ALGORITHM: ** - get the active option and keyword prefixes ** - determine whether to use Unix style or not (based on the prefixes) ** - for each command-line argument ** - attempt to match the argument as a keyword ** - if it is a keyword argument ** - record and convert its value (if any) ** else attempt to match the argument as an option ** if it is an option ** - record and convert its value (if any) ** else it is a positional parameter ** - record and convert its value (if any) ** else there are too many arguments ** - return pe_SYNTAX ** end-if ** end-for ***^^**********************************************************************/ #ifdef __ANSI_C__ X int ibm_parse( char *argv[], ARGDESC argd[] ) #endif { X register ARGDESC *ad, *args, *cmd; X register char **av = argv; X register char *p; X argName_t name; X argMask_t flags; X int parse_error = pe_SUCCESS; X BOOL ad_okay, is_match = FALSE; X X if ( !argd ) return parse_error; X X /* initialize command-structure */ X if ( !CMD_isINIT(argd) ) init_args( argd ); X cmd = argd; X X get_prefixes(); X X while ( av && (p = *av++) ) { X if ( isKWD(p) && X ( (OptPrefix != KwdPrefix) || *(p+2) && !strchr(s_ARG_SEP, *(p+2)) ) X ) { X char *s, c = '\0'; X X /* check for `++' to end flags */ X if ( *(p+1) == KwdPrefix && !*(p+2) ) { X BSET( cmd_state(cmd), ps_NOFLAGS ); X cmd_list(cmd) = ARGDESCNULL; X continue; X } X X /* get past prefix and look for possible argument */ X s = strpbrk(++p, s_ARG_SEP); X if(s) { X c = *s; X *s++ = '\0'; X } X X is_match = FALSE; X for ( args = argd ; args && !is_match ; args = cmd_defargs(args) ) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X if ( arg_type(ad) == argDummy ) continue; X X if ( !ARG_isPOSONLY(ad) && match(p, arg_sname(ad)) == 0 ) { X is_match = TRUE; X break; X }/*if*/ X } X } X X if ( c ) *(s-1) = c; /* restore the equal sign */ X X if ( !is_match ) { X if ( OptPrefix == KwdPrefix ) { X goto MATCHOPT; /* maybe its an option (and NOT a keyword) */ X } X usrerr("%c%s switch unknown", KwdPrefix, p); X parse_error = pe_SYNTAX; X cmd_list(cmd) = ARGDESCNULL; X continue; X } X X flags = arg_flags(ad); X if ( ARG_isGIVEN(ad) ) X BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD ); X X BSET( arg_flags(ad), ARGKEYWORD ); X X if( ARG_isMULTIVAL(ad) ) { X cmd_list(cmd) = ad; X } X else { X cmd_list(cmd) = ARGDESCNULL; X } X X /* if usage - just print usage and exit */ X if ( arg_type(ad) == argUsage ) { X Usage_Requested = TRUE; X usage(argd); X exit(1); X } X X /* ARGNOVALs are special, having no value */ X if ( ! ARG_isVALTAKEN(ad) ) { X ad_okay = HANDLE(ad, s, cmd_flags(cmd)); X if ( !ad_okay ) { X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X } X else { X BSET( arg_flags(ad), ARGGIVEN ); X ad = ARGDESCNULL; X } X continue; X }/*if ARGNOVAL*/ X X /* now get the real value */ X if (!s) { X if ( isUNIXISH ) s = *av++; X if ( !isUNIXISH || !s || isOPT(s) || isKWD(s) ) { X if ( ARG_isVALOPTIONAL(ad) ) { X BSET( arg_flags(ad), ARGGIVEN ); X } X else { X (VOID) get_keyword( arg_sname(ad), name ); X usrerr("%c%s switch requires an argument", KwdPrefix, name); X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X } X X if ( isUNIXISH ) av--; X continue; X }/*if arg*/ X if ( isUNIXISH ) BSET( arg_flags(ad), ARGVALSEP ); X }/*if empty*/ X X /* try to convert the type */ X ad_okay = HANDLE(ad, s, cmd_flags(cmd)); X if ( !ad_okay ) { X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X } X else { X BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN ); X } X X continue; X }/*if keyword*/ X else if ( isOPT(p) ) { X p++; /* skip over option prefix */ X MATCHOPT: X /* check for `--' to end flags */ X if ( *p == OptPrefix && !*(p+1) ) { X BSET( cmd_state(cmd), ps_NOFLAGS ); X cmd_list(cmd) = ARGDESCNULL; X continue; X } X X /* flag argument */ X while (*p) { X X /* find the flag in the list */ X is_match = FALSE; X for (args = argd; args && !is_match ; args = cmd_defargs(args)) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X register char c1 = arg_cname(ad); X register char c2 = *p; X X if ( arg_type(ad) == argDummy ) continue; X if ( ARG_isPOSONLY(ad) ) continue; X X if ( BTEST(cmd_flags(cmd), pa_ANYCASE) ) { X c1 = TOUPPER( c1 ); X c2 = TOUPPER( c2 ); X }/*if*/ X X if ( c1 == c2 ) { X is_match = TRUE; X break; X }/*if*/ X } X } X if ( !is_match ) { X usrerr("%c%c switch unknown", OptPrefix, *p++); X parse_error = pe_SYNTAX; X cmd_list(cmd) = ARGDESCNULL; X if ( !isUNIXISH && *p == *s_ARG_SEP ) p += strlen(p); X if ( !isUNIXISH && *p == OptPrefix ) ++p; X continue; X }/* if unknown-option */ X X flags = arg_flags(ad); X if ( ARG_isGIVEN(ad) ) X BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD ); X X if ( ARG_isMULTIVAL(ad) ) { X cmd_list(cmd) = ad; X } X else { X cmd_list(cmd) = ARGDESCNULL; X } X X /* move p up to point to the (possible) value */ X p++; X if ( !isUNIXISH && *p && strchr(s_ARG_SEP, *p) ) ++p; X X /* if usage - just print usage and exit */ X if (arg_type(ad) == argUsage) { X Usage_Requested = TRUE; X usage(argd); X exit(1); X } X X /* ARGNOVALs are special, having no value */ X if (! ARG_isVALTAKEN(ad)) { X ad_okay = HANDLE(ad, p, cmd_flags(cmd)); X X if ( !ad_okay ) { X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X }/*if*/ X else { X BSET( arg_flags(ad), ARGGIVEN ); X ad = ARGDESCNULL; X if ( ad_okay < 0 ) p -= ad_okay; X }/*else*/ X X if ( !isUNIXISH && *p == OptPrefix ) ++p; X continue; X }/*if*/ X X /* now get the real value */ X if ( !(*p) ) { X if ( isUNIXISH ) p = *av++; X if ( !isUNIXISH || !p || isOPT(p) || isKWD(p) ) { X if ( ARG_isVALOPTIONAL(ad) ) { X BSET( arg_flags(ad), ARGGIVEN ); X } X else { X (VOID) get_name(arg_sname(ad), name); X usrerr( "%s required for %c%c flag", X name, OptPrefix, arg_cname(ad) ); X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X }/*else*/ X X if ( isUNIXISH ) av--; X break; X }/*if arg*/ X if ( isUNIXISH ) BSET( arg_flags(ad), ARGVALSEP ); X }/*if empty*/ X X /* try to convert the type */ X ad_okay = HANDLE(ad, p, cmd_flags(cmd)); X if ( !ad_okay ) { X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X p += strlen(p); X }/*if*/ X else { X BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN ); X if ( isUNIXISH && ad_okay < 0 && !ARG_isVALSEPARATE(ad) ) { X p -= ad_okay; X } X else { X p += strlen(p); X } X }/*else*/ X X if ( !isUNIXISH && *p == OptPrefix ) ++p; X }/*while*/ X }/*elif option*/ X else { X /* parsing a list of arguments */ X if( cmd_list(cmd) ) { X ad = cmd_list(cmd); X flags = arg_flags(ad); X if ( ARG_isGIVEN(ad) ) X BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD ); X X BSET( arg_flags(ad), ARGVALSEP ); X X ad_okay = HANDLE(ad, p, cmd_flags(cmd)); X if ( !ad_okay ) { X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X } X X continue; X } X /* positional argument */ X is_match = FALSE; X for (args = argd; args && !is_match ; args = cmd_defargs(args)) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X if (arg_type(ad) == argDummy) continue; X X if ( ARG_isPOSITIONAL(ad) && X (!ARG_isGIVEN(ad) || ARG_isMULTIVAL(ad)) ) { X is_match = TRUE; X break; X }/*if*/ X } X } X X if ( !is_match ) { X usrerr("too any arguments"); X parse_error = pe_SYNTAX; X continue; X } X X flags = arg_flags(ad); X if ( ARG_isGIVEN(ad) ) X BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD ); X X if ( ARG_isMULTIVAL(ad) ) { X cmd_list(cmd) = ad; X } X X if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) ) { X BSET( cmd_state(cmd), ps_NOFLAGS ); X } X X BSET( arg_flags(ad), ARGVALSEP ); X X /* try to convert */ X ad_okay = HANDLE(ad, p, cmd_flags(cmd)); X if ( !ad_okay ) { X arg_flags(ad) = flags; X parse_error = pe_SYNTAX; X } X else { X BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN ); X } X }/*else*/ X }/*while*/ X X return parse_error; } X X /*************************************************************************** ** ^FUNCTION: fmtarg - format command-argument syntax ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static int fmtarg( ad, buf, usgflags ) /* ** ^PARAMETERS: */ X ARGDESC *ad; /* -- pointer to the argument to format */ X char *buf; /* -- character buffer to hold the formatted result */ X argMask_t usgflags; /* -- set of bitmasks corresponding to the value of the user's USAGECNTL ** environment variable */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Fmtarg will determine the proper command-line syntax for the ** given argument and write the result to the given buffer. ** ** ^REQUIREMENTS: ** buf must be large enough to hold the formatted result (100 characters ** should do the trick). ** ** ^SIDE-EFECTS: ** buf is overwritten. ** ** ^RETURN-VALUE: ** The number of printable characters in the argument-syntax-string ** ** ^ALGORITHM: ** Print argument usage based on whether or not the argument is ** positional, hidden, multi-valued (list or vector), etc .... ** Optional arguments and values are enclosed in square braces. ** ** Any syntax biases reflected in usgflags will be used. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static int fmtarg( const ARGDESC *ad, char *buf, argMask_t usgflags ) #endif { X /* buf must already be large enough */ X char *pos; X argName_t name, keyword; X X (VOID) get_name( arg_sname(ad), name ); X X if (ARG_isPOSITIONAL(ad)) { X sprintf( buf, "<%s>", name ); X } X else { X (VOID) get_keyword( arg_sname(ad), keyword ); X X if ( isupper(arg_cname(ad)) && toupper(*keyword) == arg_cname(ad) ) { X *keyword = toupper(*keyword); X } X X if ( !(usgflags & usg_LONGOPTS) ) { X sprintf( buf, "%c%c", OptPrefix, arg_cname(ad) ); X } X else if ( !(usgflags & usg_OPTS) ) { X sprintf( buf, "%c%s", KwdPrefix, keyword ); X } X else { /* use both */ X if ( OptPrefix == KwdPrefix && *keyword == arg_cname(ad) ) { X if ( !*(keyword+1) ) X sprintf( buf, "%c%c", OptPrefix, arg_cname(ad) ); X else X sprintf( buf, "%c%c[%s]", OptPrefix, arg_cname(ad), keyword+1 ); X } X else { X sprintf( buf, "%c%c|%c%s", OptPrefix, arg_cname(ad), X KwdPrefix, keyword ); X } X } X X pos = buf + strlen(buf); X X if ( ARG_isVALTAKEN(ad) && !ARG_isBOOLEAN(ad) && !ARG_isPSEUDOARG(ad) ) { X if ( isUNIXISH ) *(pos++) = ' '; X if ( ARG_isVALOPTIONAL(ad) ) *(pos++) = '['; X if ( !isUNIXISH ) *(pos++) = *s_ARG_SEP; X sprintf( pos, "<%s>", name ); X if ( ARG_isVALOPTIONAL(ad) ) strcat(pos, "]"); X }/*if*/ X }/*else*/ X X return strlen(buf); } X X /*************************************************************************** ** ^FUNCTION: ibm_usage - print a usage message ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X VOID ibm_usage( argd, usage_flags ) /* ** ^PARAMETERS: */ X ARGDESC *argd; /* -- the command-descriptor array */ X argMask_t usage_flags; /* -- flags set by $USAGECNTL */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Ibm_usage will print the Unix command-line usage of the given ** command on standard diagnostic output (stderr). The content of the ** usage message is controlled by the bitmasks in usage_flags which ** correspond to the settings in the user's USAGECNTL variable. ** ** ^REQUIREMENTS: ** argd should be a non-null command-line argument-descriptor array ** ** ^SIDE-EFECTS: ** Prints on stderr. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - if no usage is desired then exit ** - if paging is requested print to the pager instead of stderr ** - print the command-line syntax ** - if the description is requested print it ** - if verbose mode is requested, print the description of each argument ***^^**********************************************************************/ #ifdef __ANSI_C__ X void ibm_usage( const ARGDESC *argd, argMask_t usage_flags ) #endif { X register CONST ARGDESC *ad, *args, *cmd; X int max_cols = 80, max_lines = 24; X int ll, margin, options, longest, positionals; X BOOL first = TRUE; X FILE *fp; X X if ( !argd ) return; X X /* initialize command-structure */ X if ( !CMD_isINIT(argd) ) init_args( (ARGDESC *)argd ); X cmd = argd; X X /* force verbose-mode if requested */ X if ( Usage_Requested ) BSET( usage_flags, usg_VERBOSE ); X X if ( BTEST(usage_flags, usg_NONE) ) return; X X fp = ( BTEST(usage_flags, usg_PAGED) ) X ? pgopen( stderr, getenv("USAGE_PAGER") ) X : stderr; X X /* get screen size */ X get_winsize( fileno(fp), &max_lines, &max_cols ); X X fprintf(fp, "Usage: %s", ProgName); X X ll = strlen( ProgName ) + 7; X margin = ll + 1; X longest = 0; X X /* print Synopsis */ X for ( positionals = 0 ; positionals < 2 ; positionals++ ) { X for ( args = argd ; args ; args = cmd_defargs(args) ) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X argName_t buf; X int pl; X X /* don't display hidden arguments */ X if ( ARG_isHIDDEN(ad) ) continue; X if ( !positionals && ARG_isPOSITIONAL(ad) ) continue; X if ( positionals && !ARG_isPOSITIONAL(ad) ) continue; X X /* figure out how wide this parameter is (for printing) */ X pl = fmtarg(ad, buf, usage_flags); X X if ( pl > longest) longest = pl; X X if ( ARG_isMULTIVAL(ad) ) { X strcat( buf, "..." ); X pl += 3; X } X if ( !ARG_isREQUIRED(ad) ) { X pl += 2; X } X X /* see if this will fit */ X if ( (ll + pl + 1) > (max_cols - first) ) { X /* no... start a new line */ X fprintf(fp, "\n%*s", margin, ""); X ll = margin; X } X else { X /* yes... just throw in a space */ X fputc(' ', fp); X ++ll; X } X ll += pl; X X /* show the argument */ X if ( !ARG_isREQUIRED(ad) ) fputc('[', fp); X fprintf(fp, buf); X if ( !ARG_isREQUIRED(ad) ) fputc(']', fp); X X first = FALSE; /* not first line anymore */ X }/*for each ad */ X }/* for each argd */ X }/* for each parm-type */ X X fputc('\n', fp); X X if ( BTEST(usage_flags, usg_DESCRIPTION) ) { X CONST char *description = cmd_description(cmd); X X if ( description && *description ) { X fprintf( fp, "Description:\n" ); X indent_para(fp, max_cols, 8, "", 0, description); X fputc( '\n', fp ); X } X }/*if*/ X X if ( !BTEST(usage_flags, usg_VERBOSE) ) { X if ( pgactive(fp) ) (VOID) pgclose( fp ); X return; X } X X options = 0; X X /* print Argument descriptions */ X for ( positionals = 0 ; positionals < 2 ; positionals++ ) { X for ( args = argd ; args ; args = cmd_defargs(args) ) { X for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X argName_t buf; X X /* don't display hidden arguments */ X if ( ARG_isHIDDEN(ad) ) continue; X if ( !positionals && ARG_isPOSITIONAL(ad) ) continue; X if ( positionals && !ARG_isPOSITIONAL(ad) ) continue; X X if ( !options++ ) fprintf(fp, "Options/Arguments:\n"); X fmtarg(ad, buf, usage_flags); X indent_para( fp, max_cols, 8, buf, longest+2, arg_description(ad) ); X }/*for each ad */ X }/* for each argd */ X }/* for each parm-type */ X X if ( pgactive(fp) ) (VOID) pgclose( fp ); } X SHAR_EOF echo 'File parseargs/ibm_args.c is complete' && chmod 0664 parseargs/ibm_args.c || echo 'restore of parseargs/ibm_args.c failed' Wc_c="`wc -c < 'parseargs/ibm_args.c'`" test 23858 -eq "$Wc_c" || echo 'parseargs/ibm_args.c: original size 23858, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/parseargs.awk ============== if test -f 'parseargs/parseargs.awk' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/parseargs.awk (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/parseargs.awk (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/parseargs.awk' && #!/usr/bin/awk -f X ########################################################################## ## ^FILE: parseargs.awk - parseargs for awk programs ## ## ^DESCRIPTION: ## This file defines an awk function named parseargs to parse ## command-line arguments for awk scripts. It also contains a ## bare-bones template of what such an awk-script might contain. ## ## ^HISTORY: ## 02/21/91 Brad Appleton <brad@ssd.csd.harris.com> Created ###^^##################################################################### X X ######### ## ^FUNCTION: parseargs - parse command-line argument vectors ## ## ^SYNOPSIS: ## parseargs( argc, argv, argd, arr ) ## ## ^PARAMETERS: ## argc -- the number of elements in argv (usually ARGC-1). ## argv -- the vector of command-line arguments (usually ARGV). ## argd -- the argument-description string ## arr -- the associative array to assign command-line values from ## ## ^DESCRIPTION: ## Parseargs will invoke parseargs(1) to parse the command-line given ## in <argv> for the command defined by <argd>. The resulting values ## will be assigned to elements of the associative array given by <arr>. ## Values are assigned using using the syntax: arr [ "argname" ] = value; ## The exception to this is that if the <argname> is "ARGV" then the global ## array ARGV is reset to the given array (using tab separated fields). ## ## ^REQUIREMENTS: ## Any desired initial values for items in <arr> should be assigned BEFORE ## calling this function (using the syntax: arr[ "argname" ] = initial-val). ## ## The following global variables may be assigned before calling parseargs: ## ## PROGNAME -- name of the current awk script (default= ARGV[0]) ## PARSEOPTS -- any extra options to pass toi parseargs() (default="-ul") ## PARSEINPUT -- input file for parseargs(1) (default=unique-name) ## PARSEOUTPUT -- output file for parseargs(1) (default=unique-name) ## ## ^SIDE-EFFECTS: ## The files PARSEINPUT and PARSEOUTPUT are created and then deleted. ## ## The return value from parseargs(1) will be stored in the global-variable ## named PARSESTATUS. ## ## The global variable PARSEARGS will contain the command-line used to ## invoke parseargs(1). ## ## ARGV and ARGC may be reset, all other values are (re)set in <arr>. ## ## ^RETURN-VALUE: ## The exit code returned by parseargs(1). ## ## ^BUGS: ## Due to the limited ability of awk, scripts using parseargs(1) cannot ## use short-options (with a dash '-') because awk will attempt to interpret ## any such arguments as options to awk and remove them from ARGV (regardless ## of whether or not they are valid awk-options). Keyword options (with a ## plus sign '+') may still be used without this difficulty. Dash-options ## may be successfully processed if they did not first appear on the command ## to the awk-script, so the full syntax of unix-style options could be ## provided in an array other than ARGV. ## ## ^ALGORITHM: ## - set defaults for PROGNAME, PARSEOPTS, PARSEINPUT, and PARSEOUTPUT. ## - build the parseargs command (dont forget to quote arguments). ## - redirect input and output of the parseargs command. ## - run parseargs(1) ## - assign the exit-code from parseargs(1) to PARSESTATUS ## - remove PARSEINPUT ## - if PARSESTATUS != 0 ## - save RS and FS and reset RS = "" and FS = "\n" ## - for each record in PARSEOUTPUT ## - $1 is the argname and $2 is the value ## - if $1 is "ARGV" reset ARGV and ARGC ($2 is a tab separated array) ## - else assign arr[ $1 ] = $2 ## end-for ## - restore RS and FS to previous values ## - remove PARSEOUTPUT ## - return PARSESTATUS ###^^#### X function parseargs(argc, argv, argd, arr) { X ## set defaults -- use $$ to get a unique suffix string X if ( ! PROGNAME ) PROGNAME = ARGV[0]; X if ( ! PARSEOPTS ) PARSEOPTS = "-u -l"; X X "echo ${TMP:-/tmp}/parseargs.${$}_" | getline TMPFILE; X if ( ! PARSEINPUT ) PARSEINPUT = TMPFILE "in"; X if ( ! PARSEOUTPUT ) PARSEOUTPUT = TMPFILE "out"; X X ## build the options and required arguments for parseargs(1) X PARSEARGS = sprintf( "parseargs -s awk %s -- '%s'", PARSEOPTS, PROGNAME ); X X ## quote each elemnt in argv and append it to the parseargs-command X for ( i = 1 ; i <= argc ; i++ ) { X arg = argv[i]; X gsub( /'/, "'\\''", arg ); X PARSEARGS = PARSEARGS " '" arg "'"; X } X X ## set up i/o redirection X PARSEARGS = PARSEARGS " <" PARSEINPUT " >" PARSEOUTPUT; X print argd > PARSEINPUT; X X ## invoke parseargs(1) and save the status X PARSESTATUS = system( PARSEARGS ); X system( "/bin/rm -f " PARSEINPUT ); ## dont need input anymore X X ## if successful status, read the result X if ( PARSESTATUS == 0 ) { X save_RS = RS; save_FS = FS; X RS = ""; FS = "\n"; X while ( getline < PARSEOUTPUT > 0 ) { X gsub( /\034/, "\n" ); X if ( $1 == "ARGV" ) { X ARGC = 1 + split( $2, ARGV, "\t" ); X ARGV[0] = PROGNAME; X } X else arr[ $1 ] = $2; X } X RS = save_RS; FS = save_FS; X } X system( "/bin/rm -f " PARSEOUTPUT ); X X return PARSESTATUS; } X X BEGIN { X PROGNAME = "test.awk"; X ARGD = sprintf( "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", X "'?', ARGHIDDEN, argUsage, NULL, 'Help : print usage and exit'" , X "'S', ARGVALOPT, argStr, string, 'STRing : optional string arg'" , X "'g', ARGLIST, argStr, groups, 'newsGROUPS : groups to test'" , X "'r', ARGOPT, argInt, count, 'REPcount : group repeat count'" , X "'d', ARGOPT, argStr, dirname, 'DIRectory : working directory'" , X "'x', ARGOPT, argBool, xflag, 'Xflag : turn on X-mode'" , X "'y', ARGOPT, argUBool, yflag, 'Yflag : turn off Y-mode'" , X "'s', ARGOPT, argChar, sepch, 'SEPchar : field separator'" , X "'f', ARGLIST, argStr, files, 'files : files to process'" , X "' ', ARGREQ, argStr, name, 'name : name to use'" , X "' ', ARGLIST, argStr, argv, 'argv : any remaining arguments'" , X "ENDOFARGS" ); X X Args[ "count" ] = 1; X Args[ "dirname" ] = "."; X Args[ "sepch" ] = ","; X Args[ "yflag" ] = "TRUE"; X X rc = parseargs( ARGC-1, ARGV, ARGD, Args ); X if ( rc != 0 ) exit( rc ); X X ## print the parsed arguments (use defaults if not defined) X print "ARGUMENTS:"; X print "=========="; X X for ( i in Args ) X printf( "Args[\"%s\"] = \"%s\"\n", i, Args[i] ); X X argc = split( Args[ "argv" ], argv, "\t" ); X for ( i = 1 ; i <= argc ; i++ ) X printf( "argv[%d] = \"%s\"\n", i, argv[i] ); X } SHAR_EOF chmod 0664 parseargs/parseargs.awk || echo 'restore of parseargs/parseargs.awk failed' Wc_c="`wc -c < 'parseargs/parseargs.awk'`" test 6648 -eq "$Wc_c" || echo 'parseargs/parseargs.awk: original size 6648, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/parseargs.c ============== if test -f 'parseargs/parseargs.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/parseargs.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/parseargs.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/parseargs.c' && /************************************************************************* ** ^FILE: parseargs.c - command line interface to parseargs() ** ** ^DESCRIPTION: ** This file implements the command-line interface to the parseargs ** library. Under Unix, the user may use parseargs(1) (this program) ** to parse command-line arguments for shell scripts. At the present, ** time, VMS/DCL is not yet supported (nor are MS-DOS Batch-files). ** ** Given a command name, a vector of string-valued arguments such as that ** passed to a shell script, and a specification string 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. Other behavior such as prompting the ** user for missing arguments and ignoring as command-line syntax may be ** specified on the command-line through the use of various options, or ** through the use of the "PARSECNTL" environment variable. ** ** Given the command name and the argument specification string, ** parsearg -U ** prints a reasonably friendly version of the usage of the ** calling program on standard diagnostic output. The "verbosity" of ** the usage message may be controlled through the use of the ** "USAGECNTL" environment variable. ** ** Given the command name and the argument specification string, ** parsearg -M ** prints a template of the command-syntax on standard output that is ** suitable for input to nroff or troff using the -man macro package. ** ** ^SEE_ALSO: ** argtype(3), parseargs(1), parseargs(3), parsecntl(3), ** parseargs.pl, parseargs.awk ** test.sh, test.csh, test.ksh, test.rc, test.awk, test.pl ** ** ^BUGS: ** It does not make sense to use any arguments of type argTBool since ** parseargs currently has no way of knowing what the initial value of ** the variable is. For this reason, argTBool is not recognized as a ** valid argument type (even though it is used by parseargs(3)). By the ** same token, since the user cannot create their own arguments types on ** the fly from a shell-script, ARGNOVAL is not recognized as a valid ** argument flag. ** ** Commas will not be interpreted properly if any field in the argument ** specification string contains double quotes that are nested inside of ** double quotes, or single quotes that are nested inside of single quotes. ** ** Inside the argument specification string, any repeated string of ** commas that does not appear inside of double or single quotes will ** be treated as a single comma. ** ** Text descriptions for argument entries are automatically formatted in ** usage messages. Any attempt by the user to include tabs and/or newlines ** in the description will cause it to be formatted improperly. ** ** Parseargs cannot properly preserve any newlines in shell variables if ** the eval command is used to read its output (this is a shortcoming of ** the eval command, not of parseargs). If the user is concerned about ** this particular case, then the user should redirect the output from ** parseargs to a temporary file and use the source command in csh or the ** dot command (`.') in sh and ksh, to interpret the results; otherwise, ** newlines will be translated into spaces, or characters following a ** newline may be lost, in any variables that are set by parseargs. ** ** Parseargs(1) is subject to the same caveats as parseargs(3). ** Refer to the CAVEATS section of the parseargs(3) for more information. ** ** ^HISTORY: ** 07/18/90 Brad Appleton <brad@ssd.csd.harris.com> Created ***^^**********************************************************************/ X #include <fcntl.h> #include <stdio.h> #include <useful.h> #include "strfuncs.h" X #define PARSEARGS_PRIVATE /* include private definitions */ #include "parseargs.h" X /************************************************************************* ** ^SECTION: RETURN-CODES ** ** Parseargs may return any of the following status-codes: */ #define e_SYSTEM -1 /* -- A system error occurred */ #define e_SUCCESS 0 /* -- No errors, success!! */ #define e_USAGE 1 /* -- No errors were encountered. A usage-message (or manual-page-template) ** was explicitly requested (and printed) by the user. */ #define e_SYNTAX 2 /* -- A syntax error was encountered on the command-line. The error may ** be in the argument(s) intended for parseargs(1) or in the argument(s) ** for the invoking shell-script. */ #define e_NOENV 3 /* -- The user specified that the argument description was to be found in ** an environment variable, however the environment variable in question ** is unset or empty. */ #define e_ARGD 4 /* -- An error was encountered in the string that describes the arguments ** for the given command. */ /**^^**********************************************************************/ X X /* default shell variable values for a boolean argument */ static CONST char Default_StrTrue[] = "TRUE"; static CONST char Default_StrFalse[] = ""; X X /* define character sets */ static CONST char WhiteSpace[] = " \t\n\v\r\f\"'"; static CONST char ArgTableDelims[] = ","; static CONST char ArgFlagsDelims[] = "|+ \t\n\v\r\f"; X X /* macros to improve readability of string tests */ #define strEQ(s1,s2) !strcmp(s1, s2) #define strnEQ(s1,s2,n) !strncmp(s1, s2, n) X #define BUFFER_SIZE 1024 /* start off with 1k buffer & resize it */ #define ESCAPED_COMMA '\001' /* character to replace commas with */ X X /* determine the beginning and end of a struct */ #define c_BEGIN_STRUCT '{' #define c_END_STRUCT '}' X X /* determine beginning-of-arg-table string */ #define s_START_ARGS "STARTOFARGS" #define isSTART_ARGS(s) strnEQ(s, s_START_ARGS, 5) X X /* determine end-of-arg-table string */ #define s_END_ARGS "ENDOFARGS" #define isEND_ARGS(s) strnEQ(s, s_END_ARGS, 3) X X /* define #of entries per arg-descriptor */ #define NFIELDS 5 X X /************************************************************************** ** ^SECTION: SHELLS ** After the command line has been parsed, parseargs will print on stan- ** dard output, a script to set the shell variables which correspond to ** arguments that were present on the command-line. This script may be ** evaluated by redirecting it to a file and then executing the file, ** or by directly evaluating the output from parseargs (under most UNIX ** shells, this could be done using eval). If any arguments on the com- ** mand line contained any special characters that needed to be escaped ** from the shell, these characters will remain intact (not be evaluated ** by the shell) in the corresponding shell variable. ** ** The -s shell option may be used to tell parseargs which shell syntax ** to use. At present, parseargs only recognizes "sh", "csh", "ksh", ** "tcsh", "bash", "rc", "awk", and "perl" as valid command interpreters. ** Awk output is slightly different from that of the other shells in that ** the actual variable setting are not printed but each line of an ** associative array is printed (the first field is the array index, the ** second is the value for that index). If no shell is specified, then ** the Bourne shell ("sh") will be assumed. ** ** If the user wishes to use a value other than "TRUE" for a boolean ** flag that is true, this may be done using the "-T string" option. ** The same may also be done for a boolean flag that is false using the ** "-F string" option. ** ** Parseargs will only set the values of variables that correspond to ** arguments that were given on the command line. If a particular argu- ** ment was not supplied on the command line, then no assignment is made ** for the corresponding shell variable and it will have the same value ** that it had before parseargs was invoked. The only exception to this ** is that if the -u option is specified, then the positional parameters ** are unset before any shell variable assignments (which may reset the ** positional parameters) are made. ***^^*********************************************************************/ X X /* #defines for possible shell names and corresponding types */ typedef short shell_t; #define BASH ((shell_t) 0) #define TCSH ((shell_t) 1) #define CSH ((shell_t) 2) #define KSH ((shell_t) 3) #define SH ((shell_t) 4) #define RC ((shell_t) 5) #define AWK ((shell_t) 6) #define PERL ((shell_t) 7) X #define BOURNE_AGAIN_SHELL "bash" #define BOURNE_SHELL "sh" #define KORN_SHELL "ksh" #define C_SHELL "csh" #define RC_SHELL "rc" #define TC_SHELL "tcsh" #define AWK_LANG "awk" #define PERL_LANG "perl" X X /* structure for shell-specific info */ typedef struct { X char *varname; /* name of variable containing the positional parameters */ X char *setcmd; /* formatted string (%s is replaced with variable name) */ X char *prefix; /* beginning for variable setting */ X char *suffix; /* ending for variable setting */ X char *escape; /* format to escape chars (%c is the char to escape) */ X char *metachars; /* special characters that need to be escaped */ } shell_info; X X /* array of shell info - indexed by the #define for the shell type */ static CONST shell_info Shell[] = { X X /* BASH : Positional parms in -- ; Assignment Syntax: name="value"; */ X { "--", "%s=", "'", "';\n", "'\\%c'", "'" }, X X /* TCSH : Positional parms in argv ; Assignment Syntax: set name="value"; */ X { "argv", "set %s=", "'", "';\n", "'\\%c'", "'" }, X X /* CSH : Positional parms in argv ; Assignment Syntax: set name="value"; */ X { "argv", "set %s=", "'", "';\n", "'\\%c'", "'" }, X X /* KSH : Positional parms in -- ; Assignment Syntax: name="value"; */ X { "--", "%s=", "'", "';\n", "'\\%c'", "'" }, X X /* SH : Positional parms in -- ; Assignment Syntax: name="value"; */ X { "--", "%s=", "'", "';\n", "'\\%c'", "'" }, X X /* RC : Positional parms in -- ; Assignment Syntax: name="value"; */ X { "*", "%s=", "'", "';\n", "''", "'" }, X X /* AWK : Positional parms in ARGV; Assignment Syntax: name\nvalue\n\n; */ X { "ARGV", "%s\n", "", "\n\n", "'\034'", "\n" }, X X /* PERL : Positional parms in ARGV; Assignment Syntax: $name=value\n; */ X { "ARGV", "$%s = ", "", ";\n", "'.\"%c\".'", "'" } }; /*************************************************************************/ X X /* define all current arg-vector types */ typedef ARGVEC_T(char *) strvec_t; typedef ARGVEC_T(char) charvec_t; typedef ARGVEC_T(int) intvec_t; typedef ARGVEC_T(short) shortvec_t; typedef ARGVEC_T(long) longvec_t; typedef ARGVEC_T(float) floatvec_t; typedef ARGVEC_T(double) doublevec_t; typedef ARGVEC_T(VOID) genericvec_t; /* generic vector */ X X /* union to hold all possibles values of an argument */ typedef union { X BOOL Bool_val; X short Short_val; X int Int_val; X long Long_val; X float Float_val; X double Double_val; X char Char_val; X char *Str_val; X strvec_t Str_vec; X charvec_t Char_vec; X intvec_t Int_vec; X shortvec_t Short_vec; X longvec_t Long_vec; X floatvec_t Float_vec; X doublevec_t Double_vec; X genericvec_t Vector; } storage_t; X X /* structure to hold a command-line argument name, value, and fmt-string */ typedef struct { X CONST char *name; /* name of shell variable to use */ X storage_t value; /* storage for value of argument */ } cmdarg_t; #define CMDARGNULL (cmdarg_t *)NULL X EXTERN int eprintf ARGS((const char *, ...)); EXTERN VOID syserr ARGS((const char *, ...)); EXTERN VOID usrerr ARGS((const char *, ...)); EXTERN char *getenv ARGS((const char *)); EXTERN VOID manpage ARGS((const ARGDESC *)); EXTERN VOID perror ARGS((const char *)); X extern int errno; /* system wide error level */ X /*************************************************************************/ X /* X ** variables that are set via command-line arguments X */ static char *Cmd_Name; /* name of this program */ X static ARGDESC *UsrArgd = ARGDESCNULL; /* users arg-table */ static cmdarg_t *UsrVals = CMDARGNULL; /* variable names & values */ static int UsrArgc = 0; /* # of arg-table entries */ static shell_t UsrSh; /* shell indicator */ static BOOL UseStdin = TRUE; /* read argd from stdin */ X static char *ShellName = CHARNULL; /* name of user's shell */ static char *UsrName = CHARNULL; /* name of users program */ static char *FieldSep = " "; /* field-separators for arrays */ static strvec_t UsrArgv = ARGVEC_EMPTY(char *); /* users args */ static char *ArgdString = CHARNULL; /* argd string (with WhiteSpace) */ static char *ArgdEnv = CHARNULL; /* environment variable for argd */ static char *ArgdFname = CHARNULL; /* argd input file */ static BOOL Unset = FALSE; /* ?unset positional parms? */ static char *StrTrue = CHARNULL; /* string for TRUE values */ static char *StrFalse = CHARNULL; /* string for FALSE values */ static char OptsOnly = FALSE; /* parse options only? */ static char KwdsOnly = FALSE; /* parse keywords only? */ static BOOL ModArr = FALSE; /* modify array behavior */ static BOOL PrUsage = FALSE; /* ?just print usage? */ static BOOL PrManual = FALSE; /* ?just print manual page(s)? */ static BOOL Prompt = FALSE; /* ?prompt for missing args? */ static BOOL Ignore = FALSE; /* ?ignore bad syntax and continue? */ X /*************************************************************************/ X /* now we are ready to define the command-line */ static CMD_OBJECT X Args X CMD_NAME X "parseargs -- parse command-line arguments in shell scripts" X CMD_DESCRIPTION X "Given a description of the command-line and the command-line arguments, \ parseargs will parse all command-line arguments, convert them to their \ desired type, and print on standard output, a script of all the resulting \ shell assignment statements." X CMD_ARGUMENTS X 'U', ARGOPT, argBool, __ &PrUsage, X "usage : just print program usage, dont parse command-line", X X 'M', ARGOPT, argBool, __ &PrManual, X "man1 : just print man1 template, dont parse command-line", X X 'T', ARGOPT, argStr, __ &StrTrue, X "TRUEstr : string to use for TRUE Booleans (default=\"TRUE\")", X X 'F', ARGOPT, argStr, __ &StrFalse, X "FALSEstr : string to use for FALSE Booleans (default=\"\")", X X 'A', ARGOPT, argBool, __ &ModArr, X "array : modify the behavior of arrays", X X 'S', ARGOPT, argStr, __ &FieldSep, X "SEParator : field-separator-string used to delimit array elements \ (default=\" \")", X X 'a', ARGOPT, argStr, __ &ArgdString, X "ARGSpec : argument specification string", X X 'e', ARGOPT, argStr, __ &ArgdEnv, X "ENVarname : environment variable containing arg-spec", X X 'f', ARGOPT, argStr, __ &ArgdFname, X "FILEname : read the arg-spec from <filename> (default=stdin)", X X 'l', ARGOPT, argBool, __ &KwdsOnly, X "Long-OPTionS : long-options only - do not parse options", X X 'o', ARGOPT, argBool, __ &OptsOnly, X "OPTionS : options only - do not parse long-options", X X 's', ARGOPT, argStr, __ &ShellName, X "SHell : use <shell> command syntax (default=\"sh\")", X X 'u', ARGOPT, argBool, __ &Unset, X "unset : unset positional parameters before parsing", X X 'p', ARGOPT, argBool, __ &Prompt, X "prompt : prompt the user for missing required arguments", X X 'i', ARGOPT, argBool, __ &Ignore, X "ignore : ignore bad command-line syntax and continue processing \ (instead of aborting)", X X '-', ARGOPT, argDummy, __ NULL, X "+ : end of options - all remaining arguments are interpreted as \ positional parameters (even if one begins with '-' or '+')", X X ' ', ARGREQ, argStr, __ &UsrName, X "name : name of calling program", X X ' ', ARGOPT|ARGVEC, argStr, __ &UsrArgv, X "arguments : arguments to calling program", X X END_ARGUMENTS X CMD_END X X /*************************************************************************** ** ^FUNCTION: cleanup - deallocate all global storage ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID cleanup() #endif /* !__ANSI_C__ */ /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Cleanup is used to deallocate any global storage. It is called ** before exiting. ** ** ^REQUIREMENTS: ** None. ** ** ^SIDE-EFECTS: ** Storage associated with all dynamically allocated global-variables ** is released and set to NULL. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void cleanup( void ) #endif { X register ARGDESC *ad; X register storage_t val; X X /* free up any vectors from the command-line */ X for ( ad = ARG_FIRST(UsrArgd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) { X if ( ! BTEST(arg_flags(ad), ARGVEC) ) continue; X X val = *((storage_t *) arg_valp(ad)); X X if ( arg_type(ad) == argStr ) vecFree( val.Str_vec, char * ); X else if ( arg_type(ad) == argChar ) vecFree( val.Char_vec, char ); X else if ( arg_type(ad) == argInt ) vecFree( val.Int_vec, int ); X else if ( arg_type(ad) == argShort ) vecFree( val.Short_vec, short ); X else if ( arg_type(ad) == argLong ) vecFree( val.Long_vec, long ); X else if ( arg_type(ad) == argFloat ) vecFree( val.Float_vec, float ); X else if ( arg_type(ad) == argDouble ) vecFree( val.Double_vec, double ); X } X X /* free up tables */ X vecFree( UsrArgv, char * ); X if ( UsrArgd ) { X free( UsrArgd ); X UsrArgd = ARGDESCNULL; X } X if ( UsrVals ) { X free( UsrVals ); X UsrVals = CMDARGNULL; X } X if ( ArgdFname && !ArgdEnv ) { X free( ArgdString ); X ArgdString = CHARNULL; X } } X X /*************************************************************************** ** ^FUNCTION: ckalloc - allocate space, check for success ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static ARBPTR ckalloc( size ) /* ** ^PARAMETERS: */ X size_t size; /* -- the number of bytes to allocate */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Ckalloc will use malloc to attempt to fill the given request ** for memory. If The request cannot be met than a message is ** printed and execution is terminated. ** ** ^REQUIREMENTS: ** size should be > 0 ** ** ^SIDE-EFECTS: ** Memory is allocated that should later be deallocated using free(). ** ** ^RETURN-VALUE: ** The address of the allocated region. ** ** ^ALGORITHM: ** - Allocate space, check for success ***^^**********************************************************************/ #ifdef __ANSI_C__ X static ARBPTR ckalloc( size_t size ) #endif { X ARBPTR ptr; X X ptr = malloc( size ); X if ( !ptr ) { X eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name ); X cleanup(); X if ( errno ) perror( Cmd_Name ); X exit( e_SYSTEM ); X } X X return ptr; } X X /*************************************************************************** ** ^FUNCTION: ckrealloc - reallocate space, check for success ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static ARBPTR ckrealloc( ptr, size ) /* ** ^PARAMETERS: */ X ARBPTR ptr; /* -- address of the region to be expanded/shrunk */ X size_t size; /* -- the number of bytes to allocate */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Ckrealloc will use realloc to attempt to fill the given request ** for memory. If The request cannot be met than a message is ** printed and execution is terminated. ** ** ^REQUIREMENTS: ** size should be > 0 ** ** ^SIDE-EFECTS: ** Memory is allocated that should later be deallocated using free(). ** ** ^RETURN-VALUE: ** The address of the (re)allocated region (which may have been moved). ** ** ^ALGORITHM: ** - Reallocate space, check for success ***^^**********************************************************************/ #ifdef __ANSI_C__ X static ARBPTR ckrealloc( ARBPTR ptr, size_t size ) #endif { X ptr = realloc( ptr, (unsigned int)size ); X if ( !ptr ) { X eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name ); X cleanup(); X if ( errno ) perror( Cmd_Name ); X exit( e_SYSTEM ); X } X X return ptr; } X X /*************************************************************************** ** ^FUNCTION: escape_char - (re)map a character ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID escape_char( str, ch, esc ) /* ** ^PARAMETERS: */ X char *str; /* -- the string to be translated */ X int ch; /* -- the character to be replaced/translated */ X int esc; /* -- the replacement character to use */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Escape_char will escape all occurences of a character by replacing ** it with <esc> if the character appears in double or single quotes. ** ** ^REQUIREMENTS: ** Both <ch> and <esc> should be non-zero. ** <str> should be non-null and non-empty. ** ** ^SIDE-EFECTS: ** Each occurrence in <str> of <ch> within single or double quotes is ** replaced with <esc>. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void escape_char( char *str, int ch, int esc ) #endif { X int squoted = 0, dquoted = 0; X X for ( ; *str ; str++ ) { X if ( *str == '\'' && !dquoted ) X squoted = ~squoted; X else if ( *str == '"' && !squoted ) X dquoted = ~dquoted; X else if ( (squoted || dquoted) && *str == ch ) X *str = esc; X } } X X /*************************************************************************** ** ^FUNCTION: restore_char - restore any chars escaped by escape_char() ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID restore_char( str, ch, esc ) /* ** ^PARAMETERS: */ X char *str; /* -- the string to be translated */ X int ch; /* -- the character to be restored */ X int esc; /* -- the replacement character to use to escape the above character. */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Restore_char will attempt to undo the results of a previous call ** to escape_char by replacing each occurence of <esc> in <str> with <ch>. ** ** ^REQUIREMENTS: ** <str> should be the victim of a previous escape_char(str, ch, esc) call. ** Furthermore, <esc> should be a character that occurs only as a result ** of this call (it should be VERY uncommon). ** ** It should be noted that escape_char() only replaces characters in quotes ** whereas this routine replaces all occurrences. ** ** ^SIDE-EFECTS: ** Each occurrence of <esc> in <str> is replaced with <ch>. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X void restore_char( char *str, int ch, int esc ) #endif { X for ( ; *str ; str++ ) X if ( *str == esc ) *str = ch; } X X /*************************************************************************** ** ^FUNCTION: get_arg_type - return function corresponding to given string ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static argTypePtr_t get_arg_type( type_str ) /* ** ^PARAMETERS: */ X char *type_str; /* -- string corresponding to the name of an existing argXxxx type function. */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Get_arg_type will attempt to match <type_name> against the name of all ** known argXxxx argumnent translation routines and routine the address of ** the corresponding function. If no match is found, then an error message ** is printed and execution is terminated. ** ** ^REQUIREMENTS: ** type_str should be non-NULL and non-empty ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** Address of the corresponding function ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static argTypePtr_t get_arg_type( const char *type_str ) #endif { X register CONST char *str = type_str; X X /* translate all listXxx into argXxx */ X if ( strnEQ( str, "list", 4 ) ) X str += 4; X X if ( strnEQ( str, "arg", 3 ) ) X str += 3; X X if ( strEQ( str, "Usage" ) ) X return argUsage; X else if ( strEQ( str, "Dummy" ) ) X return argDummy; X else if ( strEQ( str, "Bool" ) ) X return argBool; X else if ( strEQ( str, "SBool" ) ) X return argSBool; X else if ( strEQ( str, "UBool" ) ) X return argUBool; X else if ( strEQ( str, "Int" ) ) X return argInt; X else if ( strEQ( str, "Short" ) ) X return argShort; X else if ( strEQ( str, "Long" ) ) X return argLong; X else if ( strEQ( str, "Float" ) ) X return argFloat; X else if ( strEQ( str, "Double" ) ) X return argDouble; X else if ( strEQ( str, "Char" ) ) X return argChar; X else if ( strEQ( str, "Str" ) ) X return argStr; X else { X eprintf( "%s: Fatal Error: invalid argument type '%s'\n", X Cmd_Name, type_str ); X cleanup(); X exit( e_ARGD ); X } } X X /*************************************************************************** ** ^FUNCTION: get_arg_flag - return BITMASK corresponding to string ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static argMask_t get_arg_flag( flag_str ) /* ** ^PARAMETERS: */ X char flag_str[]; /* -- name of an ARGXXXXX argument-flag */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Get_arg_flag will attempt to match the given string against the name of ** all valid argument-flags and return its associated bitmask. If no match ** is found, then an error message is printed and execution is terminated. ** ** ^REQUIREMENTS: ** flag_str should be non-NULL and non-empty ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** The bitmask corresponding to named ARGXXXX flag. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static argMask_t get_arg_flag( const char flag_str[] ) #endif { X if ( strnEQ( flag_str, "ARG", 3 ) ) { X if ( strEQ( flag_str+3, "OPT" ) ) return ARGOPT; X else if ( strEQ( flag_str+3, "REQ" ) ) return ARGREQ; X else if ( strEQ( flag_str+3, "POS" ) ) return ARGPOS; X else if ( strEQ( flag_str+3, "VALREQ" ) ) return ARGVALREQ; X else if ( strEQ( flag_str+3, "VALOPT" ) ) return ARGVALOPT; X else if ( strEQ( flag_str+3, "HIDDEN" ) ) return ARGHIDDEN; X else if ( strEQ( flag_str+3, "LIST" ) ) return ARGVEC; X else if ( strEQ( flag_str+3, "VEC" ) ) return ARGVEC; X else { X eprintf( "%s: Fatal Error: invalid argument flag '%s'\n", X Cmd_Name, flag_str ); X cleanup(); X exit( e_ARGD ); X } X } X else { X eprintf( "%s: Fatal Error: invalid argument flag '%s'\n", X Cmd_Name, flag_str ); X cleanup(); X exit( e_ARGD ); X } } X /*************************************************************************** ** ^FUNCTION: get_argtable_string - read in the argument-table ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static char *get_argtable_string() #endif /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Get_argtable_string will read (from standard input if UseStdin is set) ** the entire argument descriptor table into a string and return its address. ** ** Execution is terminated if there is an error reading STDIN or if the ** string is too big to fit into memory. ** ** ^REQUIREMENTS: ** Standard input should be open for reading and be non-interactive. ** ** ^SIDE-EFECTS: ** Memory is allocated that should later be deallocated using free. ** ** ^RETURN-VALUE: ** NULL if STDIN is connected to a terminal (after all, ** this program is for Non-interactive input) ** ** ^ALGORITHM: ** - start off with a 1k buffer ** - open the file (if necessary) ** - while (not eof) ** - read 1k bytes (or whatever is left). ** - increase the buffer size by 1k bytes. ** end-while ** - shrink the buffer down to the number of bytes used. ** - close the file (if we had to open it). ** - return the buffer address ***^^**********************************************************************/ #ifdef __ANSI_C__ X static char *get_argtable_string( void ) #endif { X int isatty ARGS((int)); X int fread ARGS((char *, int, int, FILE *)); X FILE *fp; X char *buf; X register int nchars = 0; /* # bytes read */ X register int bufsiz = 0; /* actual buffer-size needed */ X X /* open file if necessary */ X if ( UseStdin ) { X if ( isatty(STDIN) ) { X /* we wont read the arg-table from a terminal */ X eprintf( "\ %s: Fatal Error:\n\ \tcannot read arg-descriptor table from stdin\n\ \tif stdin is connected to a terminal\n!", X Cmd_Name ); X cleanup(); X exit( e_ARGD ); X } X errno = 0; /* reset errno if isatty() was not a terminal */ X fp = stdin; X } X else { X if ( (fp = fopen( ArgdFname, "r")) == FILENULL ) { X eprintf( "%s: Fatal error: Unable to open %s for reading\n", SHAR_EOF true || echo 'restore of parseargs/parseargs.c failed' fi echo 'End of part 5' echo 'File parseargs/parseargs.c is continued in part 6' echo 6 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.