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 51 Archive-name: parseargs/part06 This is part 6 of parseargs #!/bin/sh # this is Part.06 (part 6 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file parseargs/parseargs.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 6; 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/parseargs.c' else echo 'x - continuing file parseargs/parseargs.c' sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/parseargs.c' && X Cmd_Name, ArgdFname ); X cleanup(); X if ( errno ) perror( Cmd_Name ); X exit( e_SYSTEM ); X } X } X X /* get initial block for buffer */ X buf = (char *)ckalloc( BUFFER_SIZE * sizeof(char) ); X X /* X ** Loop reading characters into buffer and resizing as needed X */ X do { X /* read fildes into the buffer */ X nchars = fread( &(buf[bufsiz]), sizeof(char), BUFFER_SIZE, fp ); X if ( nchars < 0 ) { X eprintf( "\ %s: Fatal Error:\n\ \tBad status from read() while reading argument descriptor table\n", X Cmd_Name ); X free(buf); X cleanup(); X if ( errno ) perror( "" ); X exit( e_SYSTEM ); X } X errno = 0; /* errno is undefined after a succesful fread() */ X bufsiz += nchars; X X /* see if we need to grow the buffer */ X if ( nchars == BUFFER_SIZE ) X buf = (char *)ckrealloc( buf, (bufsiz + BUFFER_SIZE) * sizeof(char) ); X } while ( nchars == BUFFER_SIZE ); X X /* shrink the buffer down to the exact size used */ X buf = (char *)ckrealloc( buf, (bufsiz + 1) * sizeof(char) ); X X /* close file if necessary */ X if ( !UseStdin ) (VOID) fclose( fp ); X X return buf; } X X /*************************************************************************** ** ^FUNCTION: get_shell_type - return shell corresponding to given string ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static shell_t get_shell_type( sh_str ) /* ** ^PARAMETERS: */ X char *sh_str; /* -- string corresponding tp the basename of a shell/command-interpreter */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Get_shell_type will retrun the shell-type for the named shell. If ** No corresponding shell is known, then an error message is printed ** and execution is terminated. ** ** ^REQUIREMENTS: ** sh_str should be non-NULL and non-empty. ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** The corresponding shell-type. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static shell_t get_shell_type ( const char *sh_str ) #endif { X if ( strEQ( sh_str, BOURNE_SHELL ) ) X return SH; X else if ( strEQ( sh_str, TC_SHELL ) ) X return TCSH; X else if ( strEQ( sh_str, C_SHELL ) ) X return CSH; X else if ( strEQ( sh_str, KORN_SHELL ) ) X return KSH; X else if ( strEQ( sh_str, BOURNE_AGAIN_SHELL ) ) X return BASH; X else if ( strEQ( sh_str, RC_SHELL ) ) X return RC; X else if ( strEQ( sh_str, AWK_LANG ) ) X return AWK; X else if ( strEQ( sh_str, PERL_LANG ) ) X return PERL; X X else { X eprintf( "%s: Fatal Error: unknown shell '%s'\n", X Cmd_Name, sh_str ); X eprintf( "\tKnown shells are: %s, %s, %s, %s, %s, %s, %s, and %s\n", X AWK_LANG, BOURNE_AGAIN_SHELL, C_SHELL, KORN_SHELL, RC_SHELL, X PERL_LANG, BOURNE_SHELL, TC_SHELL ); X cleanup(); X exit( e_SYNTAX ); X } } X X /*************************************************************************** ** ^FUNCTION: build_tables - build the Argument and Value tables ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static int build_tables( argd_str ) /* ** ^PARAMETERS: */ X char argd_str[]; /* -- the comma-separated table of argument descriptions */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Build_tables will read the contents of the argument-descriptor-table ** string and build the corresponding Argument and Value tables to be ** used by parseargs(3). ** ** ^REQUIREMENTS: ** argd_str should be non-NULL and non-empty ** ** ^SIDE-EFECTS: ** The global variables UsrVals and UsrArgd are allocated and initialized ** ** ^RETURN-VALUE: ** The number of argument entries interpreted from the given string. ** ** ^ALGORITHM: ** - split argd_str into a vector of tokens ** - make sure the first and last tokens are syntactically correct ** - make sure that the number of tokens is a multiple of 5 (the number ** of fields in an argument descriptor) ** - num-args = num-tokens / 5 ** - allocate space for UsrVals and UsrArgd ** - i = 0 ** - for every 5 tokens ** - UsrArgd[i].ad_name = token#1 ** - UsrArgd[i].ad_flags = 0 ** - split token#2 into a subvector of '|' separated fields ** - for each '|' separated token ** - UsrArgd[i].ad_flags |= bitmask( subfield ) ** end-for ** - UsrArgd[i].ad_type = argtype( token#3 ) ** - UsrVals[i].name = token#4 ** - UsrArgd[i].ad_valp = &(UsrVals[i].value) ** - UsrArgd[i].ad_prompt = token#5 ** - increment i by one ** end-for ** - Initialize first and last entries in UsrArgd ** - return num-args ***^^**********************************************************************/ #ifdef __ANSI_C__ X static int build_tables( char argd_str[] ) #endif { X char **token_vec, **flags_vec, *type_name; X int i = 0, j = 0, idx, token_num = 0, flags_num = 0; X int argc = 0, ad_idx; X BOOL start_string_used = FALSE, is_braces = FALSE; X X /* what about NULL or empty-string */ X if ( !argd_str || !*argd_str ) return 0; X X /* escape all commas inside of single or double quotes */ X escape_char( argd_str, ',', ESCAPED_COMMA ); X X /* parse Argument Table String */ X token_num = strsplit( &token_vec, argd_str, ArgTableDelims ); X if ( token_num ) (VOID) strtrim( token_vec[ token_num - 1 ], WhiteSpace ); X X /* see if we need to build the tables at all */ X if ( token_num == 0 || isEND_ARGS(*token_vec) ) { X free( token_vec ); X return 0; /* nothing to parse */ X } X X /* make sure table is properly terminated */ X if ( !isEND_ARGS( token_vec[ --token_num ] ) ) { X restore_char( token_vec[ token_num ], ',', ESCAPED_COMMA ); X eprintf( "\ %s: Fatal Error:\n\ \tArgument descriptor table is not terminated with the string:\n\ \t\t\"%s\"\n\ \tLast entry in table is: \"%s\"\n", X Cmd_Name, s_END_ARGS, token_vec[ token_num ] ); X free( token_vec ); X cleanup(); X exit( e_ARGD ); X } X X /* check for optional start-string */ X (VOID) strtrim( *token_vec, WhiteSpace ); X if ( isSTART_ARGS(*token_vec) ) { X start_string_used = TRUE; X --token_num; X ++token_vec; X } X X /* make sure table has proper number of arguments */ X if ( (token_num % NFIELDS) != 0 ) { X eprintf( "\ %s: Fatal Error:\n\ \tArgument descriptor table has an invalid number of arguments\n\ \tThe number of comma-separated arguments MUST be a multiple of %d\n\ \t(not including terminating \"%s\")\n", X Cmd_Name, NFIELDS, s_END_ARGS ); X free( (start_string_used) ? (token_vec - 1) : token_vec ); X cleanup(); X exit( e_ARGD ); X } X X /* determine number of arg-descriptors and allocate arg-tables */ X argc = token_num / NFIELDS; X UsrArgd = (ARGDESC *) ckalloc( (argc + 2) * sizeof(ARGDESC) ); X UsrVals = (cmdarg_t *) ckalloc( argc * sizeof(cmdarg_t) ); X X /* start filling in the tables */ X i = 0; X while ( i < token_num ) { X restore_char( token_vec[i], ',', ESCAPED_COMMA ); X (VOID) strtrim( token_vec[i], WhiteSpace ); X idx = (i / NFIELDS); /* save index into UsrVals table */ X ad_idx = (idx + 1); /* save index into UsrArgd table */ X is_braces = FALSE; X X /* remove first curly-brace if its present (this has the drawback X ** of disallowing a left curly-brace from being an option character). X */ X if ( token_vec[i][0] == c_BEGIN_STRUCT ) { X token_vec[i][0] = ' '; X (VOID) strltrim( token_vec[i], WhiteSpace ); X is_braces = TRUE; X } X X /* get argument name */ X UsrArgd[ ad_idx ].ad_name = *(token_vec[i++]); X if ( !UsrArgd[ ad_idx ].ad_name ) UsrArgd[ ad_idx ].ad_name = ' '; X X /* X ** get argument flags, flags may be ORed together so I X ** need to parse the flags for each individual flag used X */ X UsrArgd[ ad_idx ].ad_flags = (argMask_t) 0; /* initialize */ X flags_num = strsplit( &flags_vec, token_vec[i++] , ArgFlagsDelims ); X for ( j = 0 ; j < flags_num ; j++ ) { X (VOID) strtrim( flags_vec[j], WhiteSpace ); X UsrArgd[ ad_idx ].ad_flags |= get_arg_flag( flags_vec[j] ); X } X free( flags_vec ); X X /* get argument type and name for Value table */ X type_name = strtrim( token_vec[i++], WhiteSpace ); X restore_char( token_vec[i], ',', ESCAPED_COMMA ); X UsrVals[ idx ].name = strtrim( token_vec[i++], WhiteSpace ); X X /* remove any leading "__" from the name */ X if ( strnEQ("__", UsrVals[ idx ].name, 2) ) { X (VOID) strltrim( (char *)UsrVals[idx].name, "_ \t\n\r\v\f" ); X } X X /* remove any leading '&', '$', and '@' from the name */ X if ( strchr("&$@", UsrVals[ idx ].name[0]) ) { X (VOID) strltrim( (char *)UsrVals[idx].name, "&$@ \t\n\r\v\f" ); X } X X /* get type and value pointer for Arg table */ X UsrArgd[ ad_idx ].ad_type = get_arg_type( type_name ); X UsrArgd[ ad_idx ].ad_valp = __ &(UsrVals[ idx ].value); X X /* if we have a vector we need to initialize it */ X if ( ARG_isVEC((UsrArgd + ad_idx)) ) { X UsrVals[ idx ].value.Vector.count = 0; X UsrVals[ idx ].value.Vector.array = (VOID *)NULL; X } X X /* get argument prompt/description */ X restore_char( token_vec[i], ',', ESCAPED_COMMA ); X UsrArgd[ ad_idx ].ad_prompt = strtrim( token_vec[i++], WhiteSpace ); X X /* if in curly-braces, remove the trailing brace */ X if ( is_braces ) { X int last = strlen( UsrArgd[ad_idx].ad_prompt ) - 1; X if ( UsrArgd[ ad_idx ].ad_prompt[ last ] == c_END_STRUCT ) { X *((char *)(UsrArgd[ ad_idx ].ad_prompt) + last) = '\0'; X (VOID) strrtrim( (char *)UsrArgd[ad_idx].ad_prompt, WhiteSpace ); X } X }/*end-if*/ X }/*while*/ X X /* free up token tables (just the arrays, not the actual elements) */ X free( flags_vec ); X free( (start_string_used) ? (token_vec - 1) : token_vec ); X X /* set up first & last argument entries */ X (UsrArgd -> ad_name) = UsrArgd[ argc+1 ].ad_name = '\0'; X (UsrArgd -> ad_flags) = UsrArgd[ argc+1 ].ad_flags = (argMask_t) 0; X (UsrArgd -> ad_type) = UsrArgd[ argc+1 ].ad_type = argNULL; X (UsrArgd -> ad_valp) = UsrArgd[ argc+1 ].ad_valp = ARBNULL; X UsrArgd[ argc+1 ].ad_prompt = CHARNULL; X X /* try to get a command-description */ X cmd_description(UsrArgd) = getenv( "DESCRIPTION" ); X if ( !cmd_description(UsrArgd) ) { X cmd_description(UsrArgd) = getenv( "CMD_DESCRIPTION" ); X } X X return argc; } X X /*************************************************************************** ** ^FUNCTION: put_char_arg - print a character ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static put_char_arg( fp, ch ) /* ** ^PARAMETERS: */ X FILE *fp; /* -- the output stream to write to. */ X int ch; /* -- the character to print */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Put_char_arg will write the given character on the specified output ** stream. If the character is metacharacter for the current shell, then ** it is "escaped" according to the given shell syntax. ** ** ^REQUIREMENTS: ** <fp> should be non-NULL and open for writing. ** <ch> should be a printable character. ** ** ^SIDE-EFECTS: ** output is written to <fp>. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** print a character argument on standard output ** and make sure we preserve the evaluation of ** any special characters such as: double-quotes, ** back-quotes, back-slash, dollar-signs, etc .... ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void put_char_arg( FILE *fp, int ch ) #endif { X /* newline needs to be escaped specially for CSH, TCSH, and PERL */ X if ( ch == '\n' ) { X if ( UsrSh == CSH || UsrSh == TCSH ) { X fprintf( fp, "\\\n" ); X return; X } X else if ( UsrSh == PERL ) { X fprintf( fp, "\\n" ); X return; X } X }/*if newline*/ X X if ( strchr( Shell[ UsrSh ].metachars, ch ) ) { X fprintf( fp, Shell[ UsrSh ].escape, ch ); X } X else { X fputc( ch, fp ); X } } X X /*************************************************************************** ** ^FUNCTION: put_str_arg - same as put_char_arg but for a string! ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID put_str_arg( fp, str ) /* ** ^PARAMETERS: */ X FILE *fp; /* -- the output stream to write to */ X char str[]; /* -- the string to print */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Put_str_arg will print the given string to the given output stream ** and will escape any shell meta-characters for the current shell. ** ** ^REQUIREMENTS: ** <fp> should be non-NULL and open for writing. ** <str> should be non-NULL and non-empty. ** ** ^SIDE-EFECTS: ** Output is written to <fp> ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - foreach character in str ** - put_char_arg(fp, character) ** end-for ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void put_str_arg( FILE *fp, const char str[] ) #endif { X if ( !str ) return; X X for ( ; *str ; str++ ) X put_char_arg( fp, *str ); } X X /*************************************************************************** ** ^FUNCTION: put_arg - convert & print the given value into the given buffer ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID put_arg( fp, ad, val, idx ) /* ** ^PARAMETERS: */ X FILE *fp; /* -- the output stream to write to */ X ARGDESC *ad; /* -- the argument-descriptor of the argument to print. */ X cmdarg_t *val; /* -- the value of the argument to print */ X short idx; /* -- the index in the argument-vector of the item to be printed ** (only used when ad corresponds to an ARGVEC argument). */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Put_arg will print the given variable/array setting on the given ** output stream using the syntax of the user-sepcified shell. ** ** ^REQUIREMENTS: ** <val> should be the value corresponing to the argument-descriptor <ad> ** ** ^SIDE-EFECTS: ** Output is written to <fp>. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - if we have a vector, make sure we were given a valid index. ** - if we have a vector, then value=val.vec[idx], ** else value = val.value ** - print the beginning of the variable setting ** - case (argument-type) of ** INTEGRAL-TYPE: print the integer value ** DECIMAL-TYPE: print the floating point value ** CHARACTER: print the character value and escape it if necessary ** STRING: print the string value and escape it if necessary ** BOOLEAN: print the string StrTrue if value is TRUE ** print the string StrFalse if value is FALSE ** - print the end of the variable setting ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void put_arg( X FILE *fp, const ARGDESC *ad, const cmdarg_t *val, short idx X ) #endif { X if ( ARG_isVEC(ad) ) { X if ( idx < 0 || idx >= val->value.Vector.count ) { X return; /* bad index given */ X } X X if ( arg_type(ad) == argStr ) { X if ( UsrSh == PERL ) fputc( '\'', fp ); X put_str_arg( fp, val->value.Str_vec.array[idx] ); X if ( UsrSh == PERL ) fputc( '\'', fp ); X } X else if ( arg_type(ad) == argChar ) { X if ( UsrSh == PERL ) fputc( '\'', fp ); X put_char_arg( fp, val->value.Char_vec.array[idx] ); X if ( UsrSh == PERL ) fputc( '\'', fp ); X } X else if ( arg_type(ad) == argDouble ) { X fprintf( fp, "%lf", val->value.Double_vec.array[idx] ); X } X else if ( arg_type(ad) == argFloat ) { X fprintf( fp, "%f", val->value.Float_vec.array[idx] ); X } X else if ( arg_type(ad) == argLong ) { X fprintf( fp, "%ld", val->value.Long_vec.array[idx] ); X } X else if ( arg_type(ad) == argInt ) { X fprintf( fp, "%d", val->value.Int_vec.array[idx] ); X } X else if ( arg_type(ad) == argShort ) { X fprintf( fp, "%ld", val->value.Short_vec.array[idx] ); X } X X /* Boolean vectors are not supported */ X }/*if vector*/ X else { X if ( arg_type(ad) == argStr ) { X put_str_arg( fp, val->value.Str_val ); X } X else if ( arg_type(ad) == argChar ) { X put_char_arg( fp, val->value.Char_val ); X } X else if ( arg_type(ad) == argDouble ) { X fprintf( fp, "%lf", val->value.Double_val ); X } X else if ( arg_type(ad) == argFloat ) { X fprintf( fp, "%f", val->value.Float_val ); X } X else if ( arg_type(ad) == argLong ) { X fprintf( fp, "%ld", val->value.Long_val ); X } X else if ( arg_type(ad) == argInt ) { X fprintf( fp, "%d", val->value.Int_val ); X } X else if ( arg_type(ad) == argShort ) { X fprintf( fp, "%ld", val->value.Short_val ); X } X else if ( ARG_isBOOLEAN(ad) ) { X fprintf( fp, "%s", (val->value.Bool_val) ? StrTrue : StrFalse ); X } X }/*else !vector*/ } X X /*************************************************************************** ** ^FUNCTION: print_argvector - print shell variable settings for an ARGVEC ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID print_argvector( ad, val ) /* ** ^PARAMETERS: */ X ARGDESC *ad; /* -- the argument-descriptor of the vector to print */ X cmdarg_t *val; /* -- the value of the vector to print */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Parseargs treats ARGLIST and ARGVEC arguments in a special way. The ** method used for setting up an argument list depends largely upon the ** syntax of shell that was specified on the command line via the -s option ** (although an ARGLIST argument is treated the same as an ARGVEC argument). ** ** ^Resetting_the_Positional_Parameters_to_an_Argument_List: ** For the Bourne, Bourne-Again, and Korn shells, if the variable name ** corresponding to the ARGLIST argument is "--", then the positional ** parameters of the calling program will be re-assigned to the contents ** of the argument list ($1 will be the first item, $2 the second item, ** and so on). In this particular case, the calling program may wish to ** use the -u option to reset the positional parameters to NULL before ** making any shell-variable assignments (this way, the positional ** parameters will be unset if the associated list of command line ** arguments is not encountered). ** ** Similarly for the C and TC shells, if the variable name corresponding ** to the ARGLIST argument is "argv", then the positional parameters ** of the calling program will be re-assigned to the contents of the ** argument list. ** ** For the Plan 9 shell (rc), if the variable name corresponding to the ** ARGLIST argument is "*", then the positional parameters of the calling ** program will be re-assigned to the contents of the argument list. ** ** For awk and perl, if the variable name corresponding to the ARGLIST ** argument is "ARGV", then the positional parameters of the calling ** program will be re-assigned to the contents of the argument list. ** ** ^Bourne_Shell_Argument_Lists: ** For the Bourne shell, if the associated variable name is NOT "--" ** and the -A option was NOT specified, then that variable is treated as ** a regular shell variable and is assigned using the following syntax: ** ** name='arg1 arg2 ...' ** ** After invoking parseargs, if you wish to go through all the words in ** the variable name and one of the words in name contains an IFS charac- ** ter (such as a space or a tab), then that particular word will be ** treated by the Bourne shell as two distinct words. ** ** Also for the Bourne shell, If the associated variable name is NOT ** "--" and the -A option WAS specified, then that variable is treated ** as the root name of an array that is set using the following syntax: ** ** name1='arg1' ** name2='arg2' ** ... ** ** and the variable "name_count" will be set to contain the number of ** items in the array. The user may then step through all the items in ** the array using the following syntax: ** ** i=1 ** while [ $i -le $name_count ] ; do ** eval echo "item #$i is: " \$name$i ** i=`expr $i + 1` ** done ** ** ^Korn_Shell_Argument_Lists: ** For the Korn shell, if the associated variable name is NOT "--", ** then that variable is treated as an array and is assigned using the -A ** option of the set command. The first item will be in ${name[0]}, the ** second item will be in ${name[1]}, etc ..., and all items may be given ** by ${name[*]} or ${name[@]}. If the associated variable name is NOT ** "--" and the -A option WAS specified, then that variable is assigned ** using the +A option of the set command (which preserves any array ** elements that were not overwritten by the set command). ** ** It should be noted that there is a bug in versions of the Korn shell ** earlier than 11/16/88a, in which the following: ** ** set -A name 'arg1' 'arg2' ... ** ** causes the positional parameters to be overwritten as an unintentional ** side-effect. If your version of the Korn shell is earlier than this ** and you wish to keep the contents of your positional parameters after ** invoking parseargs than you must save them yourself before you call ** parseargs. This may be accomplished by the following: ** ** set -A save_parms "$@" ** ** ^C_and_TC_Shell_Argument_Lists: ** For the C and TC shells, ARGLIST variables are treated as word-lists ** and are assigned using the following syntax: ** ** set name = ( 'arg1' 'arg2' ... ) ** ** The first item will be in $name[1], the second item will be in ** $name[2], etc ..., and all items may be given by $name. Notice that ** Korn shell arrays start at index zero whereas C and TC shell word- ** lists start at index one. ** ** ^Bourne-Again_Shell_Argument_Lists: ** At present, the Free Software Foundation's Bourne-Again shell is ** treated exactly the same as the Bourne Shell. This will change when ** bash supports arrays. ** ** ^Plan_9_Shell_Argument_Lists: ** For the Plan 9 shell, if the associated variable name is not "*" then ** it is considered to be word-list and set using the following syntax: ** ** name=( 'arg1' 'arg2' ... ) ** ** ^Awk_Argument_Lists: ** For awk, if the -A option is not given, then the output for thes ** variable-list will be a line with the variable name, followed by a ** line with each of the values (each value will be separated with the ** field separator specified using the -S option - which defaults to a ** space character): ** ** name ** arg1 arg2 ... ** ** If the -A option is given, then the associated variable is considered ** the root name of an array. The ouput for the array will consist of two ** lines for each item in the list (as in the following expample): ** ** name1 ** arg1 ** ** name2 ** arg2 ** ** and the variable "name_count" will have an output line showing the ** number of items in the array. ** ** ^Perl_Argument_Lists: ** For perl, each argument list is considered an array and is set using ** the following syntax: ** ** @name=( arg1 , arg2 , ... ) ** ** ^A_Final_Note_on_Argument_Lists: ** The word-lists used by the C shell, the arrays used by the Korn shell, ** The Plan 9 shell, awk, & perl, and the positional parameters used by ** all shells (if overwritten by parseargs) will preserve any IFS ** characters in their contents. That us to say that if an item in one ** of the aforementioned multi-word lists contains any IFS characters, ** it will not be split up into multiple items but will remain a single ** item which contains IFS characters. ** ** ^REQUIREMENTS: ** <val> should correspond to the vlue of the argument indicated by <ad> ** ** ^SIDE-EFECTS: ** prints the array assignment statement on standard output ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - print the beginning of the array assigment statement ** - print each item in the array (escaping characters where needed) ** - print the end of the array assignment statement ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void print_argvector( const ARGDESC *ad, const cmdarg_t *val ) #endif { X BOOL is_array = TRUE; X int i; X char *varname; X X switch( UsrSh ) { X case KSH : X if ( strEQ( val->name, Shell[ UsrSh ].varname ) ) { X fputs( "set -- ", stdout ); X } X else { X printf( "set %cA %s ", ((ModArr) ? '+' : '-'), val->name ); X } X break; X X case TCSH : case CSH : case RC: case PERL : X if ( UsrSh == PERL ) X printf( "@%s = ", val->name ); X else if ( UsrSh == RC ) X printf( "%s=", val->name ); X else /* UsrSh == CSH/TCSH */ X printf( "set %s=", val->name ); X fputc( '(', stdout ); X break; X X case BASH: case SH : case AWK: X if ( UsrSh == AWK ) is_array = FALSE; X if ( strEQ( val->name, Shell[ UsrSh ].varname ) ) { X fputs( ((UsrSh == AWK) ? "ARGV\n" : "set -- "), stdout ); X } X else { X if ( ModArr ) { /* use fake array syntax */ X i = strlen( val->name ); X varname = (char *)ckalloc( (i + 4) * sizeof(char) ); X for ( i = 0 ; i < val->value.Vector.count ; i++ ) { X sprintf( varname, "%s%d", val->name, i+1 ); X printf( Shell[ UsrSh ].setcmd, varname ); X printf( Shell[ UsrSh ].prefix ); X put_arg( stdout, ad, val, i ); X printf( "%s", Shell[ UsrSh ].suffix ); X } X sprintf( varname, "%s_count", val->name ); X printf( Shell[ UsrSh ].setcmd, varname ); X printf( Shell[ UsrSh ].prefix ); X printf( "%d", val->value.Vector.count ); X printf( "%s", Shell[ UsrSh ].suffix ); X free( varname ); X if ( val->value.Vector.array ) { X free( val->value.Vector.array ); X val->value.Vector.array = NULL; X } X return; X }/*if ModArr*/ X else { X is_array = FALSE; X printf( Shell[ UsrSh ].setcmd, val->name ); X printf( Shell[ UsrSh ].prefix ); X } X }/*else !positional-parms*/ X break; X X }/*switch*/ X X for ( i = 0 ; i < val->value.Vector.count ; i++ ) { X if ( is_array ) printf( Shell[ UsrSh ].prefix ); X put_arg( stdout, ad, val, i ); X if ( is_array ) printf( Shell[ UsrSh ].prefix ); X if ( i != (val->value.Vector.count - 1) ) { X fputs( ((UsrSh == PERL) ? ", " : FieldSep), stdout ); X } X }/* end-for */ X X if ( val->value.Vector.array ) { X free( val->value.Vector.array ); X val->value.Vector.array = NULL; X } X X if ( UsrSh == CSH || UsrSh == TCSH || UsrSh == RC || UsrSh == PERL ) { X fputc( ')', stdout ); X } X X fputs( ((! is_array) ? Shell[ UsrSh ].suffix : ";\n"), stdout ); } X X /*************************************************************************** ** ^FUNCTION: print_args - print the shell variable settings for the usr args ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID print_args( vals, argd ) /* ** ^PARAMETERS: */ X cmdarg_t *vals; /* -- the table of argument values. */ X ARGDESC *argd; /* -- the table of argument-descriptors. */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Print_args prints the actual shell variable assignment statement(s) for ** each argument found on the command line. If a command-line argument was ** specified withch may take an optional value, then regargdless of whether ** or not the optional value was supplied, the variable <name>_flag is set ** to the value indicated by StrTrue. ** ** ^REQUIREMENTS: ** The argument values have already been set due to the fact that parseargs ** should already have been invoked to parse the command-line ** ** ^SIDE-EFECTS: ** Variable assignment statements are printed on standard output. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - for each argument in the argument table ** - if this argument was supplied on the command-line ** - if the argument takes an optional value ** then set argname_flag = TRUE ** - if the argument is a vector ** - call print_argvector to print the vector elements ** - else ** - print the beginning of the variable assignment statement for ** the shell indicated by UsrSh ** - print the argument value using put_arg ** - print the end of the variable assignment statement for the shell ** indicated by UsrSh ** end-if vector ** end-if supplied ** end-for ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void print_args( const cmdarg_t *vals, const ARGDESC *argd ) #endif { X register CONST ARGDESC *ad; X register int i; X argName_t buf; X X /* print values for all options given */ X for ( ad = ARG_FIRST(argd), i = 0 ; !ARG_isEND(ad) ; ARG_ADVANCE(ad), i++ ) { X if ( ARG_isGIVEN(ad) ) { X /****************************************************************** X ** ^SECTION: ARGVALOPT X ** Options that may take an optional argument need special X ** consideration. The shell programmer needs to know whether X ** or not the option was given, and (if given) if it was X ** accompanied by an argument. In order to accommodate this X ** need, parseargs will set an additional shell variable for X ** each argument that is given the ARGVALOPT flag if it is X ** supplied on the command line regardless of whether or not X ** it was accompanied by its optional argument. If the user X ** has defined an option which may optionally take an argument X ** and the option appears on the command line with or without X ** its associated argument, then the shell variable <name>_flag X ** will be assigned the value "TRUE" (or the value supplied with X ** the -T option to parseargs) where <name> is the name of the X ** shell variable associated with the option in the argument X ** description string. X ***^^*************************************************************/ X if ( ARG_isVALOPTIONAL(ad) ) { X sprintf(buf, "%s_flag", vals[i].name); X printf( Shell[ UsrSh ].setcmd, buf); X printf( Shell[ UsrSh ].prefix ); X printf( "%s%s", StrTrue, Shell[ UsrSh ].suffix ); X X if ( !ARG_isVALGIVEN(ad) ) continue; X }/*if OPTARG*/ X X /* vectors are special */ X if ( ARG_isVEC(ad) ) { X print_argvector( ad, (vals + i) ); X continue; X } X X /* print shell-specific variable prefix and name */ X printf( Shell[ UsrSh ].setcmd, vals[i].name ); X printf( Shell[ UsrSh ].prefix ); X X /* print shell-variable value */ X put_arg( stdout, ad, (vals + i), 0 ); X X /* print the shell-specific suffix */ X printf( "%s", Shell[ UsrSh ].suffix ); X }/*if ARGGIVEN*/ X }/* end-for */ } X X /*************************************************************************** ** ^FUNCTION: unset_positional_parameters - unset shell parameters ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static void unset_positional_parameters() #endif /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Unset_positional_parameters will print (on standard output) the ** shell commands to unset the positional parameters of the invoking ** shell_script. ** ** ^REQUIREMENTS: ** The currenty shell-type has already been determined. ** ** ^SIDE-EFECTS: ** Prints on stdout. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** - Use the syntax of the current shell to unset the positional parameters ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void unset_positional_parameters( void ) #endif { X switch( UsrSh ) { X case TCSH: case CSH: X printf( "set argv=();\n" ); X break; X X case PERL: X printf( "@ARGV = ();\n" ); X break; X X case KSH: X printf( "set --;\n" ); X break; X X case BASH: case SH: X printf( "shift $#;\n" ); X break; X X case RC: X printf( "*=();\n" ); X break; X X case AWK: X printf( "%s%s", Shell[ UsrSh ].varname, Shell[ UsrSh ].suffix ); X break; X X default: X break; X }/*switch*/ } X X /*************************************************************************** ** ^FUNCTION: ck_cmd_errs - check for command syntax errors ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static int ck_cmd_errs() #endif /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Ck_cmd_errs will check for the improper specification of arguments ** from the command-line. ** ** ^REQUIREMENTS: ** The command-line should already have been parsed by parseargs(3) ** ** ^SIDE-EFECTS: ** - Exits the program if an error is encountered. ** - Assigns any needed defaults for StrTrue and StrFalse. ** - Gets the argd-string from an environment variable if needed ** (or sets UseStdin if it is to be read from standard input) ** - Determines the shell specified by the user (default=Bourne) ** ** ^RETURN-VALUE: ** e_SUCCESS if everything checks out all right ** Exits with one of the following exit-codes upon failure: ** ** e_SYNTAX : command-line sytntax error ** ** e_NOENV : AN environment variable was "purported" to contain the ** description string that describes all the arguments but ** upon examination, the variable was unset or empty. ** ** ^ALGORITHM: ** - make sure only one of '-a', '-e', and '-f' was given ** - turn OFF UseStdin if any of the above were given ** - make sure only one of '-l' and '-o' was given ** - if '-e' was specified, read the environment-variable and ** make sure it is non-NULL and non-empty ** - assign default values for StrTrue and StrFalse if they were not ** supplied on the command-line ** - determine the type of the user's shell ** - return ***^^**********************************************************************/ #ifdef __ANSI_C__ X static int ck_cmd_errs( void ) #endif { X /* make sure certain arg-combos were NOT used */ X if ( (ArgdString && (ArgdFname || ArgdEnv)) || (ArgdFname && ArgdEnv) ) { X eprintf( "%s: only one of `-a', `-e', or `-f' may be given.\n\n", X Cmd_Name ); X usage( Args ); X exit( e_SYNTAX ); X } X X /* make sure at least ONE of KeyWords and Options are enabled */ X if ( OptsOnly && KwdsOnly ) { X eprintf( "%s: only one of `-o' or `l' may be given.\n\n", X Cmd_Name ); X exit( e_SYNTAX ); X } X X /* turn OFF UseStdin if `-a', `-e', or `-f' was given */ X if (ArgdString || ArgdEnv || ArgdFname) X UseStdin = FALSE; X X /* get the argd-string from an environment variable if so specified */ X if ( ArgdEnv ) { X ArgdString = getenv( ArgdEnv ); X if ( !ArgdString || !*ArgdString ) { X eprintf( "%s: variable \"%s\" is NULL or does not exist\n", X Cmd_Name, ArgdEnv); X exit( e_NOENV ); X } X } X X /* set up default boolean value strings if needed */ X if ( !StrTrue ) { X StrTrue = (char *)Default_StrTrue; X } X if ( !StrFalse ) { X StrFalse = (char *)Default_StrFalse; X } X X /* see if we need to "default" the shell name */ X if ( !PrUsage && !PrManual ) { X if ( !ShellName ) { X UsrSh = SH; /* default to Bourne Shell */ X } X else { X UsrSh = get_shell_type( basename( ShellName ) ); X } X } X X /* everything is a-ok */ X return e_SUCCESS; } /* ck_cmd_errs */ X X /*************************************************************************** ** ^FUNCTION: main ** ** ^SYNOPSIS: ** main( argc, argv ) ** ** ^PARAMETERS: ** int argc; ** -- the number of argumenst on the command-line ** ** char *argv[]; ** -- the NULL terminated vector of arguments from the command-line ** (the first of which is the name of the paraseargs(1) command). ** ** ^DESCRIPTION: ** This is the main program for parseargs(1). It parses the user's command- ** line and outputs the approriate variable assignment statements (or prints ** a usage message or manual template). ** ** ^REQUIREMENTS: ** All the static local variables that are used to define the argument ** table and the values it points to (for parseargs(1) not for the user's ** command) should be properly initialized). ** ** ^SIDE-EFFECTS: ** Shell variable assignment statements, A usage message, or a manual ** page template is printed on standard output. Any diagnostic messages ** are printed on standard error. ** ** ^RETURN-VALUE: ** Execution is terminated and the corresponding exit-code is returned ** to the calling progarm (see the RETURN-CODES section). ** ** ^ALGORITHM: ** - save the name of this program ** - parse the command-line ** - read the argument descriptor string into memory (if needed) ** - build the argument and value tables ** - set ProgName to the name of the user's program ** - if need to print usage, then do so and exit with status e_USAGE ** - if need to print manual template, then do so, exit-status=e_USAGE ** - modify parsing-behavior as specified on the command-line ** - parse the user's command-line arguments ** - unset the positional parameters if required ** - print thye resulting shell variable assignments ** - deallocate any remaining storage ** - exit, status=e_SUCCESS ***^^**********************************************************************/ MAIN( argc, argv ) { X int rc; X argMask_t flags = pa_ARGV0; X X Cmd_Name = *argv = basename( *argv ); X X /* parse command-line */ X parseargs( argv, Args ); X X /* see if there is any reason to exit now */ X (VOID) ck_cmd_errs(); X X /* set desired option-syntax */ X if ( KwdsOnly ) BSET(flags, pa_KWDSONLY); X if ( OptsOnly ) BSET(flags, pa_OPTSONLY); X X /* if needed - allocate and read in the argument descriptor string */ X if ( !ArgdString ) { X ArgdString = get_argtable_string(); X if ( ArgdString ) { X strrtrim( ArgdString, WhiteSpace ); X if ( !*ArgdString ) ArgdString = CHARNULL; X } X } X X /* fill in the argument tables from the environment variable */ X if ( ArgdString ) UsrArgc = build_tables( ArgdString ); X X /* if no arguments or options taken, use NULL */ X if ( !UsrArgc ) UsrArgd = ARGDESCNULL; X X ProgName = UsrName; /* set up program name */ X X if ( PrUsage ) { /* just print usage and exit */ X usage( UsrArgd ); X } X else if ( PrManual ) { /* print man pages and exit */ X manpage( UsrArgd ); X } X else { /* parse callers command-line & print variable settings */ X if ( Prompt ) BSET(flags, pa_PROMPT); X if ( Ignore ) BSET(flags, pa_IGNORE); X if ( flags ) (VOID) parsecntl( UsrArgd, pc_PARSEFLAGS, pc_WRITE, flags ); X X if ( (rc = parseargs( UsrArgv.array, UsrArgd )) != 0 ) { X if ( rc > 0 ) { X rc = e_SYNTAX; X } X else { /* (rc < 0) means a system error */ X cleanup(); X if ( errno ) perror( UsrName ); X exit( e_SYSTEM ); X } X }/*if*/ X X if ( Unset ) unset_positional_parameters(); X X print_args( UsrVals, UsrArgd ); X } X X cleanup(); X exit( rc ); } /* main */ SHAR_EOF echo 'File parseargs/parseargs.c is complete' && chmod 0664 parseargs/parseargs.c || echo 'restore of parseargs/parseargs.c failed' Wc_c="`wc -c < 'parseargs/parseargs.c'`" test 70264 -eq "$Wc_c" || echo 'parseargs/parseargs.c: original size 70264, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/parseargs.h ============== if test -f 'parseargs/parseargs.h' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/parseargs.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/parseargs.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/parseargs.h' && /************************************************************************* ** ^FILE: parseargs.h -- definitions for argument parsing library ** ** ^DESCRIPTION: ** This file contains all the necessary macros, type, and function ** declarations necessary to use the parseargs library. For most purposes, ** no special symbols need be #defined before including this file; however, ** for implementors (and more sophisticated uses), the following symbols ** may be #defined before including this file in order to include/exclude ** various portions of the file: ** ** PARSEARGS_PRIVATE ** Include private definitions that are needed to implement/enhance ** various functions in the parseargs library. ** ** PARSEARGS_NEXTERNS ** Do NOT include the external function declarations for the members ** of the parseargs library. ** ** PARSEARGS_NARGTYPES ** Do NOT include the external function declarations any of the ** pre-defined argument-type (argXxxx) functions. ** ** ^HISTORY: ** 12/03/90 Brad Appleton <brad@ssd.csd.harris.com> ** - Added all the #ifdef stuff ** - Added public and private macros for getting and setting ** the attributes of an argdesc-array and an argdesc ** ** --/--/-- Peter da Silva <peter@ferranti.com> ** ** --/--/-- Eric P. Allman <eric@Berkeley.EDU> Created ***^^**********************************************************************/ X /* $Header: parseargs.h,v 2.0 89/12/24 00:56:29 eric Exp $ */ X #ifndef PARSEARGS_H #define PARSEARGS_H X #include <useful.h> X X /* type definition for argument prompt strings */ #define MAX_ARGNAME 80 typedef char argName_t[ MAX_ARGNAME ]; X X /* type definition for bitmasks */ typedef unsigned short argMask_t; X /********************************************************************** ** ^STRUCT: ARGDESC - argument-descriptor ** ** ^DESCRIPTION: ** The basic type used by the parseargs library is the argument descriptor ** (or "argdesc" for short). An ARGDESC structure is used to describe a ** command-line argument. Each command line argument contains various ** fields which need to be set and/or queried by the programmer. Each ** field is described in further detail below: */ typedef struct _argdesc { X char ad_name; /* -- This is a single character which corresponds to the option-letter ** (case-sensitive) from the command-line that matches the argument ** described by this structure. Positional-arguments are denoted by ** putting a a space character in this field. */ X argMask_t ad_flags; /* -- This field contains the various bitflags that describe the semantics ** of this argument. See the ARGFLAGS section for more information on ** the possible combinations of bitmasks for this field. */ X BOOL (*ad_type) ARGS((struct _argdesc *, char *, BOOL)); /* -- This field is a pointer to a type conversion function (such as the ** ones provided in argtype(3). The type conversion function is respon- ** sible for verifying the validity of the argument, allocating any ** necessary storage for its internal representation, and converting ** the command-line argument into its required internal form. The type ** conversion function used may be one of the pre-defined argtype(3) ** functions. The function is given three parameters: The first is ** a pointer to the ARGDESC struct in question, the second is the ** string-value (if any) supplied on the command-line, and the third ** is a boolean value that is TRUE only if the second parameter points ** to temporary storage (and hence may need to be copied). ** In the case of parseargs(1) this field must correspond to the name ** of one of the argument type functions described in argtype(3). */ X ARBPTR ad_valp; /* -- This field is a generic pointer to the storage used to represent ** the internal value of the command-line argument. It may be a ** pointer to a number, a boolean value, a string, a list, or anything ** else for which there exists a corresponding arg-type function to ** use in the ad_type field. In the case of of parseargs(1) this field ** must be the name of the corresponding shell variable which eventually ** hold the value of the argument given on the command-line. */ X CONST char *ad_prompt; /* -- This field contains the long-name of the argument and an optional ** description (the description must be separated from the long-name by ** at least one whitespace characters and may optionally be enclosed in ** a set of balanced delimiters (such as parentheses, curly-braces, ** square-brackets, or angle-brackets. If the long-name contains any ** uppercase characters, then the substring of long-name consisting of ** all uppercase characters is used as the argument name and the entire ** long-name is used as the name of the argument-value (if a value my be ** supplied). The long-name may be matched by supplying a unique prefix ** of either the argument name or the argument-value name. */ } ARGDESC; /**^^**********************************************************************/ X X X /* define a NULL pointer to an arg-descriptor */ #define ARGDESCNULL (ARGDESC *)NULL X X /* define a pointer to an argtype function */ typedef BOOL (*argTypePtr_t) ARGS((ARGDESC *, char *, BOOL)); X X /********************************************************************** ** ^SECTION: RETURN-CODES ** The XparseXXXX functions in the parseargs library may return any of ** the following return codes (which are #define'd below): */ #define pe_SYSTEM -1 /* -- a system error occurred. The global variable errno may indicate ** the problem (then again, it may not). */ #define pe_SUCCESS 0 /* -- success, no errors encountered. */ #define pe_SYNTAX 1 /* -- a command-line syntax error was encountered */ #define pe_DEFARGS 2 /* -- 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). */ #define pe_NOMATCH 3 /* -- unable to match the named argument. This occurs ** when the argument keyword name passed to parsecntl() (using the ** pc_ARGFLAGS functions code) was found in the given argdesc-array ** or in its default-list. */ #define pe_BADMODE 4 /* -- bad mode for given command in parsecntl(). This occurs when ** pc_WRITE or pc_RDWR mode is passed to parsecntl() in conjunction ** with the pc_ARGFLAGS functions code. Parsecntl will not modify ** existing arguments. */ #define pe_BADCNTL 5 /* -- bad command for parsecntl. This occurs if an unknown function-code ** was passed to parsecntl(). */ #define pe_BADOPEN 6 /* -- error opening file */ #define pe_BADREAD 7 /* -- error reading file */ /**^^**********************************************************************/ X X /********************************************************************** ** ^SECTION: ARGUMENT-FLAGS ** These are the possible bitmasks that may be turned ON or OFF in ** the ad_flags field of an ARGDESC structure. */ #define ARGOPT 0x0000 /* -- This flag is actually a dummy flag (i.e. it is the default). This flag ** specifies that the command-line argument is optional (need not appear ** on the command-line). It is only needed if no other flags are used to ** define the given argument. If other flags are given and ARGREQ is NOT ** one of them, then ARGOPT is always assumed. */ #define ARGREQ 0x0001 /* -- The associated command argument is required on the command-line. */ #define ARGPOS 0x0002 /* -- The associated command argument is positonal. The difference between ** using this flag to indicate a positional argument and between using ** a blank in the ad_name field to indicate a positional arguments is ** the following: If this flag is set but the ad_name of the argument ** is non-blank, then this argument may be matched either positionally ** or by keyword. If the ad_name field is blank, then this argument may ** only be matched positionally. */ #define ARGNOVAL 0x0004 /* -- The associated command argument takes no value (as in "-x value"); ** Its mere presence (or lack thereof) on the command-line is sufficient ** to determine the necessary action(s) to take (as in "-x"). ** ** NOTE: all argBool, and arg[STU]Bool arguments are always forced to be ** ARGNOVAL (as are argUsage arguments). */ #define ARGVALOPT 0x0008 /* -- This flag is used to indicate that the command-line argument takes a ** value (as in "-s string" or "/str=string") but that the value to this ** command-line argument is NOT required (hence simply "-s" or "/str" is ** also permissable). */ #define ARGVALREQ 0x0000 /* -- Another "dummy" flag. Unless ARGNOVAL or ARGVALOPT is specified, ** ARGVALREQ is always assumed. This flag indicates that the value to a ** command-line argument is required (hence "-s string" is okay but just ** "-s" is not). */ #define ARGHIDDEN 0x0010 /* -- Don't display this argument in usage messages but still attempt to ** match it against strings given on the command-line. */ #define ARGLIST 0x0020 /* -- A variable number of values are used for this argument (and hence may ** use more than one or two argv elements from the command-line as in ** "-l val1 val2 ..."). The list of values must be stored in an arglist ** structure (NOT a vector structure), an the corresponding argument-type ** function should be one of the listXxxx functions. */ #define ARGVEC 0x0040 /* -- A variable number of values are used for this argument (and hence may ** use more than one or two argv elements from the command-line as in ** in "-v val1 val2 ..."). The list of values must be stored in a vector ** structure (NOT an arglist structure). */ X /* The following bitmasks may also be present, but, unlike the above masks ** which must be specified by the programmer at initialization time, the ** following masks must only be read (never set) by the programmer: */ #define ARGGIVEN 0x0080 /* -- The argument WAS given on the command-line. */ #define ARGVALGIVEN 0x0100 /* -- The value for this argument was given on the command-line. */ #define ARGVALSEP 0x0200 /* -- The value to this argument was supplied in a separate argv element ** from the argument itself (as in "-x value" as opposed to "-xvalue"). */ #define ARGKEYWORD 0x0400 /* -- This argument was matched as a keyword (long-form) on the command-line ** and not as a single character. */ #define ARGDESCRIBED 0x0800 /* -- This argument was given a description by the programmer at ** initialization. */ #define ARGCOPYF 0x1000 /* -- This flag is only used for lists and vectors (multivalued arguments) ** and is used on a per-item basis. If it is set, it means that the ** corresponding value in the vector/list required space to be allocated ** (such as the duplication of a temporary string). */ /**^^**********************************************************************/ X X /********************************************************************** ** ^SECTION: ARGDESC-MACROS ** The following macros are used to extract and query the attributes ** of a pointer to a preprocessed arg-descriptor: */ #define arg_cname(ad) ((ad) -> ad_name) /* -- return the single-character name of an argument. */ #define arg_flags(ad) ((ad) -> ad_flags) /* -- return the argument flags of an argument. The flags themselves ** may be manipulated using the BTEST, BSET, and BCLEAR macros ** #defined in <useful.h>. */ #define arg_type(ad) ((ad) -> ad_type) /* -- return the pointer to the value-translation-routine of an argument. */ #define arg_valp(ad) ((ad) -> ad_valp) /* -- return the pointer to the value of this argument. */ #define arg_sname(ad) ((ad) -> ad_prompt) /* -- return the string name of an argument. */ #define arg_sdesc(ad) ( arg_sname(ad) + strlen(arg_sname(ad)) + 1 ) /* -- return the description of an argument. If a description was supplied, ** the ARGDESCRIBED flag will be set and the description will immediately ** follow the NUL byte of the string name. */ #define ARG_isDESCRIBED(ad) BTEST( arg_flags(ad), ARGDESCRIBED ) /* -- Evaluates to TRUE only if an argument description was provided. */ #define arg_description(ad) ( ARG_isDESCRIBED(ad) ? arg_sdesc(ad) : "" ) /* -- Return the description string (or an empty string if no description ** was given) for this argument. */ #define ARG_isPOSITIONAL(ad) BTEST( arg_flags(ad), ARGPOS ) /* -- Evaluates to TRUE if this argument may be positionally matched. */ #define ARG_isPOSONLY(ad) ( arg_cname(ad) == ' ' ) /* -- Evaluates to TRUE if this argument may only be positionally matched. */ #define ARG_isLIST(ad) ( BTEST(arg_flags(ad), ARGLIST) ) /* -- Evaluates to TRUE if this argument is an arglist. */ #define ARG_isVEC(ad) ( BTEST(arg_flags(ad), ARGVEC) ) /* -- Evaluates to TRUE if this argument is a vector. */ #define ARG_isMULTIVAL(ad) ( BTEST(arg_flags(ad), ARGVEC | ARGLIST) ) /* -- Evaluates to TRUE if this argument is an arglist or a vector. */ #define ARG_isVALTAKEN(ad) ( ! BTEST(arg_flags(ad), ARGNOVAL) ) /* -- Evaluates to TRUE if this argument does NOT take a value. */ #define ARG_isGIVEN(ad) ( BTEST(arg_flags(ad), ARGGIVEN) ) /* -- Evaluates to TRUE if this argument was given on the command-line. */ #define ARG_isVALGIVEN(ad) ( BTEST(arg_flags(ad), ARGVALGIVEN) ) /* -- Evaluates to TRUE if the argument value was given on the command-line. */ #define ARG_isREQUIRED(ad) ( BTEST(arg_flags(ad), ARGREQ) ) /* -- Evaluates to TRUE if this argument is required. */ #define ARG_isVALOPTIONAL(ad) ( BTEST(arg_flags(ad), ARGVALOPT) ) /* -- Evaluates to TRUE if the argument value is optional. */ #define ARG_isVALSEPARATE(ad) ( BTEST(arg_flags(ad), ARGVALSEP) ) /* -- Evaluates to TRUE if the argument value is optional. */ #define ARG_isHIDDEN(ad) ( BTEST(arg_flags(ad), ARGHIDDEN) ) /* -- Evaluates to TRUE if this argument is omitted from usage messages. */ /**^^**********************************************************************/ X X X /* macro to define a NULL argument-type function */ #define argNULL (argTypePtr_t)NULL X X /* macro for an empty argument descriptor */ #define ARG_EMPTY { '\0', 0x0000, argNULL, ARBNULL, CHARNULL } X X /* X ** macro to denote start & end of an ARGDESC array declared without X ** the CMD_XXXXXXX macros which follow. X */ #define STARTOFARGS ARG_EMPTY #define ENDOFARGS ARG_EMPTY, ARG_EMPTY X /*************************************************************************** ** ^SECTION: CMD-MACROS ** Parseargs.h defines a set of macros to allow a more "self documenting" ** approach to declaring argument-descriptor arrays. The "old-style" is ** still accepted (but if used it is recommended that the STARTOFARGS ** macro is used in conjunction with ENDOFARGS). An example use of these ** macros (which, with one exception, all begin with "CMD") follows: ** ** #include <parseargs.h> ** ** static BOOL bflag = FALSE; ** static char *arg1 = CHARNULL; ** static char *arg2 = CHARNULL; ** ** static ** CMD_OBJECT ** MyCmd ** ** CMD_NAME ** "mycmd -- one line statement of purpose" ** ** CMD_DESCRIPTION ** "Mycmd will try really really hard to run without errors \ ** and do whatever the heck it is supposed to do. If (God forbid) \ ** something should actually go wrong it will say so." ** ** CMD_ARGUMENTS ** 'b', ARGOPT, argSBool, __ &bflag, ** "bflag -- turn on `b'-mode (whatever that is)", ** ** ' ', ARGREQ, argStr, __ &arg1, ** "arg1 -- first argument to this spiffy program", ** ** ' ', ARGOPT, argStr, __ &arg2, ** "arg2 -- optional second argument to this spiffy program", ** ** END_ARGUMENTS ** CMD_END ** ** main( argc, argv ) ** int argc; ** char *argv[]; ** { ** (void) parseargs( argv, MyCmd ); ** (void) dostuff(); ** exit( 0 ); ** } ***^^**********************************************************************/ #define CMD_OBJECT ARGDESC #define CMD_NAME [] = { { '\0', (argMask_t)0x0000, (argTypePtr_t) #define CMD_DESCRIPTION , ARBNULL, #define CMD_ARGUMENTS }, #define START_ARGUMENTS ARG_EMPTY #define END_ARGUMENTS ARG_EMPTY #define CMD_END }; X X /* X ** shorthand for declaring main program X */ #ifdef __ANSI_C__ # define MAIN(argc,argv) int main( int argc, char *argv[] ) #else # define MAIN(argc,argv) int main( argc, argv ) int argc; char *argv[]; #endif X /*************************************************************************** ** ^SECTION: MULTI-VALUED_ARGUMENTS ** Parseargs supports two different types of multi-valued arguments: ** linked-lists and vectors. The linked-lists are called argument lists ** (or arg-lists) and are specified by supplying the ARGLIST flag along ** with an associated listXxxx argument-translation routine. The value ** associated with an arg-list should be a list structure of type ArgList. ** The include file <parseargs.h> defines four macros for manipulating ** ArgList structures: ARGLISTNULL, L_NEXT, L_STRING, and L_FLAGS. ** ** ARGLISTNULL is simply the NULL argument-list pointer. L_NEXT and ** L_STRING each take a pointer to a non-NULL ArgList structure. L_NEXT ** returns the address of the next item in the list and L_STRING returns ** the string-value of the current list-item. L_FLAGS return the arg- ** flags for a given item in the list. With non-multivalued, only the ** flags in the argument descriptor are needed; lists and vectors however ** need a set of flags for each item they contain. Once an arg-list has ** been created, it may be deallocated using the function listFree. List- ** Free takes two parameters, the first of which is the address of the ** first item in the arg-list, and the second of which is a boolean value ** that is TRUE only if each value pointed to by each item should also be ** deallocated. ** ** An alternative to argument-lists is argument vectors (or arg-vectors). ** Arg-vectors use the ARGVEC flag instead of the ARGLIST flag and do not ** require a special listXxxx function for each vector-type. Each of the ** argXxxx functions is responsible for handling vectors of its type ** (although some argXxx functions such as the boolean types do not sup- SHAR_EOF true || echo 'restore of parseargs/parseargs.h failed' fi echo 'End of part 6' echo 'File parseargs/parseargs.h is continued in part 7' echo 7 > _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.