home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- ** ^FILE: parseargs.c - command line interface to parseargs()
- **
- ** 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).
- **
- ** ^DESCRIPTION:
- ** 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 bad 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,
- ** parseargs -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,
- ** parseargs -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.
- **
- ** Given no other arguments,
- ** parseargs -#
- ** prints on standard output, the current version and patchlevel of the
- ** running version of parseargs.
- **
- ** ^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.
- **
- ** ^HISTORY:
- ** 07/18/90 Brad Appleton <brad@ssd.csd.harris.com> Created
- **
- ** 02/--/91 Brad Appleton <brad@ssd.csd.harris.com>
- ** - Added awk, perl, and rc to the list of shells to generate output for
- ** - added calls to vecFree in cleanup()
- **
- ** 03/31/91 Brad Appleton <brad@ssd.csd.harris.com>
- ** - fixed bug in cleanup for SunOS (added free_vectors())
- ** - fixed printing of single quotes for perl scripts
- ** - added -C option for case-insensitivity
- ** - added hidden -# option to print current version and patchlevel
- **
- ** 27/08/91 Earl Chew <cechew@bruce.cs.monash.edu.au>
- ** - Use ProgNameLen when accessing ProgName
- ***^^**********************************************************************/
-
- #include <stdio.h>
- #include <errno.h>
- #include <useful.h>
- #include "strfuncs.h"
- #include "patchlevel.h"
-
- #define PARSEARGS_PRIVATE /* include private definitions */
- #include "parseargs.h"
-
-
- /*************************************************************************
- ** ^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.
- */
- /**^^**********************************************************************/
-
- /* default shell variable values for a boolean argument */
- static CONST char Default_StrTrue[] = "TRUE";
- static CONST char Default_StrFalse[] = "";
-
- /* 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";
-
- /* macros to improve readability of string tests */
- #define strEQ(s1,s2) !strcmp(s1, s2)
- #define strnEQ(s1,s2,n) !strncmp(s1, s2, n)
-
- #define BUFFER_SIZE 1024 /* start off with 1k buffer & resize it */
- #define ESCAPED_COMMA '\001' /* character to replace commas with */
-
- /* determine the beginning and end of a struct */
- #define c_BEGIN_STRUCT '{'
- #define c_END_STRUCT '}'
-
- /* determine beginning-of-arg-table string */
- #define s_START_ARGS "STARTOFARGS"
- #define isSTART_ARGS(s) strnEQ(s, s_START_ARGS, 5)
-
- /* determine end-of-arg-table string */
- #define s_END_ARGS "ENDOFARGS"
- #define isEND_ARGS(s) strnEQ(s, s_END_ARGS, 3)
-
- /* define #of entries per arg-descriptor */
- #define NFIELDS 5
-
-
- /**************************************************************************
- ** ^SECTION: SHELLS
- ** After the command line has been parsed, parseargs will print on
- ** standard 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
- ** command 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 the following shells as
- ** valid command-interpreters:
- **
- ** sh
- ** bash
- ** ksh
- ** csh
- ** tcsh
- ** itcsh
- ** ash
- ** zsh
- ** rc
- ** awk
- ** perl
- ** tcl
- **
- ** Awk output is slightly different from that of the other shells in that
- ** the actual variable settings 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
- ** argument 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.
- ***^^*********************************************************************/
-
- /* type used to index into the shell-info array */
- typedef unsigned shellidx_t;
-
- /* possible shell names and corresponding types */
- typedef enum {
- SH, /* ash and sh are equivalent for our purposes */
- BASH,
- CSH, /* tcsh & itcsh are equivalent to csh for our purposes */
- KSH,
- RC,
- ZSH,
- CLAM, /* not yet supported */
- AWK,
- PERL,
- TCL
- } shell_t;
-
- /* structure for shell-specific info */
- typedef struct {
- shell_t type; /* type of shell */
- char *name; /* name of the shell */
-
- char *unset; /* syntax used to unset positional parameters */
-
- char *sclset; /* syntax used to set scalars (%s is the scalar name) */
- char *sclpfx; /* prefix used for scalars */
- char *sclsfx; /* suffix used for scalars */
-
- char *aryset; /* syntax used to set arrays (%s is the array name) */
- char *arypfx; /* prefix used for arrays */
- char *arysep; /* separator used for arrays */
- char *arysfx; /* suffix used for arrays */
-
- char **escapes; /* an array of strings (the last of which must be NULL).
- ** For each string - the first character is the character
- ** to escape and the remainder of the string is the
- ** escape sequence to use for that character.
- */
- } shell_info;
-
-
- /* Here are the escape-arrays for the various shells
- */
- static char * sh_escapes[] = {
- "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\\\\\\", "''\\''",
- CHARNULL
- };
-
- static char * csh_escapes[] = {
- "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\n\\\n", "\\\\\\", "''\\''", "!\\!",
- CHARNULL
- };
-
- static char * zsh_escapes[] = {
- "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\\\\\\", "''\\''", "!\\!",
- CHARNULL
- };
-
- static char * rc_escapes[] = {
- "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\\\\\\", "'''", CHARNULL
- };
-
- static char * perl_escapes[] = {
- "\b\\b", "\r\\r", "\v\\v", "\t\\t", "\n\\n", "\f\\f", "\\\\\\", "'\\'",
- CHARNULL
- };
-
- static char * tcl_escapes[] = {
- "\b\\b", "\r\\r", "\v\\v", "\t\\t", "\n\\n", "\f\\f", "\\\\\\", "\"\\\"",
- "{\\{", "}\\}", "[\\[", "]\\]", "$\\$",
- CHARNULL
- };
-
- static char * awk_escapes[] = { "'''", CHARNULL };
-
-
- /* array of shell info records for supported shells */
- static CONST shell_info Shell[] = {
- {
- /* Bourne Shell */
- SH, "sh",
-
- /* "shift $#" unsets the positional parameters */
- "shift $#;\n",
-
- /* "name='value'" assigns "value" to the variable "name" */
- "%s=", "'", "';\n",
-
- /* "ary='elt1 elt2 ...'" assigns an array named "ary" */
- "%s=", "'", "%s", "';\n",
-
- sh_escapes
- },
- {
- /* Bourne-Again shell -- we treat it the same as the Bourne shell.
- ** (this should change when BASH supports arrays)
- */
- BASH, "bash",
- "shift $#;\n",
- "%s=", "'", "';\n",
- "%s=", "'", "%s", "';\n",
- sh_escapes
- },
- {
- /* Korn Shell */
- KSH, "ksh",
-
- /* "set -- ;" unsets the positional parameters */
- "set --;\n",
-
- /* "name='value'" assigns "value" to the variable "name" */
- "%s=", "'", "';\n",
-
- /* "set [-+]A ary 'elt1' 'elt2' ..." assigns an array named "ary" */
- "set %cA %s ", "'", "'%s'", "';\n",
-
- sh_escapes
- },
- {
- /* C-Shell */
- CSH, "csh",
-
- /* "set argv=()" unsets the positional parameters */
- "set argv=();\n",
-
- /* "set name='value'" assigns "value" to the variable "name" */
- "set %s=", "'", "';\n",
-
- /* "set ary=( 'elt1' 'elt2' ... )" assigns an array named "ary" */
- "set %s=", "( '", "'%s'", "' );\n",
-
- csh_escapes
- },
- {
- /* Z-Shell -- this is a lot like the C-Shell except we dont need
- ** the 'set' keyword when assigning variables and arrays
- */
- ZSH, "zsh",
-
- /* "argv=()" unsets the positional parameters */
- "argv=();\n",
-
- /* "name='value'" assigns "value" to the variable "name" */
- "%s=", "'", "';\n",
-
- /* "ary=( 'elt1' 'elt2' ... )" assigns an array named "ary" */
- "%s=", "( '", "'%s'", "' );\n",
-
- zsh_escapes
- },
- {
- /* rc -- the Plan 9 shell designed by Tom Duff */
- RC, "rc",
-
- /* "*=()" unsets the positional parameters */
- "*=();\n",
-
- /* "name='value'" assigns "value" to the variable "name" */
- "%s=", "'", "';\n",
-
- /* "ary=( 'elt1' 'elt2' ... )" assigns an array named "ary" */
- "%s=", "( '", "'%s'", "' );\n",
-
- rc_escapes
- },
- {
- /* perl - Larry Wall's Practical Extraction and Report Language */
- PERL, "perl",
-
- /* "@ARGV = ()" unsets the positional parameters */
- "@ARGV = ();\n",
-
- /* "$name = 'value' ;" assigns "value" to the variable "name" */
- "$%s = ", "'", "';\n",
-
- /* "@ary = ( 'elt1', 'elt2', ... );" assigns an array named "ary" */
- "@%s = ", "( '", "', '", "' );\n",
-
- perl_escapes
- },
- {
- /* TCL - Tool Command Language */
- TCL, "tcl",
-
- /* "set argv {}" unsets the positional parameters */
- "set argv {} ;\n",
-
- /* "set $name {value} ;" assigns "value" to the variable "name" */
- "set %s ", "\"", "\" ;\n",
-
- /* "set ary { {elt1} {elt2} ... };" assigns an array named "ary" */
- "set %s ", "[ list \"", "\" \"", "\" ] ;\n",
-
- tcl_escapes
- },
- {
- /* awk -- Aho, Weinberger, & Kernighan's pattern-action language
- **
- ** we treat awk differently then the other shells. This is because
- ** we cant use actual awk syntax (since awk doesnt have the equivalent
- ** of an 'eval' command). Instead, we write out an input stream for
- ** consisting or variable assignments. Records are multi-line, and
- ** separated by a blank line. Fields are separated by a newline.
- ** The first field is the name of the variable and the remaining
- ** fields (if more than one remains we have an array) of the values
- ** associated with the variable.
- */
- AWK, "awk",
- "ARGV\n\n",
- "%s\n", "", "\n\n",
- "%s\n", "", "%s", "\n\n",
- awk_escapes
- }
- };
-
- static size_t NumShells = ( sizeof(Shell) / sizeof(shell_info) );
-
-
- /*************************************************************************/
-
- /* 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 */
-
- /* union to hold all possibles values of an argument */
- typedef union {
- BOOL Bool_val;
- short Short_val;
- int Int_val;
- long Long_val;
- float Float_val;
- double Double_val;
- char Char_val;
- char *Str_val;
- strvec_t Str_vec;
- charvec_t Char_vec;
- intvec_t Int_vec;
- shortvec_t Short_vec;
- longvec_t Long_vec;
- floatvec_t Float_vec;
- doublevec_t Double_vec;
- genericvec_t Vector;
- } storage_t;
-
- /* structure to hold a command-line argument name, value, and fmt-string */
- typedef struct {
- CONST char *name; /* name of shell variable to use */
- storage_t value; /* storage for value of argument */
- } cmdarg_t;
- #define CMDARGNULL (cmdarg_t *)NULL
-
- 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 *));
-
- /*************************************************************************/
-
- /*
- ** argVers() -- This is the function used to print the version of parseargs
- ** on standard output and then exit (regardless of where its
- ** corresponding '-#' appears on the command line and regardless
- ** of what may have preceded it).
- */
- /*ARGSUSED*/
- #ifdef __ANSI_C__
- static BOOL argVers( register ARGDESC *ad, register char *vp, BOOL copyf )
- #else
- static BOOL argVers( ad, vp, copyf )
- register ARGDESC *ad;
- register char *vp;
- BOOL copyf;
- #endif
- {
- printf( "%s\n", _Ident );
- exit( e_USAGE );
- }
-
- /*
- ** variables that are set via command-line arguments
- */
- static char *Cmd_Name; /* name of this program */
-
- static ARGDESC *UsrArgd = ARGDESCNULL; /* users arg-table */
- static cmdarg_t *UsrVals = CMDARGNULL; /* variable names & values */
- static int UsrArgc = 0; /* # of arg-table entries */
- static shellidx_t UsrSh; /* shell indicator */
- static BOOL UseStdin = TRUE; /* read argd from stdin */
-
- 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? */
- static BOOL AnyCase = FALSE; /* ?case-insensitivity? */
- static BOOL Flags1st = FALSE; /* ?non-positionals first? */
-
- /*************************************************************************/
- /* now we are ready to define the command-line */
- static
- CMD_OBJECT
- Args
-
- CMD_NAME
- "parseargs -- parse command-line arguments in shell scripts"
-
- CMD_DESCRIPTION
- "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."
-
- CMD_ARGUMENTS
- '#', ARGNOVAL, argVers, __ NULL,
- "VERsion : just print program version, dont parse command-line",
-
- 'U', ARGOPT, argBool, __ &PrUsage,
- "usage : just print program usage, dont parse command-line",
-
- 'M', ARGOPT, argBool, __ &PrManual,
- "man1 : just print man1 template, dont parse command-line",
-
- 'T', ARGOPT, argStr, __ &StrTrue,
- "TRUEstr : string to use for TRUE Booleans (default=\"TRUE\")",
-
- 'F', ARGOPT, argStr, __ &StrFalse,
- "FALSEstr : string to use for FALSE Booleans (default=\"\")",
-
- 'C', ARGOPT, argBool, __ &AnyCase,
- "caseignore : parse options using case-insensitivity",
-
- 'A', ARGOPT, argBool, __ &ModArr,
- "array : modify the behavior of arrays",
-
- 'S', ARGOPT, argStr, __ &FieldSep,
- "SEParator : field-separator-string used to delimit array elements \
- (default=\" \")",
-
- 'a', ARGOPT, argStr, __ &ArgdString,
- "ARGSpec : argument specification string",
-
- 'e', ARGOPT, argStr, __ &ArgdEnv,
- "ENVarname : environment variable containing arg-spec",
-
- 'f', ARGOPT, argStr, __ &ArgdFname,
- "FILEname : read the arg-spec from <filename> (default=stdin)",
-
- 'l', ARGOPT, argBool, __ &KwdsOnly,
- "Long-OPTionS : long-options only - do not parse options",
-
- 'o', ARGOPT, argBool, __ &OptsOnly,
- "OPTionS : options only - do not parse long-options",
-
- 's', ARGOPT, argStr, __ &ShellName,
- "SHell : use <shell> command syntax (default=\"sh\")",
-
- 'u', ARGOPT, argBool, __ &Unset,
- "unset : unset positional parameters before parsing",
-
- 'p', ARGOPT, argBool, __ &Prompt,
- "prompt : prompt the user for missing required arguments",
-
- 'i', ARGOPT, argBool, __ &Ignore,
- "ignore : ignore bad command-line syntax and continue processing \
- (instead of aborting)",
-
- '1', ARGOPT, argBool, __ &Flags1st,
- "1st : force non-positional arguments to precede all positional arguments",
-
- '-', ARGOPT, argDummy, __ NULL,
- "+ : end of options - all remaining arguments are interpreted as \
- positional parameters (even if one begins with '-' or '+')",
-
- ' ', ARGREQ, argStr, __ &UsrName,
- "name : name of calling program",
-
- ' ', ARGOPT|ARGVEC, argStr, __ &UsrArgv,
- "arguments : arguments to calling program",
-
- END_ARGUMENTS
-
- CMD_END
-
-
- /***************************************************************************
- ** ^FUNCTION: free_vectors - deallocate all vectors in an ARGDESC
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID free_vectors( argd )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC argd[];
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Free_vectors will deallocate the storage used for each arg-vector
- ** referenced by argd.
- **
- ** ^REQUIREMENTS:
- ** None.
- **
- ** ^SIDE-EFFECTS:
- ** Storage associated with all dynamically allocated arg-vectors
- ** is released and set to NULL.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static void free_vectors( ARGDESC argd[] )
- #endif
- {
- register ARGDESC *ad;
- register storage_t val;
-
- if ( !argd || !CMD_isINIT(argd) ) return;
-
- for ( ad = ARG_FIRST(argd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
- if ( ! BTEST(arg_flags(ad), ARGVEC) ) continue;
-
- val = *((storage_t *) arg_valp(ad));
-
- if ( arg_type(ad) == argStr ) vecFree( val.Str_vec, char * );
- else if ( arg_type(ad) == argChar ) vecFree( val.Char_vec, char );
- else if ( arg_type(ad) == argInt ) vecFree( val.Int_vec, int );
- else if ( arg_type(ad) == argShort ) vecFree( val.Short_vec, short );
- else if ( arg_type(ad) == argLong ) vecFree( val.Long_vec, long );
- else if ( arg_type(ad) == argFloat ) vecFree( val.Float_vec, float );
- else if ( arg_type(ad) == argDouble ) vecFree( val.Double_vec, double );
- }
-
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: cleanup - deallocate all global storage
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- 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-EFFECTS:
- ** Storage associated with all dynamically allocated global-variables
- ** is released and set to NULL.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static void cleanup( void )
- #endif
- {
- /* free up tables */
- vecFree( UsrArgv, char * );
- if ( UsrArgd ) {
- free_vectors( UsrArgd );
- free( UsrArgd );
- UsrArgd = ARGDESCNULL;
- }
- if ( UsrVals ) {
- free( UsrVals );
- UsrVals = CMDARGNULL;
- }
- if ( ArgdFname && !ArgdEnv ) {
- free( ArgdString );
- ArgdString = CHARNULL;
- }
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: ckalloc - allocate space, check for success
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static ARBPTR ckalloc( size )
- /*
- ** ^PARAMETERS:
- */
- size_t size;
- /* -- the number of bytes to allocate
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** 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__
- static ARBPTR ckalloc( size_t size )
- #endif
- {
- ARBPTR ptr;
-
- ptr = (ARBPTR)malloc( size );
- if ( !ptr ) {
- eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name );
- cleanup();
- if ( errno ) perror( Cmd_Name );
- exit( e_SYSTEM );
- }
-
- return ptr;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: ckrealloc - reallocate space, check for success
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static ARBPTR ckrealloc( ptr, size )
- /*
- ** ^PARAMETERS:
- */
- ARBPTR ptr;
- /* -- address of the region to be expanded/shrunk
- */
- size_t size;
- /* -- the number of bytes to allocate
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** 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__
- static ARBPTR ckrealloc( ARBPTR ptr, size_t size )
- #endif
- {
- ptr = realloc( ptr, (unsigned int)size );
- if ( !ptr ) {
- eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name );
- cleanup();
- if ( errno ) perror( Cmd_Name );
- exit( e_SYSTEM );
- }
-
- return ptr;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: escape_char - (re)map a character
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID escape_char( str, ch, esc )
- /*
- ** ^PARAMETERS:
- */
- char *str;
- /* -- the string to be translated
- */
- int ch;
- /* -- the character to be replaced/translated
- */
- int esc;
- /* -- the replacement character to use
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** Each occurrence in <str> of <ch> within single or double quotes is
- ** replaced with <esc>.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static void escape_char( char *str, int ch, int esc )
- #endif
- {
- int squoted = 0, dquoted = 0;
-
- for ( ; *str ; str++ ) {
- if ( *str == '\'' && !dquoted )
- squoted = ~squoted;
- else if ( *str == '"' && !squoted )
- dquoted = ~dquoted;
- else if ( (squoted || dquoted) && *str == ch )
- *str = esc;
- }
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: restore_char - restore any chars escaped by escape_char()
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID restore_char( str, ch, esc )
- /*
- ** ^PARAMETERS:
- */
- char *str;
- /* -- the string to be translated
- */
- int ch;
- /* -- the character to be restored
- */
- int esc;
- /* -- the replacement character to use to escape the above character.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** Each occurrence of <esc> in <str> is replaced with <ch>.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- void restore_char( char *str, int ch, int esc )
- #endif
- {
- for ( ; *str ; str++ )
- if ( *str == esc ) *str = ch;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: get_arg_type - return function corresponding to given string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static argTypePtr_t get_arg_type( type_str )
- /*
- ** ^PARAMETERS:
- */
- char *type_str;
- /* -- string corresponding to the name of an existing argXxxx type function.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** Address of the corresponding function
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static argTypePtr_t get_arg_type( const char *type_str )
- #endif
- {
- register CONST char *str = type_str;
-
- /* translate all listXxx into argXxx */
- if ( strnEQ( str, "list", 4 ) )
- str += 4;
-
- if ( strnEQ( str, "arg", 3 ) )
- str += 3;
-
- if ( strEQ( str, "Usage" ) )
- return argUsage;
- else if ( strEQ( str, "Dummy" ) )
- return argDummy;
- else if ( strEQ( str, "Bool" ) )
- return argBool;
- else if ( strEQ( str, "SBool" ) )
- return argSBool;
- else if ( strEQ( str, "UBool" ) )
- return argUBool;
- else if ( strEQ( str, "Int" ) )
- return argInt;
- else if ( strEQ( str, "Short" ) )
- return argShort;
- else if ( strEQ( str, "Long" ) )
- return argLong;
- else if ( strEQ( str, "Float" ) )
- return argFloat;
- else if ( strEQ( str, "Double" ) )
- return argDouble;
- else if ( strEQ( str, "Char" ) )
- return argChar;
- else if ( strEQ( str, "Str" ) )
- return argStr;
- else {
- eprintf( "%s: Fatal Error: invalid argument type '%s'\n",
- Cmd_Name, type_str );
- cleanup();
- exit( e_ARGD );
- }
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: get_arg_flag - return BITMASK corresponding to string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static argMask_t get_arg_flag( flag_str )
- /*
- ** ^PARAMETERS:
- */
- char flag_str[];
- /* -- name of an ARGXXXXX argument-flag
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** The bitmask corresponding to named ARGXXXX flag.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static argMask_t get_arg_flag( const char flag_str[] )
- #endif
- {
- if ( strnEQ( flag_str, "ARG", 3 ) ) {
- if ( strEQ( flag_str+3, "OPT" ) ) return ARGOPT;
- else if ( strEQ( flag_str+3, "REQ" ) ) return ARGREQ;
- else if ( strEQ( flag_str+3, "POS" ) ) return ARGPOS;
- else if ( strEQ( flag_str+3, "VALREQ" ) ) return ARGVALREQ;
- else if ( strEQ( flag_str+3, "VALOPT" ) ) return ARGVALOPT;
- else if ( strEQ( flag_str+3, "HIDDEN" ) ) return ARGHIDDEN;
- else if ( strEQ( flag_str+3, "LIST" ) ) return ARGVEC;
- else if ( strEQ( flag_str+3, "VEC" ) ) return ARGVEC;
- else {
- eprintf( "%s: Fatal Error: invalid argument flag '%s'\n",
- Cmd_Name, flag_str );
- cleanup();
- exit( e_ARGD );
- }
- }
- else {
- eprintf( "%s: Fatal Error: invalid argument flag '%s'\n",
- Cmd_Name, flag_str );
- cleanup();
- exit( e_ARGD );
- }
- }
-
- /***************************************************************************
- ** ^FUNCTION: get_argtable_string - read in the argument-table
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- 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-EFFECTS:
- ** 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__
- static char *get_argtable_string( void )
- #endif
- {
- int isatty ARGS((int));
- #if (!defined(__ANSI_C__) && !defined(vms))
- int fread();
- #endif
- FILE *fp;
- char *buf;
- register size_t nchars = 0; /* # bytes read */
- register size_t bufsiz = 0; /* actual buffer-size needed */
-
- /* open file if necessary */
- if ( UseStdin ) {
- if ( isatty(STDIN) ) {
- /* we wont read the arg-table from a terminal */
- eprintf( "\
- %s: Fatal Error:\n\
- \tcannot read arg-descriptor table from stdin\n\
- \tif stdin is connected to a terminal!\n",
- Cmd_Name );
- cleanup();
- exit( e_ARGD );
- }
- errno = 0; /* reset errno if isatty() was not a terminal */
- fp = stdin;
- }
- else {
- if ( (fp = fopen( ArgdFname, "r")) == FILENULL ) {
- eprintf( "%s: Fatal error: Unable to open %s for reading\n",
- Cmd_Name, ArgdFname );
- cleanup();
- if ( errno ) perror( Cmd_Name );
- exit( e_SYSTEM );
- }
- }
-
- /* get initial block for buffer */
- buf = (char *)ckalloc( BUFFER_SIZE * sizeof(char) );
-
- /*
- ** Loop reading characters into buffer and resizing as needed
- */
- do {
- /* read fildes into the buffer */
- nchars = (size_t) fread( &(buf[bufsiz]), sizeof(char), BUFFER_SIZE, fp );
- if ( ferror(fp) ) {
- eprintf( "\
- %s: Fatal Error:\n\
- \tBad return from fread() while reading argument descriptor table\n",
- Cmd_Name );
- free(buf);
- cleanup();
- if ( errno ) perror( "" );
- exit( e_SYSTEM );
- }
- errno = 0; /* errno is undefined after a succesful fread() */
- bufsiz += nchars;
-
- /* see if we need to grow the buffer */
- if ( nchars == BUFFER_SIZE )
- buf = (char *)ckrealloc( buf, (bufsiz + BUFFER_SIZE) * sizeof(char) );
- } while ( nchars == BUFFER_SIZE );
-
- /* shrink the buffer down to the exact size used */
- buf = (char *)ckrealloc( buf, (bufsiz + 1) * sizeof(char) );
-
- /* close file if necessary */
- if ( !UseStdin ) (VOID) fclose( fp );
-
- return buf;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: get_shell_index - return shell corresponding to given string
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static shell_t get_shell_index( sh_str )
- /*
- ** ^PARAMETERS:
- */
- char *sh_str;
- /* -- string corresponding tp the basename of a shell/command-interpreter
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Get_shell_index will return 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-EFFECTS:
- ** None.
- **
- ** ^RETURN-VALUE:
- ** The corresponding shell-type.
- **
- ** ^ALGORITHM:
- ** Trivial.
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static shellidx_t get_shell_index ( const char *sh_str )
- #endif
- {
- int i;
- register CONST char *sh = sh_str;
-
- /* special case to recognize ash, tcsh & itcsh */
- if ( strEQ( sh, "ash" ) ) ++sh;
- else if ( strEQ( sh, "tcsh" ) ) ++sh;
- else if ( strEQ( sh, "itcsh" ) ) sh += 2;
-
- for ( i = 0 ; i < NumShells ; i++ ) {
- if ( strEQ( sh, Shell[i].name ) ) return (shellidx_t) i;
- }
-
- usrerr( "Unknown shell \"%s\"", sh_str );
- eprintf( "\tKnown shells are listed below:\n" );
- for ( i = 0 ; i < NumShells ; i++ ) {
- if ( strEQ( "csh", Shell[i].name ) ) {
- eprintf( "\t\tcsh/tcsh/itcsh\n" );
- }
- else {
- eprintf( "\t\t%s\n", Shell[i].name );
- }
- }
-
- cleanup();
- exit( e_SYNTAX );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: build_tables - build the Argument and Value tables
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static int build_tables( argd_str )
- /*
- ** ^PARAMETERS:
- */
- char argd_str[];
- /* -- the comma-separated table of argument descriptions
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** 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__
- static int build_tables( char argd_str[] )
- #endif
- {
- char **token_vec, **flags_vec, *type_name;
- int i = 0, j = 0, idx, token_num = 0, flags_num = 0;
- int argc = 0, ad_idx;
- BOOL start_string_used = FALSE, is_braces = FALSE;
-
- /* what about NULL or empty-string */
- if ( !argd_str || !*argd_str ) return 0;
-
- /* escape all commas inside of single or double quotes */
- escape_char( argd_str, ',', ESCAPED_COMMA );
-
- /* parse Argument Table String */
- token_num = strsplit( &token_vec, argd_str, ArgTableDelims );
- if ( token_num ) (VOID) strtrim( token_vec[ token_num - 1 ], WhiteSpace );
-
- /* see if we need to build the tables at all */
- if ( token_num == 0 || isEND_ARGS(*token_vec) ) {
- free( token_vec );
- return 0; /* nothing to parse */
- }
-
- /* make sure table is properly terminated */
- if ( !isEND_ARGS( token_vec[ --token_num ] ) ) {
- restore_char( token_vec[ token_num ], ',', ESCAPED_COMMA );
- 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",
- Cmd_Name, s_END_ARGS, token_vec[ token_num ] );
- free( token_vec );
- cleanup();
- exit( e_ARGD );
- }
-
- /* check for optional start-string */
- (VOID) strtrim( *token_vec, WhiteSpace );
- if ( isSTART_ARGS(*token_vec) ) {
- start_string_used = TRUE;
- --token_num;
- ++token_vec;
- }
-
- /* make sure table has proper number of arguments */
- if ( (token_num % NFIELDS) != 0 ) {
- 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",
- Cmd_Name, NFIELDS, s_END_ARGS );
- free( (start_string_used) ? (token_vec - 1) : token_vec );
- cleanup();
- exit( e_ARGD );
- }
-
- /* determine number of arg-descriptors and allocate arg-tables */
- argc = token_num / NFIELDS;
- UsrArgd = (ARGDESC *) ckalloc( (argc + 2) * sizeof(ARGDESC) );
- UsrVals = (cmdarg_t *) ckalloc( argc * sizeof(cmdarg_t) );
-
- /* start filling in the tables */
- i = 0;
- while ( i < token_num ) {
- restore_char( token_vec[i], ',', ESCAPED_COMMA );
- (VOID) strtrim( token_vec[i], WhiteSpace );
- idx = (i / NFIELDS); /* save index into UsrVals table */
- ad_idx = (idx + 1); /* save index into UsrArgd table */
- is_braces = FALSE;
-
- /* remove first curly-brace if its present (this has the drawback
- ** of disallowing a left curly-brace from being an option character).
- */
- if ( token_vec[i][0] == c_BEGIN_STRUCT ) {
- token_vec[i][0] = ' ';
- (VOID) strltrim( token_vec[i], WhiteSpace );
- is_braces = TRUE;
- }
-
- /* get argument name */
- UsrArgd[ ad_idx ].ad_name = *(token_vec[i++]);
- if ( !UsrArgd[ ad_idx ].ad_name ) UsrArgd[ ad_idx ].ad_name = ' ';
-
- /*
- ** get argument flags, flags may be ORed together so I
- ** need to parse the flags for each individual flag used
- */
- UsrArgd[ ad_idx ].ad_flags = (argMask_t) 0; /* initialize */
- flags_num = strsplit( &flags_vec, token_vec[i++] , ArgFlagsDelims );
- for ( j = 0 ; j < flags_num ; j++ ) {
- (VOID) strtrim( flags_vec[j], WhiteSpace );
- UsrArgd[ ad_idx ].ad_flags |= get_arg_flag( flags_vec[j] );
- }
- free( flags_vec );
-
- /* get argument type and name for Value table */
- type_name = strtrim( token_vec[i++], WhiteSpace );
- restore_char( token_vec[i], ',', ESCAPED_COMMA );
- UsrVals[ idx ].name = strtrim( token_vec[i++], WhiteSpace );
-
- /* remove any leading "__" from the name */
- if ( strnEQ("__", UsrVals[ idx ].name, 2) ) {
- (VOID) strltrim( (char *)UsrVals[idx].name, "_ \t\n\r\v\f" );
- }
-
- /* remove any leading '&', '$', and '@' from the name */
- if ( strchr("&$@", UsrVals[ idx ].name[0]) ) {
- (VOID) strltrim( (char *)UsrVals[idx].name, "&$@ \t\n\r\v\f" );
- }
-
- /* get type and value pointer for Arg table */
- UsrArgd[ ad_idx ].ad_type = get_arg_type( type_name );
- UsrArgd[ ad_idx ].ad_valp = __ &(UsrVals[ idx ].value);
-
- /* if we have a vector we need to initialize it */
- if ( ARG_isVEC((UsrArgd + ad_idx)) ) {
- UsrVals[ idx ].value.Vector.count = 0;
- UsrVals[ idx ].value.Vector.array = (VOID *)NULL;
- }
-
- /* get argument prompt/description */
- restore_char( token_vec[i], ',', ESCAPED_COMMA );
- UsrArgd[ ad_idx ].ad_prompt = strtrim( token_vec[i++], WhiteSpace );
-
- /* if in curly-braces, remove the trailing brace */
- if ( is_braces ) {
- int last = strlen( UsrArgd[ad_idx].ad_prompt ) - 1;
- if ( UsrArgd[ ad_idx ].ad_prompt[ last ] == c_END_STRUCT ) {
- *((char *)(UsrArgd[ ad_idx ].ad_prompt) + last) = '\0';
- (VOID) strrtrim( (char *)UsrArgd[ad_idx].ad_prompt, WhiteSpace );
- }
- }/*end-if*/
- }/*while*/
-
- /* free up token tables (just the arrays, not the actual elements) */
- free( flags_vec );
- free( (start_string_used) ? (token_vec - 1) : token_vec );
-
- /* set up first & last argument entries */
- (UsrArgd -> ad_name) = UsrArgd[ argc+1 ].ad_name = '\0';
- (UsrArgd -> ad_flags) = UsrArgd[ argc+1 ].ad_flags = (argMask_t) 0;
- (UsrArgd -> ad_type) = UsrArgd[ argc+1 ].ad_type = argNULL;
- (UsrArgd -> ad_valp) = UsrArgd[ argc+1 ].ad_valp = ARBNULL;
- UsrArgd[ argc+1 ].ad_prompt = CHARNULL;
-
- /* try to get a command-description */
- cmd_description(UsrArgd) = getenv( "DESCRIPTION" );
- if ( !cmd_description(UsrArgd) ) {
- cmd_description(UsrArgd) = getenv( "CMD_DESCRIPTION" );
- }
-
- return argc;
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: put_char_arg - print a character
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static put_char_arg( fp, ch )
- /*
- ** ^PARAMETERS:
- */
- FILE *fp;
- /* -- the output stream to write to.
- */
- int ch;
- /* -- the character to print
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** 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__
- static void put_char_arg( FILE *fp, int ch )
- #endif
- {
- char **escapes = Shell[ UsrSh ].escapes;
- BOOL found = FALSE;
- for ( ; (escapes && *escapes) ; escapes++ ) {
- char * esc = *escapes;
- if ( *esc == ch ) {
- fputs( esc + 1, fp );
- found = TRUE;
- }
- }
- if ( ! found ) fputc( ch, fp );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: put_str_arg - same as put_char_arg but for a string!
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID put_str_arg( fp, str )
- /*
- ** ^PARAMETERS:
- */
- FILE *fp;
- /* -- the output stream to write to
- */
- char str[];
- /* -- the string to print
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** Output is written to <fp>
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - foreach character in str
- ** - put_char_arg(fp, character)
- ** end-for
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static void put_str_arg( FILE *fp, const char str[] )
- #endif
- {
- if ( !str ) return;
-
- for ( ; *str ; str++ )
- put_char_arg( fp, *str );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: put_arg - convert & print the given value into the given buffer
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID put_arg( fp, ad, val, idx )
- /*
- ** ^PARAMETERS:
- */
- FILE *fp;
- /* -- the output stream to write to
- */
- ARGDESC *ad;
- /* -- the argument-descriptor of the argument to print.
- */
- cmdarg_t *val;
- /* -- the value of the argument to print
- */
- 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__ */
-
- /* ^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-EFFECTS:
- ** 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__
- static void put_arg(
- FILE *fp, const ARGDESC *ad, const cmdarg_t *val, short idx
- )
- #endif
- {
- if ( ARG_isVEC(ad) ) {
- if ( idx < 0 || idx >= val->value.Vector.count ) {
- return; /* bad index given */
- }
-
- if ( arg_type(ad) == argStr ) {
- put_str_arg( fp, val->value.Str_vec.array[idx] );
- }
- else if ( arg_type(ad) == argChar ) {
- put_char_arg( fp, val->value.Char_vec.array[idx] );
- }
- else if ( arg_type(ad) == argDouble ) {
- fprintf( fp, "%lf", val->value.Double_vec.array[idx] );
- }
- else if ( arg_type(ad) == argFloat ) {
- fprintf( fp, "%f", val->value.Float_vec.array[idx] );
- }
- else if ( arg_type(ad) == argLong ) {
- fprintf( fp, "%ld", val->value.Long_vec.array[idx] );
- }
- else if ( arg_type(ad) == argInt ) {
- fprintf( fp, "%d", val->value.Int_vec.array[idx] );
- }
- else if ( arg_type(ad) == argShort ) {
- fprintf( fp, "%ld", val->value.Short_vec.array[idx] );
- }
-
- /* Boolean vectors are not supported */
- }/*if vector*/
- else {
- if ( arg_type(ad) == argStr ) {
- put_str_arg( fp, val->value.Str_val );
- }
- else if ( arg_type(ad) == argChar ) {
- put_char_arg( fp, val->value.Char_val );
- }
- else if ( arg_type(ad) == argDouble ) {
- fprintf( fp, "%lf", val->value.Double_val );
- }
- else if ( arg_type(ad) == argFloat ) {
- fprintf( fp, "%f", val->value.Float_val );
- }
- else if ( arg_type(ad) == argLong ) {
- fprintf( fp, "%ld", val->value.Long_val );
- }
- else if ( arg_type(ad) == argInt ) {
- fprintf( fp, "%d", val->value.Int_val );
- }
- else if ( arg_type(ad) == argShort ) {
- fprintf( fp, "%ld", val->value.Short_val );
- }
- else if ( ARG_isBOOLEAN(ad) ) {
- fprintf( fp, "%s", (val->value.Bool_val) ? StrTrue : StrFalse );
- }
- }/*else !vector*/
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: print_argvector - print shell variable settings for an ARGVEC
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID print_argvector( ad, val )
- /*
- ** ^PARAMETERS:
- */
- ARGDESC *ad;
- /* -- the argument-descriptor of the vector to print
- */
- cmdarg_t *val;
- /* -- the value of the vector to print
- */
- #endif /* !__ANSI_C__ */
-
- /* ^DESCRIPTION:
- ** Parseargs treats ARGLIST 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 ARGLIST arguments are treated exactly the same as ARGVEC
- ** arguments). With the exception perl which always uses a comma to
- ** separate array elements, all shells will use the string specified
- ** with the -S option as the field separator between elements of an
- ** array (the default field separator is a space character).
- **
- ** ^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 & Z shells (zsh, csh, tcsh, itcsh), 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 then 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
- ** character (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_Shell_Argument_Lists:
- ** For the C shells (csh, tcsh, itcsh), 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 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 a word-list and set using the following
- ** syntax:
- **
- ** name=( 'arg1' 'arg2' ... )
- **
- ** ^Z_Shell_Argument_Lists:
- ** For the Z shell, ARGLIST variables are treated as word-lists and are
- ** assigned using the following syntax:
- **
- ** 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 Z and C shell word-lists
- ** start at index one.
- **
- ** ^Awk_Argument_Lists:
- ** For awk, if the -A option is not given, then the output for the
- ** 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).
- **
- ** 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 example):
- **
- ** 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 and Z shells, 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 is 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-EFFECTS:
- ** 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__
- static void print_argvector( const ARGDESC *ad, const cmdarg_t *val )
- #endif
- {
- register shell_t cli = Shell[ UsrSh ].type;
- BOOL set_printed = FALSE;
- int i;
- char *varname;
-
- /* need to check for '--' for sh, ksh, and bash */
- if ( strEQ("--", val->name) && (cli == SH || cli == BASH || cli == KSH) ) {
- printf( "set -- " );
- set_printed = TRUE;
- }
- /* if faking arrays for sh, bash, or awk -- do it now! */
- else if ( ModArr && (cli == SH || cli == BASH || cli == AWK) ) {
- i = strlen( val->name );
- varname = (char *)ckalloc( (i + 4) * sizeof(char) );
- for ( i = 0 ; i < val->value.Vector.count ; i++ ) {
- sprintf( varname, "%s%d", val->name, i+1 );
- printf( Shell[ UsrSh ].sclset, varname );
- printf( Shell[ UsrSh ].sclpfx );
- put_arg( stdout, ad, val, i );
- printf( "%s", Shell[ UsrSh ].sclsfx );
- }
- sprintf( varname, "%s_count", val->name );
- printf( Shell[ UsrSh ].sclset, varname );
- printf( Shell[ UsrSh ].sclpfx );
- printf( "%d", val->value.Vector.count );
- printf( "%s", Shell[ UsrSh ].sclsfx );
- return;
- }
-
- /* print the array already */
- if ( !set_printed ) {
- if ( cli == KSH ) {
- printf( Shell[ UsrSh ].aryset, ((ModArr) ? '+' : '-'), val->name );
- }
- else {
- printf( Shell[ UsrSh ].aryset, val->name );
- }
- }
- printf( Shell[ UsrSh ].arypfx );
- for ( i = 0 ; i < val->value.Vector.count ; i++ ) {
- put_arg( stdout, ad, val, i );
- if ( (i + 1) != val->value.Vector.count ) {
- printf( Shell[ UsrSh ].arysep, FieldSep );
- }
- }
- printf( Shell[ UsrSh ].arysfx );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: print_args - print the shell variable settings for the usr args
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- static VOID print_args( vals, argd )
- /*
- ** ^PARAMETERS:
- */
- cmdarg_t *vals;
- /* -- the table of argument values.
- */
- ARGDESC *argd;
- /* -- the table of argument-descriptors.
- */
- #endif /* !__ANSI_C__ */
-
- /* ^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-EFFECTS:
- ** 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__
- static void print_args( const cmdarg_t *vals, const ARGDESC *argd )
- #endif
- {
- register CONST ARGDESC *ad;
- register int i;
- argName_t buf;
-
- /* print values for all options given */
- for ( ad = ARG_FIRST(argd), i = 0 ; !ARG_isEND(ad) ; ARG_ADVANCE(ad), i++ ) {
- if ( ARG_isGIVEN(ad) ) {
- /******************************************************************
- ** ^SECTION: ARGVALOPT
- ** Options that may take an optional argument need special
- ** consideration. The shell programmer needs to know whether
- ** or not the option was given, and (if given) if it was
- ** accompanied by an argument. In order to accommodate this
- ** need, parseargs will set an additional shell variable for
- ** each argument that is given the ARGVALOPT flag if it is
- ** supplied on the command line regardless of whether or not
- ** it was accompanied by its optional argument. If the user
- ** has defined an option which may optionally take an argument
- ** and the option appears on the command line with or without
- ** its associated argument, then the shell variable <name>_flag
- ** will be assigned the value "TRUE" (or the value supplied with
- ** the -T option to parseargs) where <name> is the name of the
- ** shell variable associated with the option in the argument
- ** description string.
- ***^^*************************************************************/
- if ( ARG_isVALOPTIONAL(ad) ) {
- sprintf(buf, "%s_flag", vals[i].name);
- printf( Shell[ UsrSh ].sclset, buf);
- printf( Shell[ UsrSh ].sclpfx );
- printf( "%s%s", StrTrue, Shell[ UsrSh ].sclsfx );
-
- if ( !ARG_isVALGIVEN(ad) ) continue;
- }/*if OPTARG*/
-
- /* vectors are special */
- if ( ARG_isVEC(ad) ) {
- print_argvector( ad, (vals + i) );
- continue;
- }
-
- /* print shell-specific variable prefix and name */
- printf( Shell[ UsrSh ].sclset, vals[i].name );
- printf( Shell[ UsrSh ].sclpfx );
-
- /* print shell-variable value */
- put_arg( stdout, ad, (vals + i), 0 );
-
- /* print the shell-specific suffix */
- printf( "%s", Shell[ UsrSh ].sclsfx );
- }/*if ARGGIVEN*/
- }/* end-for */
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: unset_positional_parameters - unset shell parameters
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- 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-EFFECTS:
- ** Prints on stdout.
- **
- ** ^RETURN-VALUE:
- ** None.
- **
- ** ^ALGORITHM:
- ** - Use the syntax of the current shell to unset the positional parameters
- ***^^**********************************************************************/
- #ifdef __ANSI_C__
- static void unset_positional_parameters( void )
- #endif
- {
- printf( Shell[ UsrSh ].unset );
- }
-
-
- /***************************************************************************
- ** ^FUNCTION: ck_cmd_errs - check for command syntax errors
- **
- ** ^SYNOPSIS:
- */
- #ifndef __ANSI_C__
- 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-EFFECTS:
- ** - 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__
- static int ck_cmd_errs( void )
- #endif
- {
- /* make sure certain arg-combos were NOT used */
- if ( (ArgdString && (ArgdFname || ArgdEnv)) || (ArgdFname && ArgdEnv) ) {
- eprintf( "%s: only one of `-a', `-e', or `-f' may be given.\n\n",
- Cmd_Name );
- usage( Args );
- exit( e_SYNTAX );
- }
-
- /* make sure at least ONE of KeyWords and Options are enabled */
- if ( OptsOnly && KwdsOnly ) {
- eprintf( "%s: only one of `-o' or `l' may be given.\n\n",
- Cmd_Name );
- exit( e_SYNTAX );
- }
-
- /* turn OFF UseStdin if `-a', `-e', or `-f' was given */
- if (ArgdString || ArgdEnv || ArgdFname)
- UseStdin = FALSE;
-
- /* get the argd-string from an environment variable if so specified */
- if ( ArgdEnv ) {
- ArgdString = getenv( ArgdEnv );
- if ( !ArgdString || !*ArgdString ) {
- eprintf( "%s: variable \"%s\" is NULL or does not exist\n",
- Cmd_Name, ArgdEnv);
- exit( e_NOENV );
- }
- }
-
- /* set up default boolean value strings if needed */
- if ( !StrTrue ) {
- StrTrue = (char *)Default_StrTrue;
- }
- if ( !StrFalse ) {
- StrFalse = (char *)Default_StrFalse;
- }
-
- /* see if we need to "default" the shell name */
- if ( !PrUsage && !PrManual ) {
- if ( !ShellName ) {
- UsrSh = get_shell_index( "sh" ); /* default to Bourne Shell */
- }
- else {
- UsrSh = get_shell_index( basename( ShellName ) );
- }
- }
-
- /* everything is a-ok */
- return e_SUCCESS;
- } /* ck_cmd_errs */
-
-
- /***************************************************************************
- ** ^FUNCTION: main
- **
- ** ^SYNOPSIS:
- ** main( argc, argv )
- **
- ** ^PARAMETERS:
- ** int argc;
- ** -- the number of arguments 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 )
- {
- int rc;
- argMask_t flags = pa_ARGV0;
-
- Cmd_Name = *argv = basename( *argv );
-
- /* parse command-line */
- parseargs( argv, Args );
-
- /* see if there is any reason to exit now */
- (VOID) ck_cmd_errs();
-
- /* set desired option-syntax */
- if ( KwdsOnly ) BSET(flags, pa_KWDSONLY);
- if ( OptsOnly ) BSET(flags, pa_OPTSONLY);
-
- /* if needed - allocate and read in the argument descriptor string */
- if ( !ArgdString ) {
- ArgdString = get_argtable_string();
- if ( ArgdString ) {
- strrtrim( ArgdString, WhiteSpace );
- if ( !*ArgdString ) ArgdString = CHARNULL;
- }
- }
-
- /* fill in the argument tables from the environment variable */
- if ( ArgdString ) UsrArgc = build_tables( ArgdString );
-
- /* if no arguments or options taken, use NULL */
- if ( !UsrArgc ) UsrArgd = ARGDESCNULL;
-
- ProgName = UsrName; /* set up program name */
- ProgNameLen = strlen(ProgName);
-
- if ( PrUsage ) { /* just print usage and exit */
- usage( UsrArgd );
- }
- else if ( PrManual ) { /* print man pages and exit */
- manpage( UsrArgd );
- exit( e_USAGE );
- }
- else { /* parse callers command-line & print variable settings */
- if ( Prompt ) BSET(flags, pa_PROMPT);
- if ( Ignore ) BSET(flags, pa_IGNORE);
- if ( AnyCase ) BSET(flags, pa_ANYCASE);
- if ( Flags1st ) BSET(flags, pa_FLAGS1ST);
- if ( flags ) (VOID) parsecntl( UsrArgd, pc_PARSEFLAGS, pc_WRITE, flags );
-
- #if ( 0 == 1 )
- /* for TCL only -- send stderr to stdout */
- if ( UsrSh == TCL ) {
- close( fileno(stderr) );
- dup( fileno(stdout) );
- }
- #endif
-
- if ( (rc = parseargs( UsrArgv.array, UsrArgd )) != 0 ) {
- if ( rc > 0 ) {
- rc = e_SYNTAX;
- }
- else { /* (rc < 0) means a system error */
- cleanup();
- if ( errno ) perror( UsrName );
- exit( e_SYSTEM );
- }
- }/*if*/
-
- if ( Unset ) unset_positional_parameters();
-
- print_args( UsrVals, UsrArgd );
- }
-
- cleanup();
- exit( rc );
- } /* main */
-