home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume29 / parseargs / part10 / parseargs.c
Encoding:
C/C++ Source or Header  |  1992-05-19  |  74.0 KB  |  2,296 lines

  1. /*************************************************************************
  2. ** ^FILE: parseargs.c - command line interface to parseargs()
  3. **
  4. **    This file implements the command-line interface to the parseargs
  5. **    library. Under Unix, the user may use parseargs(1) (this program)
  6. **    to parse command-line arguments for shell scripts.  At the present,
  7. **    time, VMS/DCL is not yet supported (nor are MS-DOS Batch-files).
  8. **
  9. ** ^DESCRIPTION:
  10. **    Given a command name, a vector of string-valued arguments such as that
  11. **    passed to a shell script, and a specification string describing the
  12. **    possible arguments, parseargs matches actual arguments to possible
  13. **    arguments, converts values to the desired type, and diagnoses problems
  14. **    such as missing arguments, extra arguments, and argument values that
  15. **    are syntactically incorrect.  Other behavior such as prompting the
  16. **    user for missing arguments and ignoring bad command-line syntax may be
  17. **    specified on the command-line through the use of various options, or
  18. **    through the use of the "PARSECNTL" environment variable.
  19. **
  20. **    Given the command name and the argument specification string,
  21. **       parseargs -U
  22. **    prints a reasonably friendly version of the usage of the calling program
  23. **    on standard diagnostic output. The "verbosity" of the usage message may
  24. **    be controlled through the use of the "USAGECNTL" environment variable.
  25. **
  26. **    Given the command name and the argument specification string,
  27. **       parseargs -M
  28. **    prints a template of the command-syntax on standard output that is
  29. **    suitable for input to nroff or troff using the -man macro package.
  30. **
  31. **    Given no other arguments,
  32. **       parseargs -#
  33. **    prints on standard output, the current version and patchlevel of the
  34. **    running version of parseargs.
  35. **
  36. ** ^SEE_ALSO:
  37. **    argtype(3), parseargs(1), parseargs(3), parsecntl(3),
  38. **    parseargs.pl, parseargs.awk
  39. **    test.sh, test.csh, test.ksh, test.rc, test.awk, test.pl
  40. **
  41. ** ^BUGS:
  42. **    It does not make sense to use any arguments of type argTBool since
  43. **    parseargs currently has no way of knowing what the initial value of
  44. **    the variable is. For this reason, argTBool is not recognized as a
  45. **    valid argument type (even though it is used by parseargs(3)). By the
  46. **    same token, since the user cannot create their own arguments types on
  47. **    the fly from a shell-script, ARGNOVAL is not recognized as a valid
  48. **    argument flag.
  49. **
  50. **    Commas will not be interpreted properly if any field in the argument
  51. **    specification string contains double quotes that are nested inside of
  52. **    double quotes, or single quotes that are nested inside of single quotes.
  53. **
  54. **    Inside the argument specification string, any repeated string of
  55. **    commas that does not appear inside of double or single quotes will
  56. **    be treated as a single comma.
  57. **
  58. **    Text descriptions for argument entries are automatically formatted in
  59. **    usage messages. Any attempt by the user to include tabs and/or newlines
  60. **    in the description will cause it to be formatted improperly.
  61. **
  62. **    Parseargs cannot properly preserve any newlines in shell variables if
  63. **    the eval command is used to read its output (this is a shortcoming of
  64. **    the eval command, not of parseargs). If the user is concerned about
  65. **    this particular case, then the user should redirect the output from
  66. **    parseargs to a temporary file and use the source command in csh or the
  67. **    dot command (`.') in sh and ksh, to interpret the results; otherwise,
  68. **    newlines will be translated into spaces, or characters following a
  69. **    newline may be lost, in any variables that are set by parseargs.
  70. **
  71. ** ^HISTORY:
  72. **    07/18/90     Brad Appleton     <brad@ssd.csd.harris.com>     Created
  73. **
  74. **    02/--/91     Brad Appleton     <brad@ssd.csd.harris.com>
  75. **    - Added awk, perl, and rc to the list of shells to generate output for
  76. **    - added calls to vecFree in cleanup()
  77. **
  78. **    03/31/91     Brad Appleton     <brad@ssd.csd.harris.com>
  79. **    - fixed bug in cleanup for SunOS (added free_vectors())
  80. **    - fixed printing of single quotes for perl scripts
  81. **    - added -C option for case-insensitivity
  82. **    - added hidden -# option to print current version and patchlevel
  83. **
  84. **    27/08/91     Earl Chew     <cechew@bruce.cs.monash.edu.au>
  85. **    - Use ProgNameLen when accessing ProgName
  86. ***^^**********************************************************************/
  87.  
  88. #include <stdio.h>
  89. #include <errno.h>
  90. #include <useful.h>
  91. #include "strfuncs.h"
  92. #include "patchlevel.h"
  93.  
  94. #define PARSEARGS_PRIVATE   /* include private definitions */
  95. #include "parseargs.h"
  96.  
  97.  
  98. /*************************************************************************
  99. ** ^SECTION: RETURN-CODES
  100. **
  101. ** Parseargs may return any of the following status-codes:
  102. */
  103. #define  e_SYSTEM   -1
  104. /*    -- A system error occurred
  105. */
  106. #define  e_SUCCESS   0
  107. /*    -- No errors, success!!
  108. */
  109. #define  e_USAGE     1
  110. /*    -- No errors were encountered. A usage-message, or manual-page-template
  111. **       was explicitly requested (and printed) by the user.
  112. */
  113. #define  e_SYNTAX    2
  114. /*    -- A syntax error was encountered on the command-line. The error may
  115. **       be in the argument(s) intended for parseargs(1) or in the argument(s)
  116. **       for the invoking shell-script.
  117. */
  118. #define  e_NOENV     3
  119. /*    -- The user specified that the argument description was to be found in
  120. **       an environment variable, however the environment variable in question
  121. **       is unset or empty.
  122. */
  123. #define  e_ARGD      4
  124. /*    -- An error was encountered in the string that describes the arguments
  125. **       for the given command.
  126. */
  127. /**^^**********************************************************************/
  128.  
  129.    /* default shell variable values for a boolean argument */
  130. static CONST char  Default_StrTrue[]  = "TRUE";
  131. static CONST char  Default_StrFalse[] = "";
  132.  
  133.       /* define character sets */
  134. static CONST char  WhiteSpace[] = " \t\n\v\r\f\"'";
  135. static CONST char  ArgTableDelims[] = ",";
  136. static CONST char  ArgFlagsDelims[] = "|+ \t\n\v\r\f";
  137.  
  138.    /* macros to improve readability of string tests */
  139. #define strEQ(s1,s2)     !strcmp(s1, s2)
  140. #define strnEQ(s1,s2,n)  !strncmp(s1, s2, n)
  141.  
  142. #define  BUFFER_SIZE  1024       /* start off with 1k buffer & resize it */
  143. #define  ESCAPED_COMMA  '\001'   /* character to replace commas with */
  144.  
  145.       /* determine the beginning and end of a struct */
  146. #define  c_BEGIN_STRUCT  '{'
  147. #define  c_END_STRUCT    '}'
  148.  
  149.       /* determine beginning-of-arg-table string */
  150. #define  s_START_ARGS    "STARTOFARGS"
  151. #define  isSTART_ARGS(s)  strnEQ(s, s_START_ARGS, 5)
  152.  
  153.       /* determine end-of-arg-table string */
  154. #define  s_END_ARGS    "ENDOFARGS"
  155. #define  isEND_ARGS(s)  strnEQ(s, s_END_ARGS, 3)
  156.  
  157.       /* define #of entries per arg-descriptor */
  158. #define  NFIELDS  5
  159.  
  160.  
  161. /**************************************************************************
  162. ** ^SECTION: SHELLS
  163. **    After the command line has been parsed, parseargs will print on
  164. **    standard output, a script to set the shell variables which correspond
  165. **    to arguments that were present on the command-line.  This script may
  166. **    be evaluated by redirecting it to a file and then executing the file,
  167. **    or by directly evaluating the output from parseargs (under most UNIX
  168. **    shells, this could be done using eval).  If any arguments on the
  169. **    command line contained any special characters that needed to be
  170. **    escaped from the shell, these characters will remain intact (not be
  171. **    evaluated by the shell) in the corresponding shell variable.
  172. **
  173. **    The -s shell option may be used to tell parseargs which shell syntax
  174. **    to use. At present, parseargs only recognizes the following shells as
  175. **    valid command-interpreters:
  176. **
  177. **         sh
  178. **         bash
  179. **         ksh
  180. **         csh
  181. **         tcsh
  182. **         itcsh
  183. **         ash
  184. **         zsh
  185. **         rc
  186. **         awk
  187. **         perl
  188. **         tcl
  189. **
  190. **    Awk output is slightly different from that of the other shells in that
  191. **    the actual variable settings are not printed but each line of an
  192. **    associative array is printed (the first field is the array index, the
  193. **    second is the value for that index).  If no shell is specified, then
  194. **    the Bourne shell ("sh") will be assumed.
  195. **
  196. **    If the user wishes to use a value other than "TRUE" for a boolean
  197. **    flag that is true, this may be done using the -T string option.  The
  198. **    same may also be done for a boolean flag that is false using the -F
  199. **    string option.
  200. **
  201. **    Parseargs will only set the values of variables that correspond to
  202. **    arguments that were given on the command line. If a particular
  203. **    argument was not supplied on the command line, then no assignment is
  204. **    made for the corresponding shell variable and it will have the same
  205. **    value that it had before parseargs was invoked. The only exception to
  206. **    this is that if the -u option is specified, then the positional
  207. **    parameters are unset before any shell variable assignments (which may
  208. **    reset the positional parameters) are made.
  209. ***^^*********************************************************************/
  210.  
  211.    /* type used to index into the shell-info array */
  212. typedef unsigned  shellidx_t;
  213.  
  214.    /* possible shell names and corresponding types */
  215. typedef enum {
  216.    SH,    /* ash and sh are equivalent for our purposes */
  217.    BASH,
  218.    CSH,   /* tcsh & itcsh are equivalent to csh for our purposes */
  219.    KSH,
  220.    RC,
  221.    ZSH,
  222.    CLAM,  /* not yet supported */
  223.    AWK,
  224.    PERL,
  225.    TCL
  226. } shell_t;
  227.  
  228.    /* structure for shell-specific info */
  229. typedef struct {
  230.    shell_t  type;    /* type of shell */
  231.    char  *name;      /* name of the shell */
  232.  
  233.    char  *unset;     /* syntax used to unset positional parameters */
  234.  
  235.    char  *sclset;    /* syntax used to set scalars (%s is the scalar name) */
  236.    char  *sclpfx;    /* prefix used for scalars */
  237.    char  *sclsfx;    /* suffix used for scalars */
  238.  
  239.    char  *aryset;    /* syntax used to set arrays (%s is the array name) */
  240.    char  *arypfx;    /* prefix used for arrays */
  241.    char  *arysep;    /* separator used for arrays */
  242.    char  *arysfx;    /* suffix used for arrays */
  243.  
  244.    char  **escapes;  /* an array of strings (the last of which must be NULL).
  245.                      ** For each string - the first character is the character
  246.                      ** to escape and the remainder of the string is the
  247.                      ** escape sequence to use for that character.
  248.                      */
  249. } shell_info;
  250.  
  251.  
  252.    /* Here are the escape-arrays for the various shells
  253.    */
  254. static char * sh_escapes[] = {
  255.    "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\\\\\\", "''\\''",
  256.    CHARNULL
  257. };
  258.  
  259. static char * csh_escapes[] = {
  260.    "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\n\\\n", "\\\\\\", "''\\''", "!\\!",
  261.    CHARNULL
  262. };
  263.  
  264. static char * zsh_escapes[] = {
  265.    "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\\\\\\", "''\\''", "!\\!",
  266.    CHARNULL
  267. };
  268.  
  269. static char * rc_escapes[] = {
  270.    "\b\\\b", "\r\\\r", "\v\\\v", "\f\\\f", "\\\\\\", "'''", CHARNULL
  271. };
  272.  
  273. static char * perl_escapes[] = {
  274.    "\b\\b", "\r\\r", "\v\\v", "\t\\t", "\n\\n", "\f\\f", "\\\\\\", "'\\'",
  275.    CHARNULL
  276. };
  277.  
  278. static char * tcl_escapes[] = {
  279.    "\b\\b", "\r\\r", "\v\\v", "\t\\t", "\n\\n", "\f\\f", "\\\\\\", "\"\\\"",
  280.    "{\\{", "}\\}", "[\\[", "]\\]", "$\\$", 
  281.    CHARNULL
  282. };
  283.  
  284. static char * awk_escapes[] = { "'''", CHARNULL };
  285.  
  286.  
  287.    /* array of shell info records for supported shells */
  288. static CONST shell_info  Shell[] = {
  289.    {
  290.          /* Bourne Shell */
  291.       SH,  "sh",
  292.  
  293.          /* "shift $#" unsets the positional parameters */
  294.       "shift $#;\n",
  295.  
  296.          /* "name='value'" assigns "value" to the variable "name" */
  297.       "%s=",  "'",  "';\n",
  298.  
  299.          /* "ary='elt1 elt2 ...'" assigns an array named "ary" */
  300.       "%s=",  "'",  "%s",  "';\n",
  301.  
  302.       sh_escapes
  303.    },
  304.    {
  305.          /* Bourne-Again shell -- we treat it the same as the Bourne shell.
  306.          ** (this should change when BASH supports arrays)
  307.          */
  308.       BASH,  "bash",
  309.       "shift $#;\n",  
  310.       "%s=",  "'",  "';\n",
  311.       "%s=",  "'",  "%s",  "';\n",
  312.       sh_escapes
  313.    },
  314.    {
  315.          /* Korn Shell */
  316.       KSH,  "ksh",
  317.  
  318.          /* "set -- ;" unsets the positional parameters */
  319.       "set --;\n",  
  320.  
  321.          /* "name='value'" assigns "value" to the variable "name" */
  322.       "%s=",  "'",  "';\n",
  323.  
  324.          /* "set [-+]A ary 'elt1' 'elt2' ..." assigns an array named "ary" */
  325.       "set %cA %s ",  "'",  "'%s'",  "';\n",
  326.  
  327.       sh_escapes
  328.    },
  329.    {
  330.          /* C-Shell */
  331.       CSH,  "csh",
  332.  
  333.          /* "set argv=()" unsets the positional parameters */
  334.       "set argv=();\n",  
  335.  
  336.          /* "set name='value'" assigns "value" to the variable "name" */
  337.       "set %s=",  "'",  "';\n",
  338.  
  339.          /* "set ary=( 'elt1' 'elt2' ... )" assigns an array named "ary" */
  340.       "set %s=",  "( '",  "'%s'",  "' );\n",
  341.  
  342.       csh_escapes
  343.    },
  344.    {
  345.          /* Z-Shell -- this is a lot like the C-Shell except we dont need
  346.          ** the 'set' keyword when assigning variables and arrays
  347.          */
  348.       ZSH,  "zsh",
  349.  
  350.          /* "argv=()" unsets the positional parameters */
  351.       "argv=();\n",  
  352.  
  353.          /* "name='value'" assigns "value" to the variable "name" */
  354.       "%s=",  "'",  "';\n",
  355.  
  356.          /* "ary=( 'elt1' 'elt2' ... )" assigns an array named "ary" */
  357.       "%s=",  "( '",  "'%s'",  "' );\n",
  358.  
  359.       zsh_escapes
  360.    },
  361.    {
  362.          /* rc -- the Plan 9 shell designed by Tom Duff */
  363.       RC,  "rc",
  364.  
  365.          /* "*=()" unsets the positional parameters */
  366.       "*=();\n",  
  367.  
  368.          /* "name='value'" assigns "value" to the variable "name" */
  369.       "%s=",  "'",  "';\n",
  370.  
  371.          /* "ary=( 'elt1' 'elt2' ... )" assigns an array named "ary" */
  372.       "%s=",  "( '",  "'%s'",  "' );\n",
  373.  
  374.       rc_escapes
  375.    },
  376.    {
  377.          /* perl - Larry Wall's Practical Extraction and Report Language */
  378.       PERL,  "perl",
  379.  
  380.          /* "@ARGV = ()" unsets the positional parameters */
  381.       "@ARGV = ();\n",  
  382.  
  383.          /* "$name = 'value' ;" assigns "value" to the variable "name" */
  384.       "$%s = ",  "'",  "';\n",
  385.  
  386.          /* "@ary = ( 'elt1', 'elt2', ... );" assigns an array named "ary" */
  387.       "@%s = ",  "( '",  "', '",  "' );\n",
  388.  
  389.       perl_escapes
  390.    },
  391.    {
  392.          /* TCL - Tool Command Language */
  393.       TCL,  "tcl",
  394.  
  395.          /* "set argv  {}" unsets the positional parameters */
  396.       "set argv {} ;\n",  
  397.  
  398.          /* "set $name {value} ;" assigns "value" to the variable "name" */
  399.       "set %s ",  "\"",  "\" ;\n",
  400.  
  401.          /* "set ary { {elt1} {elt2} ... };" assigns an array named "ary" */
  402.       "set %s ",  "[ list \"",  "\" \"",  "\" ] ;\n",
  403.  
  404.       tcl_escapes
  405.    },
  406.    {
  407.          /* awk -- Aho, Weinberger, & Kernighan's pattern-action language
  408.          **
  409.          ** we treat awk differently then the other shells. This is because
  410.          ** we cant use actual awk syntax (since awk doesnt have the equivalent
  411.          ** of an 'eval' command). Instead, we write out an input stream for
  412.          ** consisting or variable assignments. Records are multi-line, and
  413.          ** separated by a blank line. Fields are separated by a newline. 
  414.          ** The first field is the name of the variable and the remaining
  415.          ** fields (if more than one remains we have an array) of the values
  416.          ** associated with the variable.
  417.          */
  418.       AWK,  "awk",
  419.       "ARGV\n\n",  
  420.       "%s\n",  "",  "\n\n",
  421.       "%s\n",  "",  "%s",  "\n\n",
  422.       awk_escapes
  423.    }
  424. };
  425.  
  426. static size_t NumShells = ( sizeof(Shell) / sizeof(shell_info) );
  427.  
  428.  
  429. /*************************************************************************/
  430.  
  431.    /* define all current arg-vector types */
  432. typedef ARGVEC_T(char *)  strvec_t;
  433. typedef ARGVEC_T(char)    charvec_t;
  434. typedef ARGVEC_T(int)     intvec_t;
  435. typedef ARGVEC_T(short)   shortvec_t;
  436. typedef ARGVEC_T(long)    longvec_t;
  437. typedef ARGVEC_T(float)   floatvec_t;
  438. typedef ARGVEC_T(double)  doublevec_t;
  439. typedef ARGVEC_T(VOID)    genericvec_t;  /* generic vector */
  440.  
  441.       /* union to hold all possibles values of an argument */
  442. typedef union {
  443.    BOOL    Bool_val;
  444.    short   Short_val;
  445.    int     Int_val;
  446.    long    Long_val;
  447.    float   Float_val;
  448.    double  Double_val;
  449.    char    Char_val;
  450.    char   *Str_val;
  451.    strvec_t      Str_vec;
  452.    charvec_t     Char_vec;
  453.    intvec_t      Int_vec;
  454.    shortvec_t    Short_vec;
  455.    longvec_t     Long_vec;
  456.    floatvec_t    Float_vec;
  457.    doublevec_t   Double_vec;
  458.    genericvec_t  Vector;
  459. } storage_t;
  460.  
  461.    /* structure to hold a command-line argument name, value, and fmt-string */
  462. typedef struct {
  463.    CONST char *name;   /* name of shell variable to use */
  464.    storage_t   value;  /* storage for value of argument */
  465. } cmdarg_t;
  466. #define CMDARGNULL (cmdarg_t *)NULL
  467.  
  468. EXTERN  int   eprintf   ARGS((const char *, ...));
  469. EXTERN  VOID  syserr    ARGS((const char *, ...));
  470. EXTERN  VOID  usrerr    ARGS((const char *, ...));
  471. EXTERN  char *getenv    ARGS((const char *));
  472. EXTERN  VOID  manpage   ARGS((const ARGDESC *));
  473. EXTERN  VOID  perror    ARGS((const char *));
  474.  
  475. /*************************************************************************/
  476.  
  477. /*
  478. ** argVers() -- This is the function used to print the version of parseargs
  479. **              on standard output and then exit (regardless of where its
  480. **              corresponding '-#' appears on the command line and regardless
  481. **              of what may have preceded it).
  482. */
  483. /*ARGSUSED*/
  484. #ifdef __ANSI_C__
  485.    static BOOL argVers( register ARGDESC *ad,  register char *vp,  BOOL copyf )
  486. #else
  487.    static BOOL argVers( ad, vp, copyf )
  488.    register ARGDESC *ad;
  489.    register char *vp;
  490.    BOOL copyf;
  491. #endif
  492. {
  493.   printf( "%s\n", _Ident );
  494.   exit( e_USAGE );
  495. }
  496.  
  497.    /*
  498.    ** variables that are set via command-line arguments
  499.    */
  500. static  char   *Cmd_Name;  /* name of this program */
  501.  
  502. static  ARGDESC   *UsrArgd = ARGDESCNULL;       /* users arg-table */
  503. static  cmdarg_t  *UsrVals = CMDARGNULL;        /* variable names & values */
  504. static  int        UsrArgc = 0;                 /* # of arg-table entries */
  505. static  shellidx_t  UsrSh;                      /* shell indicator */
  506. static  BOOL       UseStdin = TRUE;             /* read argd from stdin */
  507.  
  508. static  char   *ShellName  = CHARNULL;  /* name of user's shell */
  509. static  char   *UsrName    = CHARNULL;  /* name of users program */
  510. static  char   *FieldSep   = " ";       /* field-separators for arrays */
  511. static  strvec_t UsrArgv   = ARGVEC_EMPTY(char *);   /* users args */
  512. static  char   *ArgdString = CHARNULL;  /* argd string (with WhiteSpace) */
  513. static  char   *ArgdEnv    = CHARNULL;  /* environment variable for argd */
  514. static  char   *ArgdFname  = CHARNULL;  /* argd input file */
  515. static  BOOL    Unset      = FALSE;     /* ?unset positional parms? */
  516. static  char   *StrTrue    = CHARNULL;  /* string for TRUE values */
  517. static  char   *StrFalse   = CHARNULL;  /* string for FALSE values */
  518. static  char    OptsOnly   = FALSE;     /* parse options only? */
  519. static  char    KwdsOnly   = FALSE;     /* parse keywords only? */
  520. static  BOOL    ModArr     = FALSE;     /* modify array behavior */
  521. static  BOOL    PrUsage    = FALSE;     /* ?just print usage? */
  522. static  BOOL    PrManual   = FALSE;     /* ?just print manual page(s)? */
  523. static  BOOL    Prompt     = FALSE;     /* ?prompt for missing args? */
  524. static  BOOL    Ignore     = FALSE;     /* ?ignore bad syntax and continue? */
  525. static  BOOL    AnyCase    = FALSE;     /* ?case-insensitivity? */
  526. static  BOOL    Flags1st   = FALSE;     /* ?non-positionals first? */
  527.  
  528. /*************************************************************************/
  529.    /* now we are ready to define the command-line */
  530. static
  531. CMD_OBJECT
  532.    Args
  533.  
  534. CMD_NAME
  535.    "parseargs  --  parse command-line arguments in shell scripts"
  536.  
  537. CMD_DESCRIPTION
  538.    "Given a description of the command-line and the command-line arguments, \
  539. parseargs will parse all command-line arguments, convert them to their \
  540. desired type, and print on standard output, a script of all the resulting \
  541. shell assignment statements."
  542.  
  543. CMD_ARGUMENTS
  544.    '#', ARGNOVAL,  argVers,  __ NULL,
  545.    "VERsion : just print program version, dont parse command-line",
  546.  
  547.    'U', ARGOPT,  argBool,  __ &PrUsage,
  548.    "usage : just print program usage, dont parse command-line",
  549.  
  550.    'M', ARGOPT,  argBool,  __ &PrManual,
  551.    "man1 : just print man1 template, dont parse command-line",
  552.  
  553.    'T', ARGOPT,  argStr,  __ &StrTrue,
  554.    "TRUEstr : string to use for TRUE Booleans   (default=\"TRUE\")",
  555.  
  556.    'F', ARGOPT,  argStr,  __ &StrFalse,
  557.    "FALSEstr : string to use for FALSE Booleans  (default=\"\")",
  558.  
  559.    'C', ARGOPT,  argBool,  __ &AnyCase,
  560.    "caseignore : parse options using case-insensitivity",
  561.  
  562.    'A', ARGOPT,  argBool,  __ &ModArr,
  563.    "array : modify the behavior of arrays",
  564.  
  565.    'S', ARGOPT,  argStr,  __ &FieldSep,
  566.    "SEParator : field-separator-string used to delimit array elements \
  567. (default=\" \")",
  568.  
  569.    'a', ARGOPT,  argStr,  __ &ArgdString,
  570.    "ARGSpec : argument specification string",
  571.  
  572.    'e', ARGOPT,  argStr,  __ &ArgdEnv,
  573.    "ENVarname : environment variable containing arg-spec",
  574.  
  575.    'f', ARGOPT,  argStr,  __ &ArgdFname,
  576.    "FILEname : read the arg-spec from <filename> (default=stdin)",
  577.  
  578.    'l', ARGOPT,  argBool,  __ &KwdsOnly,
  579.    "Long-OPTionS : long-options only - do not parse options",
  580.  
  581.    'o', ARGOPT,  argBool,  __ &OptsOnly,
  582.    "OPTionS : options only - do not parse long-options",
  583.  
  584.    's', ARGOPT,  argStr,  __ &ShellName,
  585.    "SHell : use <shell> command syntax        (default=\"sh\")",
  586.  
  587.    'u', ARGOPT,  argBool,  __ &Unset,
  588.    "unset : unset positional parameters before parsing",
  589.  
  590.    'p', ARGOPT,  argBool,  __ &Prompt,
  591.    "prompt : prompt the user for missing required arguments",
  592.  
  593.    'i', ARGOPT,  argBool,  __ &Ignore,
  594.    "ignore : ignore bad command-line syntax and continue processing \
  595. (instead of aborting)",
  596.  
  597.    '1', ARGOPT,  argBool,  __ &Flags1st,
  598.    "1st : force non-positional arguments to precede all positional arguments",
  599.  
  600.    '-', ARGOPT,  argDummy, __ NULL,
  601.    "+ : end of options - all remaining arguments are interpreted as \
  602. positional parameters (even if one begins with '-' or '+')",
  603.  
  604.    ' ', ARGREQ,  argStr, __ &UsrName,
  605.    "name : name of calling program",
  606.  
  607.    ' ', ARGOPT|ARGVEC,  argStr,  __ &UsrArgv,
  608.    "arguments : arguments to calling program",
  609.  
  610.    END_ARGUMENTS
  611.  
  612. CMD_END
  613.  
  614.  
  615. /***************************************************************************
  616. ** ^FUNCTION: free_vectors - deallocate all vectors in an ARGDESC
  617. **
  618. ** ^SYNOPSIS:
  619. */
  620. #ifndef __ANSI_C__
  621.    static  VOID free_vectors( argd )
  622. /*
  623. ** ^PARAMETERS:
  624. */
  625.    ARGDESC argd[];
  626. #endif  /* !__ANSI_C__ */
  627.  
  628. /* ^DESCRIPTION:
  629. **    Free_vectors will deallocate the storage used for each arg-vector
  630. **    referenced by argd.
  631. **
  632. ** ^REQUIREMENTS:
  633. **    None.
  634. **
  635. ** ^SIDE-EFFECTS:
  636. **    Storage associated with all dynamically allocated arg-vectors
  637. **    is released and set to NULL.
  638. **
  639. ** ^RETURN-VALUE:
  640. **    None.
  641. **
  642. ** ^ALGORITHM:
  643. **    Trivial.
  644. ***^^**********************************************************************/
  645. #ifdef __ANSI_C__
  646.    static void free_vectors( ARGDESC argd[] )
  647. #endif
  648. {
  649.    register ARGDESC *ad;
  650.    register storage_t val;
  651.  
  652.    if ( !argd  ||  !CMD_isINIT(argd) )  return;
  653.  
  654.    for ( ad = ARG_FIRST(argd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  655.       if ( ! BTEST(arg_flags(ad), ARGVEC) )  continue;
  656.  
  657.       val = *((storage_t *) arg_valp(ad));
  658.  
  659.       if      ( arg_type(ad) == argStr )     vecFree( val.Str_vec, char * );
  660.       else if ( arg_type(ad) == argChar )    vecFree( val.Char_vec, char );
  661.       else if ( arg_type(ad) == argInt )     vecFree( val.Int_vec, int );
  662.       else if ( arg_type(ad) == argShort )   vecFree( val.Short_vec, short );
  663.       else if ( arg_type(ad) == argLong )    vecFree( val.Long_vec, long );
  664.       else if ( arg_type(ad) == argFloat )   vecFree( val.Float_vec, float );
  665.       else if ( arg_type(ad) == argDouble )  vecFree( val.Double_vec, double );
  666.    }
  667.  
  668. }
  669.  
  670.  
  671. /***************************************************************************
  672. ** ^FUNCTION: cleanup - deallocate all global storage
  673. **
  674. ** ^SYNOPSIS:
  675. */
  676. #ifndef __ANSI_C__
  677.    static  VOID cleanup()
  678. #endif  /* !__ANSI_C__ */
  679. /*
  680. ** ^PARAMETERS:
  681. **    None.
  682. **
  683. ** ^DESCRIPTION:
  684. **    Cleanup is used to deallocate any global storage. It is called
  685. **    before exiting.
  686. **
  687. ** ^REQUIREMENTS:
  688. **    None.
  689. **
  690. ** ^SIDE-EFFECTS:
  691. **    Storage associated with all dynamically allocated global-variables
  692. **    is released and set to NULL.
  693. **
  694. ** ^RETURN-VALUE:
  695. **    None.
  696. **
  697. ** ^ALGORITHM:
  698. **    Trivial.
  699. ***^^**********************************************************************/
  700. #ifdef __ANSI_C__
  701.    static void cleanup( void )
  702. #endif
  703. {
  704.    /* free up tables */
  705.    vecFree( UsrArgv, char * );
  706.    if ( UsrArgd ) {
  707.       free_vectors( UsrArgd );
  708.       free( UsrArgd );
  709.       UsrArgd = ARGDESCNULL;
  710.    }
  711.    if ( UsrVals ) {
  712.       free( UsrVals );
  713.       UsrVals = CMDARGNULL;
  714.    }
  715.    if ( ArgdFname   &&   !ArgdEnv ) {
  716.       free( ArgdString );
  717.       ArgdString = CHARNULL;
  718.    }
  719. }
  720.  
  721.  
  722. /***************************************************************************
  723. ** ^FUNCTION: ckalloc - allocate space, check for success
  724. **
  725. ** ^SYNOPSIS:
  726. */
  727. #ifndef __ANSI_C__
  728.    static ARBPTR ckalloc( size )
  729. /*
  730. ** ^PARAMETERS:
  731. */
  732.    size_t  size;
  733. /*    -- the number of bytes to allocate
  734. */
  735. #endif  /* !__ANSI_C__ */
  736.  
  737. /* ^DESCRIPTION:
  738. **    Ckalloc will use malloc to attempt to fill the given request 
  739. **    for memory. If The request cannot be met than a message is
  740. **    printed and execution is terminated.
  741. **
  742. ** ^REQUIREMENTS:
  743. **    size should be > 0
  744. **
  745. ** ^SIDE-EFFECTS:
  746. **    Memory is allocated that should later be deallocated using free().
  747. **
  748. ** ^RETURN-VALUE:
  749. **    The address of the allocated region.
  750. **
  751. ** ^ALGORITHM:
  752. **    - Allocate space, check for success
  753. ***^^**********************************************************************/
  754. #ifdef __ANSI_C__
  755.    static ARBPTR ckalloc( size_t size )
  756. #endif
  757. {
  758.    ARBPTR  ptr;
  759.  
  760.    ptr = (ARBPTR)malloc( size );
  761.    if ( !ptr ) {
  762.       eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name );
  763.       cleanup();
  764.       if ( errno )  perror( Cmd_Name );
  765.       exit( e_SYSTEM );
  766.    }
  767.  
  768.    return  ptr;
  769. }
  770.  
  771.  
  772. /***************************************************************************
  773. ** ^FUNCTION: ckrealloc - reallocate space, check for success
  774. **
  775. ** ^SYNOPSIS:
  776. */
  777. #ifndef __ANSI_C__
  778.    static ARBPTR ckrealloc( ptr, size )
  779. /*
  780. ** ^PARAMETERS:
  781. */
  782.    ARBPTR  ptr;
  783. /*    -- address of the region to be expanded/shrunk
  784. */
  785.    size_t  size;
  786. /*    -- the number of bytes to allocate
  787. */
  788. #endif  /* !__ANSI_C__ */
  789.  
  790. /* ^DESCRIPTION:
  791. **    Ckrealloc will use realloc to attempt to fill the given request 
  792. **    for memory. If The request cannot be met than a message is
  793. **    printed and execution is terminated.
  794. **
  795. ** ^REQUIREMENTS:
  796. **    size should be > 0
  797. **
  798. ** ^SIDE-EFFECTS:
  799. **    Memory is allocated that should later be deallocated using free().
  800. **
  801. ** ^RETURN-VALUE:
  802. **    The address of the (re)allocated region (which may have been moved).
  803. **
  804. ** ^ALGORITHM:
  805. **    - Reallocate space, check for success
  806. ***^^**********************************************************************/
  807. #ifdef __ANSI_C__
  808.    static ARBPTR ckrealloc( ARBPTR ptr, size_t size )
  809. #endif
  810. {
  811.    ptr = realloc( ptr, (unsigned int)size );
  812.    if ( !ptr ) {
  813.       eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name );
  814.       cleanup();
  815.       if ( errno )  perror( Cmd_Name );
  816.       exit( e_SYSTEM );
  817.    }
  818.  
  819.    return  ptr;
  820. }
  821.  
  822.  
  823. /***************************************************************************
  824. ** ^FUNCTION: escape_char - (re)map a character
  825. **
  826. ** ^SYNOPSIS:
  827. */
  828. #ifndef __ANSI_C__
  829.    static VOID escape_char( str, ch, esc )
  830. /*
  831. ** ^PARAMETERS:
  832. */
  833.    char *str;
  834. /*    -- the string to be translated
  835. */
  836.    int ch;
  837. /*    --  the character to be replaced/translated
  838. */
  839.    int esc;
  840. /*    -- the replacement character to use
  841. */
  842. #endif  /* !__ANSI_C__ */
  843.  
  844. /* ^DESCRIPTION:
  845. **    Escape_char will escape all occurences of a character by replacing
  846. **    it with <esc> if the character appears in double or single quotes.
  847. **
  848. ** ^REQUIREMENTS:
  849. **    Both <ch> and <esc> should be non-zero.
  850. **    <str> should be non-null and non-empty.
  851. **
  852. ** ^SIDE-EFFECTS:
  853. **    Each occurrence in <str> of <ch> within single or double quotes is
  854. **    replaced with <esc>.
  855. **
  856. ** ^RETURN-VALUE:
  857. **    None.
  858. **
  859. ** ^ALGORITHM:
  860. **    Trivial.
  861. ***^^**********************************************************************/
  862. #ifdef __ANSI_C__
  863.    static void escape_char( char *str, int ch, int esc )
  864. #endif
  865. {
  866.    int   squoted = 0, dquoted = 0;
  867.  
  868.    for ( ; *str ; str++ ) {
  869.       if ( *str == '\''  &&  !dquoted )
  870.          squoted = ~squoted;
  871.       else if ( *str == '"'  &&  !squoted )
  872.          dquoted = ~dquoted;
  873.       else if ( (squoted || dquoted)  &&  *str == ch )
  874.          *str = esc;
  875.    }
  876. }
  877.  
  878.  
  879. /***************************************************************************
  880. ** ^FUNCTION: restore_char - restore any chars escaped by escape_char()
  881. **
  882. ** ^SYNOPSIS:
  883. */
  884. #ifndef __ANSI_C__
  885.    static VOID restore_char( str, ch, esc )
  886. /*
  887. ** ^PARAMETERS:
  888. */
  889.    char *str;
  890. /*    -- the string to be translated
  891. */
  892.    int ch;
  893. /*    --  the character to be restored
  894. */
  895.    int esc;
  896. /*    -- the replacement character to use to escape the above character.
  897. */
  898. #endif  /* !__ANSI_C__ */
  899.  
  900. /* ^DESCRIPTION:
  901. **    Restore_char will attempt to undo the results of a previous call
  902. **    to escape_char by replacing each occurence of <esc> in <str> with <ch>.
  903. **
  904. ** ^REQUIREMENTS:
  905. **    <str> should be the victim of a previous escape_char(str, ch, esc) call.
  906. **    Furthermore, <esc> should be a character that occurs only as a result
  907. **    of this call (it should be VERY uncommon).
  908. **
  909. **    It should be noted that escape_char() only replaces characters in quotes
  910. **    whereas this routine replaces all occurrences.
  911. **
  912. ** ^SIDE-EFFECTS:
  913. **    Each occurrence of <esc> in <str> is replaced with <ch>.
  914. **
  915. ** ^RETURN-VALUE:
  916. **    None.
  917. **
  918. ** ^ALGORITHM:
  919. **    Trivial.
  920. ***^^**********************************************************************/
  921. #ifdef __ANSI_C__
  922.    void restore_char( char *str, int ch, int esc )
  923. #endif
  924. {
  925.    for ( ; *str ; str++ )
  926.       if ( *str == esc )   *str = ch;
  927. }
  928.  
  929.  
  930. /***************************************************************************
  931. ** ^FUNCTION: get_arg_type - return function corresponding to given string
  932. **
  933. ** ^SYNOPSIS:
  934. */
  935. #ifndef __ANSI_C__
  936.    static argTypePtr_t  get_arg_type( type_str )
  937. /*
  938. ** ^PARAMETERS:
  939. */
  940.    char *type_str;
  941. /*    -- string corresponding to the name of an existing argXxxx type function.
  942. */
  943. #endif  /* !__ANSI_C__ */
  944.  
  945. /* ^DESCRIPTION:
  946. **    Get_arg_type will attempt to match <type_name> against the name of all
  947. **    known argXxxx argumnent translation routines and routine the address of
  948. **    the corresponding function. If no match is found, then an error message
  949. **    is printed and execution is terminated.
  950. **
  951. ** ^REQUIREMENTS:
  952. **    type_str should be non-NULL and non-empty
  953. **
  954. ** ^SIDE-EFFECTS:
  955. **    None.
  956. **
  957. ** ^RETURN-VALUE:
  958. **    Address of the corresponding function
  959. **
  960. ** ^ALGORITHM:
  961. **    Trivial.
  962. ***^^**********************************************************************/
  963. #ifdef __ANSI_C__
  964.    static  argTypePtr_t  get_arg_type( const char *type_str )
  965. #endif
  966. {
  967.    register  CONST char *str = type_str;
  968.  
  969.    /* translate all listXxx into argXxx */
  970.    if ( strnEQ( str, "list", 4 ) )
  971.       str += 4;
  972.  
  973.    if ( strnEQ( str, "arg", 3 ) )
  974.       str += 3;
  975.  
  976.    if      ( strEQ( str, "Usage" ) )
  977.       return   argUsage;
  978.    else if ( strEQ( str, "Dummy" ) )
  979.       return   argDummy;
  980.    else if ( strEQ( str, "Bool" ) )
  981.       return   argBool;
  982.    else if ( strEQ( str, "SBool" ) )
  983.       return   argSBool;
  984.    else if ( strEQ( str, "UBool" ) )
  985.       return   argUBool;
  986.    else if ( strEQ( str, "Int" ) )
  987.       return   argInt;
  988.    else if ( strEQ( str, "Short" ) )
  989.       return   argShort;
  990.    else if ( strEQ( str, "Long" ) )
  991.       return   argLong;
  992.    else if ( strEQ( str, "Float" ) )
  993.       return   argFloat;
  994.    else if ( strEQ( str, "Double" ) )
  995.       return   argDouble;
  996.    else if ( strEQ( str, "Char" ) )
  997.       return   argChar;
  998.    else if ( strEQ( str, "Str" ) )
  999.       return   argStr;
  1000.    else {
  1001.       eprintf( "%s: Fatal Error: invalid argument type '%s'\n",
  1002.                Cmd_Name, type_str );
  1003.       cleanup();
  1004.       exit( e_ARGD );
  1005.    }
  1006. }
  1007.  
  1008.  
  1009. /***************************************************************************
  1010. ** ^FUNCTION: get_arg_flag - return BITMASK corresponding to string
  1011. **
  1012. ** ^SYNOPSIS:
  1013. */
  1014. #ifndef __ANSI_C__
  1015.    static argMask_t get_arg_flag( flag_str )
  1016. /*
  1017. ** ^PARAMETERS:
  1018. */
  1019.    char flag_str[];
  1020. /*    -- name of an ARGXXXXX argument-flag
  1021. */
  1022. #endif  /* !__ANSI_C__ */
  1023.  
  1024. /* ^DESCRIPTION:
  1025. **    Get_arg_flag will attempt to match the given string against the name of
  1026. **    all valid argument-flags and return its associated bitmask.  If no match
  1027. **    is found, then an error message is printed and execution is terminated.
  1028. **
  1029. ** ^REQUIREMENTS:
  1030. **    flag_str should be non-NULL and non-empty
  1031. **
  1032. ** ^SIDE-EFFECTS:
  1033. **    None.
  1034. **
  1035. ** ^RETURN-VALUE:
  1036. **    The bitmask corresponding to named ARGXXXX flag.
  1037. **
  1038. ** ^ALGORITHM:
  1039. **    Trivial.
  1040. ***^^**********************************************************************/
  1041. #ifdef __ANSI_C__
  1042.    static argMask_t get_arg_flag( const char flag_str[] )
  1043. #endif
  1044. {
  1045.    if ( strnEQ( flag_str, "ARG", 3 ) )   {
  1046.       if      ( strEQ( flag_str+3, "OPT" ) )     return   ARGOPT;
  1047.       else if ( strEQ( flag_str+3, "REQ" ) )     return   ARGREQ;
  1048.       else if ( strEQ( flag_str+3, "POS" ) )     return   ARGPOS;
  1049.       else if ( strEQ( flag_str+3, "VALREQ" ) )  return   ARGVALREQ;
  1050.       else if ( strEQ( flag_str+3, "VALOPT" ) )  return   ARGVALOPT;
  1051.       else if ( strEQ( flag_str+3, "HIDDEN" ) )  return   ARGHIDDEN;
  1052.       else if ( strEQ( flag_str+3, "LIST" ) )    return   ARGVEC;
  1053.       else if ( strEQ( flag_str+3, "VEC" ) )     return   ARGVEC;
  1054.       else {
  1055.          eprintf( "%s: Fatal Error: invalid argument flag '%s'\n",
  1056.                   Cmd_Name, flag_str );
  1057.          cleanup();
  1058.          exit( e_ARGD );
  1059.       }
  1060.    }
  1061.    else {
  1062.       eprintf( "%s: Fatal Error: invalid argument flag '%s'\n",
  1063.                Cmd_Name, flag_str );
  1064.       cleanup();
  1065.       exit( e_ARGD );
  1066.    }
  1067. }
  1068.  
  1069. /***************************************************************************
  1070. ** ^FUNCTION: get_argtable_string - read in the argument-table
  1071. **
  1072. ** ^SYNOPSIS:
  1073. */
  1074. #ifndef __ANSI_C__
  1075.    static char *get_argtable_string()
  1076. #endif
  1077. /*
  1078. ** ^PARAMETERS:
  1079. **    None.
  1080. **
  1081. ** ^DESCRIPTION:
  1082. **    Get_argtable_string will read (from standard input if UseStdin is set)
  1083. **    the entire argument descriptor table into a string and return its address.
  1084. **
  1085. **    Execution is terminated if there is an error reading STDIN or if the
  1086. **    string is too big to fit into memory.
  1087. **
  1088. ** ^REQUIREMENTS:
  1089. **    Standard input should be open for reading and be non-interactive.
  1090. **
  1091. ** ^SIDE-EFFECTS:
  1092. **    Memory is allocated that should later be deallocated using free.
  1093. **
  1094. ** ^RETURN-VALUE:
  1095. **    NULL if STDIN is connected to a terminal (after all,
  1096. **    this program is for Non-interactive input)
  1097. **
  1098. ** ^ALGORITHM:
  1099. **    - start off with a 1k buffer
  1100. **    - open the file (if necessary)
  1101. **    - while (not eof)
  1102. **      - read 1k bytes (or whatever is left).
  1103. **      - increase the buffer size by 1k bytes.
  1104. **      end-while
  1105. **    - shrink the buffer down to the number of bytes used.
  1106. **    - close the file (if we had to open it).
  1107. **    - return the buffer address
  1108. ***^^**********************************************************************/
  1109. #ifdef __ANSI_C__
  1110.    static char *get_argtable_string( void )
  1111. #endif
  1112. {
  1113.    int  isatty  ARGS((int));
  1114. #if (!defined(__ANSI_C__)  &&  !defined(vms))
  1115.    int  fread();
  1116. #endif
  1117.    FILE *fp;
  1118.    char *buf;
  1119.    register size_t  nchars = 0;   /* # bytes read */
  1120.    register size_t  bufsiz = 0;   /* actual buffer-size needed */
  1121.  
  1122.    /* open file if necessary */
  1123.    if ( UseStdin )  {
  1124.       if ( isatty(STDIN) ) {
  1125.             /* we wont read the arg-table from a terminal */
  1126.          eprintf( "\
  1127. %s: Fatal Error:\n\
  1128. \tcannot read arg-descriptor table from stdin\n\
  1129. \tif stdin is connected to a terminal!\n",
  1130.                   Cmd_Name );
  1131.          cleanup();
  1132.          exit( e_ARGD );
  1133.       }
  1134.       errno = 0;   /* reset errno if isatty() was not a terminal */
  1135.       fp = stdin;
  1136.    }
  1137.    else {
  1138.       if ( (fp = fopen( ArgdFname, "r")) == FILENULL )   {
  1139.          eprintf( "%s: Fatal error: Unable to open %s for reading\n",
  1140.                   Cmd_Name, ArgdFname );
  1141.          cleanup();
  1142.          if ( errno )  perror( Cmd_Name );
  1143.          exit( e_SYSTEM );
  1144.       }
  1145.    }
  1146.  
  1147.       /* get initial block for buffer */
  1148.    buf = (char *)ckalloc( BUFFER_SIZE * sizeof(char) );
  1149.  
  1150.    /*
  1151.    ** Loop reading characters into buffer and resizing as needed
  1152.    */
  1153.    do {
  1154.          /* read fildes into the buffer */
  1155.       nchars = (size_t) fread( &(buf[bufsiz]), sizeof(char), BUFFER_SIZE, fp );
  1156.       if ( ferror(fp) )   {
  1157.          eprintf( "\
  1158. %s: Fatal Error:\n\
  1159. \tBad return from fread() while reading argument descriptor table\n",
  1160.                   Cmd_Name );
  1161.          free(buf);
  1162.          cleanup();
  1163.          if ( errno )  perror( "" );
  1164.          exit( e_SYSTEM );
  1165.       }
  1166.       errno = 0;  /* errno is undefined after a succesful fread() */
  1167.       bufsiz += nchars;
  1168.  
  1169.          /* see if we need to grow the buffer */
  1170.       if ( nchars == BUFFER_SIZE )
  1171.          buf = (char *)ckrealloc( buf, (bufsiz + BUFFER_SIZE) * sizeof(char) );
  1172.    } while ( nchars == BUFFER_SIZE );
  1173.  
  1174.       /* shrink the buffer down to the exact size used */
  1175.    buf = (char *)ckrealloc( buf, (bufsiz + 1) * sizeof(char) );
  1176.  
  1177.       /* close file if necessary */
  1178.    if ( !UseStdin )  (VOID) fclose( fp );
  1179.  
  1180.    return   buf;
  1181. }
  1182.  
  1183.  
  1184. /***************************************************************************
  1185. ** ^FUNCTION: get_shell_index - return shell corresponding to given string
  1186. **
  1187. ** ^SYNOPSIS:
  1188. */
  1189. #ifndef __ANSI_C__
  1190.    static shell_t get_shell_index( sh_str )
  1191. /*
  1192. ** ^PARAMETERS:
  1193. */
  1194.    char *sh_str;
  1195. /*    -- string corresponding tp the basename of a shell/command-interpreter
  1196. */
  1197. #endif  /* !__ANSI_C__ */
  1198.  
  1199. /* ^DESCRIPTION:
  1200. **    Get_shell_index will return the shell-type for the named shell. If
  1201. **    No corresponding shell is known, then an error message is printed
  1202. **    and execution is terminated.
  1203. **
  1204. ** ^REQUIREMENTS:
  1205. **    sh_str should be non-NULL and non-empty.
  1206. **
  1207. ** ^SIDE-EFFECTS:
  1208. **    None.
  1209. **
  1210. ** ^RETURN-VALUE:
  1211. **    The corresponding shell-type.
  1212. **
  1213. ** ^ALGORITHM:
  1214. **    Trivial.
  1215. ***^^**********************************************************************/
  1216. #ifdef __ANSI_C__
  1217.    static shellidx_t get_shell_index ( const char *sh_str )
  1218. #endif
  1219. {
  1220.    int  i;
  1221.    register CONST char *sh = sh_str;
  1222.  
  1223.       /* special case to recognize ash, tcsh & itcsh */
  1224.    if ( strEQ( sh, "ash" ) )   ++sh;
  1225.    else if ( strEQ( sh, "tcsh" ) )   ++sh;
  1226.    else if ( strEQ( sh, "itcsh" ) )  sh += 2;
  1227.  
  1228.    for ( i = 0 ; i < NumShells ; i++ ) {
  1229.       if ( strEQ( sh, Shell[i].name ) )   return  (shellidx_t) i;
  1230.    }
  1231.  
  1232.    usrerr( "Unknown shell \"%s\"", sh_str );
  1233.    eprintf( "\tKnown shells are listed below:\n" );
  1234.    for ( i = 0 ; i < NumShells ; i++ ) {
  1235.       if ( strEQ( "csh", Shell[i].name ) ) {
  1236.          eprintf( "\t\tcsh/tcsh/itcsh\n" );
  1237.       }
  1238.       else {
  1239.          eprintf( "\t\t%s\n", Shell[i].name );
  1240.       }
  1241.    }
  1242.  
  1243.    cleanup();
  1244.    exit( e_SYNTAX );
  1245. }
  1246.  
  1247.  
  1248. /***************************************************************************
  1249. ** ^FUNCTION: build_tables - build the Argument and Value tables
  1250. **
  1251. ** ^SYNOPSIS:
  1252. */
  1253. #ifndef __ANSI_C__
  1254.    static int build_tables( argd_str )
  1255. /*
  1256. ** ^PARAMETERS:
  1257. */
  1258.    char argd_str[];
  1259. /*    -- the comma-separated table of argument descriptions
  1260. */
  1261. #endif  /* !__ANSI_C__ */
  1262.  
  1263. /* ^DESCRIPTION:
  1264. **    Build_tables will read the contents of the argument-descriptor-table
  1265. **    string and build the corresponding Argument and Value tables to be
  1266. **    used by parseargs(3).
  1267. **
  1268. ** ^REQUIREMENTS:
  1269. **    argd_str should be non-NULL and non-empty
  1270. **
  1271. ** ^SIDE-EFFECTS:
  1272. **    The global variables UsrVals and UsrArgd are allocated and initialized
  1273. **
  1274. ** ^RETURN-VALUE:
  1275. **    The number of argument entries interpreted from the given string.
  1276. **
  1277. ** ^ALGORITHM:
  1278. **    - split argd_str into a vector of tokens
  1279. **    - make sure the first and last tokens are syntactically correct
  1280. **    - make sure that the number of tokens is a multiple of 5 (the number
  1281. **      of fields in an argument descriptor)
  1282. **    - num-args = num-tokens / 5
  1283. **    - allocate space for UsrVals and UsrArgd
  1284. **    - i = 0
  1285. **    - for every 5 tokens
  1286. **      - UsrArgd[i].ad_name = token#1
  1287. **      - UsrArgd[i].ad_flags = 0
  1288. **      - split token#2 into a subvector of '|' separated fields
  1289. **      - for each '|' separated token
  1290. **        - UsrArgd[i].ad_flags |= bitmask( subfield )
  1291. **        end-for
  1292. **      - UsrArgd[i].ad_type = argtype( token#3 )
  1293. **      - UsrVals[i].name = token#4
  1294. **      - UsrArgd[i].ad_valp = &(UsrVals[i].value)
  1295. **      - UsrArgd[i].ad_prompt = token#5
  1296. **      - increment i by one
  1297. **    end-for
  1298. **    - Initialize first and last entries in UsrArgd
  1299. **    - return  num-args
  1300. ***^^**********************************************************************/
  1301. #ifdef __ANSI_C__
  1302.    static int build_tables( char argd_str[] )
  1303. #endif
  1304. {
  1305.    char **token_vec, **flags_vec, *type_name;
  1306.    int   i = 0, j = 0, idx, token_num = 0, flags_num = 0;
  1307.    int   argc = 0, ad_idx;
  1308.    BOOL  start_string_used = FALSE, is_braces = FALSE;
  1309.  
  1310.       /* what about NULL or empty-string */
  1311.    if ( !argd_str  ||  !*argd_str )  return  0;
  1312.  
  1313.       /* escape all commas inside of single or double quotes */
  1314.    escape_char( argd_str, ',', ESCAPED_COMMA );
  1315.  
  1316.       /* parse Argument Table String */
  1317.    token_num = strsplit( &token_vec, argd_str, ArgTableDelims );
  1318.    if ( token_num )  (VOID) strtrim( token_vec[ token_num - 1 ], WhiteSpace );
  1319.  
  1320.       /* see if we need to build the tables at all */
  1321.    if ( token_num == 0 || isEND_ARGS(*token_vec) ) {
  1322.       free( token_vec );
  1323.       return   0;    /* nothing to parse */
  1324.    }
  1325.  
  1326.       /* make sure table is properly terminated */
  1327.    if ( !isEND_ARGS( token_vec[ --token_num ] ) )  {
  1328.       restore_char( token_vec[ token_num ], ',', ESCAPED_COMMA );
  1329.       eprintf( "\
  1330. %s: Fatal Error:\n\
  1331. \tArgument descriptor table is not terminated with the string:\n\
  1332. \t\t\"%s\"\n\
  1333. \tLast entry in table is: \"%s\"\n",
  1334.                Cmd_Name, s_END_ARGS, token_vec[ token_num ] );
  1335.       free( token_vec );
  1336.       cleanup();
  1337.       exit( e_ARGD );
  1338.    }
  1339.  
  1340.       /* check for optional start-string */
  1341.    (VOID) strtrim( *token_vec, WhiteSpace );
  1342.    if ( isSTART_ARGS(*token_vec) ) {
  1343.       start_string_used = TRUE;
  1344.       --token_num;
  1345.       ++token_vec;
  1346.    }
  1347.  
  1348.       /* make sure table has proper number of arguments */
  1349.    if ( (token_num % NFIELDS) != 0 ) {
  1350.       eprintf( "\
  1351. %s: Fatal Error:\n\
  1352. \tArgument descriptor table has an invalid number of arguments\n\
  1353. \tThe number of comma-separated arguments MUST be a multiple of %d\n\
  1354. \t(not including terminating \"%s\")\n",
  1355.                Cmd_Name, NFIELDS, s_END_ARGS );
  1356.       free( (start_string_used) ? (token_vec - 1) : token_vec );
  1357.       cleanup();
  1358.       exit( e_ARGD );
  1359.    }
  1360.  
  1361.       /* determine number of arg-descriptors and allocate arg-tables */
  1362.    argc = token_num / NFIELDS;
  1363.    UsrArgd = (ARGDESC *) ckalloc( (argc + 2) * sizeof(ARGDESC) );
  1364.    UsrVals = (cmdarg_t *) ckalloc( argc * sizeof(cmdarg_t) );
  1365.  
  1366.       /* start filling in the tables */
  1367.    i = 0;
  1368.    while ( i < token_num )  {
  1369.       restore_char( token_vec[i], ',', ESCAPED_COMMA );
  1370.       (VOID) strtrim( token_vec[i], WhiteSpace );
  1371.       idx = (i / NFIELDS); /* save index into UsrVals table */
  1372.       ad_idx = (idx + 1);   /* save index into UsrArgd table */
  1373.       is_braces = FALSE;
  1374.  
  1375.          /* remove first curly-brace if its present (this has the drawback
  1376.          ** of disallowing a left curly-brace from being an option character).
  1377.          */
  1378.       if ( token_vec[i][0] == c_BEGIN_STRUCT ) {
  1379.          token_vec[i][0] = ' ';
  1380.          (VOID) strltrim( token_vec[i], WhiteSpace );
  1381.          is_braces = TRUE;
  1382.       }
  1383.  
  1384.          /* get argument name */
  1385.       UsrArgd[ ad_idx ].ad_name = *(token_vec[i++]);
  1386.       if ( !UsrArgd[ ad_idx ].ad_name )  UsrArgd[ ad_idx ].ad_name = ' ';
  1387.  
  1388.          /*
  1389.          ** get argument flags, flags may be ORed together so I
  1390.          ** need to parse the flags for each individual flag used
  1391.          */
  1392.       UsrArgd[ ad_idx ].ad_flags = (argMask_t) 0;   /* initialize */
  1393.       flags_num = strsplit( &flags_vec, token_vec[i++] , ArgFlagsDelims );
  1394.       for ( j = 0 ; j < flags_num ; j++ ) {
  1395.             (VOID) strtrim( flags_vec[j], WhiteSpace );
  1396.             UsrArgd[ ad_idx ].ad_flags |= get_arg_flag( flags_vec[j] );
  1397.       }
  1398.       free( flags_vec );
  1399.  
  1400.          /* get argument type and name for Value table */
  1401.       type_name = strtrim( token_vec[i++], WhiteSpace );
  1402.       restore_char( token_vec[i], ',', ESCAPED_COMMA );
  1403.       UsrVals[ idx ].name = strtrim( token_vec[i++], WhiteSpace );
  1404.  
  1405.          /* remove any leading "__" from the name */
  1406.       if ( strnEQ("__", UsrVals[ idx ].name, 2) ) {
  1407.          (VOID) strltrim( (char *)UsrVals[idx].name, "_ \t\n\r\v\f" );
  1408.       }
  1409.  
  1410.          /* remove any leading '&', '$', and '@' from the name */
  1411.       if ( strchr("&$@", UsrVals[ idx ].name[0]) ) {
  1412.          (VOID) strltrim( (char *)UsrVals[idx].name, "&$@ \t\n\r\v\f" );
  1413.       }
  1414.  
  1415.          /* get type and value pointer for Arg table */
  1416.       UsrArgd[ ad_idx ].ad_type = get_arg_type( type_name );
  1417.       UsrArgd[ ad_idx ].ad_valp = __ &(UsrVals[ idx ].value);
  1418.  
  1419.          /* if we have a vector we need to initialize it */
  1420.       if ( ARG_isVEC((UsrArgd + ad_idx)) )  {
  1421.          UsrVals[ idx ].value.Vector.count = 0;
  1422.          UsrVals[ idx ].value.Vector.array = (VOID *)NULL;
  1423.       }
  1424.  
  1425.          /* get argument prompt/description */
  1426.       restore_char( token_vec[i], ',', ESCAPED_COMMA );
  1427.       UsrArgd[ ad_idx ].ad_prompt = strtrim( token_vec[i++], WhiteSpace );
  1428.  
  1429.          /* if in curly-braces, remove the trailing brace */
  1430.       if ( is_braces ) {
  1431.         int  last = strlen( UsrArgd[ad_idx].ad_prompt ) - 1;
  1432.         if ( UsrArgd[ ad_idx ].ad_prompt[ last ] == c_END_STRUCT ) {
  1433.            *((char *)(UsrArgd[ ad_idx ].ad_prompt) + last) = '\0';
  1434.            (VOID) strrtrim( (char *)UsrArgd[ad_idx].ad_prompt, WhiteSpace );
  1435.         }
  1436.       }/*end-if*/
  1437.    }/*while*/
  1438.  
  1439.       /* free up token tables (just the arrays, not the actual elements) */
  1440.    free( flags_vec );
  1441.    free( (start_string_used) ? (token_vec - 1) : token_vec );
  1442.  
  1443.       /* set up first & last argument entries */
  1444.    (UsrArgd -> ad_name)    =   UsrArgd[ argc+1 ].ad_name    = '\0';
  1445.    (UsrArgd -> ad_flags)   =   UsrArgd[ argc+1 ].ad_flags   = (argMask_t) 0;
  1446.    (UsrArgd -> ad_type)    =   UsrArgd[ argc+1 ].ad_type    = argNULL;
  1447.    (UsrArgd -> ad_valp)    =   UsrArgd[ argc+1 ].ad_valp    = ARBNULL;
  1448.    UsrArgd[ argc+1 ].ad_prompt = CHARNULL;
  1449.  
  1450.       /* try to get a command-description */
  1451.    cmd_description(UsrArgd) = getenv( "DESCRIPTION" );
  1452.    if ( !cmd_description(UsrArgd) ) {
  1453.       cmd_description(UsrArgd) = getenv( "CMD_DESCRIPTION" );
  1454.    }
  1455.  
  1456.    return  argc;
  1457. }
  1458.  
  1459.  
  1460. /***************************************************************************
  1461. ** ^FUNCTION: put_char_arg - print a character
  1462. **
  1463. ** ^SYNOPSIS:
  1464. */
  1465. #ifndef __ANSI_C__
  1466.    static put_char_arg( fp, ch )
  1467. /*
  1468. ** ^PARAMETERS:
  1469. */
  1470.    FILE *fp;
  1471. /*    -- the output stream to write to.
  1472. */
  1473.    int ch;
  1474. /*    -- the character to print
  1475. */
  1476. #endif  /* !__ANSI_C__ */
  1477.  
  1478. /* ^DESCRIPTION:
  1479. **    Put_char_arg will write the given character on the specified output
  1480. **    stream. If the character is metacharacter for the current shell, then
  1481. **    it is "escaped" according to the given shell syntax.
  1482. **
  1483. ** ^REQUIREMENTS:
  1484. **    <fp> should be non-NULL and open for writing.
  1485. **    <ch> should be a printable character.
  1486. **
  1487. ** ^SIDE-EFFECTS:
  1488. **    output is written to <fp>.
  1489. **
  1490. ** ^RETURN-VALUE:
  1491. **    None.
  1492. **
  1493. ** ^ALGORITHM:
  1494. **    print a character argument on standard output
  1495. **    and make sure we preserve the evaluation of
  1496. **    any special characters such as: double-quotes,
  1497. **    back-quotes, back-slash, dollar-signs, etc ....
  1498. ***^^**********************************************************************/
  1499. #ifdef __ANSI_C__
  1500.    static void put_char_arg( FILE *fp, int ch )
  1501. #endif
  1502. {
  1503.    char **escapes = Shell[ UsrSh ].escapes;
  1504.    BOOL  found = FALSE;
  1505.    for ( ; (escapes && *escapes) ; escapes++ ) {
  1506.       char * esc = *escapes;
  1507.       if ( *esc == ch )  {
  1508.          fputs( esc + 1, fp );
  1509.          found = TRUE;
  1510.       }
  1511.    }
  1512.    if ( ! found )  fputc( ch, fp );
  1513. }
  1514.  
  1515.  
  1516. /***************************************************************************
  1517. ** ^FUNCTION: put_str_arg - same as put_char_arg but for a string!
  1518. **
  1519. ** ^SYNOPSIS:
  1520. */
  1521. #ifndef __ANSI_C__
  1522.    static VOID put_str_arg( fp, str )
  1523. /*
  1524. ** ^PARAMETERS:
  1525. */
  1526.    FILE *fp;
  1527. /*    -- the output stream to write to
  1528. */
  1529.    char str[];
  1530. /*    -- the string to print
  1531. */
  1532. #endif  /* !__ANSI_C__ */
  1533.  
  1534. /* ^DESCRIPTION:
  1535. **    Put_str_arg will print the given string to the given output stream
  1536. **    and will escape any shell meta-characters for the current shell.
  1537. **
  1538. ** ^REQUIREMENTS:
  1539. **    <fp> should be non-NULL and open for writing.
  1540. **    <str> should be non-NULL and non-empty.
  1541. **
  1542. ** ^SIDE-EFFECTS:
  1543. **    Output is written to <fp>
  1544. **
  1545. ** ^RETURN-VALUE:
  1546. **    None.
  1547. **
  1548. ** ^ALGORITHM:
  1549. **    - foreach character in str
  1550. **     - put_char_arg(fp, character)
  1551. **    end-for
  1552. ***^^**********************************************************************/
  1553. #ifdef __ANSI_C__
  1554.    static void put_str_arg( FILE *fp, const char str[] )
  1555. #endif
  1556. {
  1557.    if ( !str )   return;
  1558.  
  1559.    for ( ; *str ; str++ )
  1560.       put_char_arg( fp, *str );
  1561. }
  1562.  
  1563.  
  1564. /***************************************************************************
  1565. ** ^FUNCTION: put_arg - convert & print the given value into the given buffer
  1566. **
  1567. ** ^SYNOPSIS:
  1568. */
  1569. #ifndef __ANSI_C__
  1570.    static VOID put_arg( fp, ad, val, idx )
  1571. /*
  1572. ** ^PARAMETERS:
  1573. */
  1574.    FILE *fp;
  1575. /*    -- the output stream to write to
  1576. */
  1577.    ARGDESC *ad;
  1578. /*    -- the argument-descriptor of the argument to print.
  1579. */
  1580.    cmdarg_t *val;
  1581. /*    -- the value of the argument to print
  1582. */
  1583.    short idx;
  1584. /*    -- the index in the argument-vector of the item to be printed
  1585. **       (only used when ad corresponds to an ARGVEC argument).
  1586. */
  1587. #endif  /* !__ANSI_C__ */
  1588.  
  1589. /* ^DESCRIPTION:
  1590. **    Put_arg will print the given variable/array setting on the given
  1591. **    output stream using the syntax of the user-sepcified shell.
  1592. **
  1593. ** ^REQUIREMENTS:
  1594. **    <val> should be the value corresponing to the argument-descriptor <ad>
  1595. **
  1596. ** ^SIDE-EFFECTS:
  1597. **    Output is written to <fp>.
  1598. **
  1599. ** ^RETURN-VALUE:
  1600. **    None.
  1601. **
  1602. ** ^ALGORITHM:
  1603. **    - if we have a vector, make sure we were given a valid index.
  1604. **    - if we have a vector, then value=val.vec[idx],
  1605. **      else value = val.value
  1606. **    - print the beginning of the variable setting
  1607. **    - case (argument-type) of
  1608. **        INTEGRAL-TYPE: print the integer value
  1609. **        DECIMAL-TYPE: print the floating point value
  1610. **        CHARACTER: print the character value and escape it if necessary
  1611. **        STRING: print the string value and escape it if necessary
  1612. **        BOOLEAN: print the string StrTrue if value is TRUE
  1613. **                 print the string StrFalse if value is FALSE
  1614. **    - print the end of the variable setting
  1615. ***^^**********************************************************************/
  1616. #ifdef __ANSI_C__
  1617.    static void put_arg(
  1618.       FILE *fp, const ARGDESC *ad, const cmdarg_t *val, short idx
  1619.    )
  1620. #endif
  1621. {
  1622.    if ( ARG_isVEC(ad) ) {
  1623.       if ( idx < 0  ||  idx >= val->value.Vector.count ) {
  1624.          return;   /* bad index given */
  1625.       }
  1626.  
  1627.       if ( arg_type(ad) == argStr ) {
  1628.          put_str_arg( fp, val->value.Str_vec.array[idx] );
  1629.       }
  1630.       else if ( arg_type(ad) == argChar ) {
  1631.          put_char_arg( fp, val->value.Char_vec.array[idx] );
  1632.       }
  1633.       else if ( arg_type(ad) == argDouble ) {
  1634.          fprintf( fp, "%lf", val->value.Double_vec.array[idx] );
  1635.       }
  1636.       else if ( arg_type(ad) == argFloat ) {
  1637.          fprintf( fp, "%f", val->value.Float_vec.array[idx] );
  1638.       }
  1639.       else if ( arg_type(ad) == argLong ) {
  1640.          fprintf( fp, "%ld", val->value.Long_vec.array[idx] );
  1641.       }
  1642.       else if ( arg_type(ad) == argInt ) {
  1643.          fprintf( fp, "%d", val->value.Int_vec.array[idx] );
  1644.       }
  1645.       else if ( arg_type(ad) == argShort ) {
  1646.          fprintf( fp, "%ld", val->value.Short_vec.array[idx] );
  1647.       }
  1648.  
  1649.       /* Boolean vectors are not supported */
  1650.    }/*if vector*/
  1651.    else {
  1652.       if ( arg_type(ad) == argStr ) {
  1653.          put_str_arg( fp, val->value.Str_val );
  1654.       }
  1655.       else if ( arg_type(ad) == argChar ) {
  1656.          put_char_arg( fp, val->value.Char_val );
  1657.       }
  1658.       else if ( arg_type(ad) == argDouble ) {
  1659.          fprintf( fp, "%lf", val->value.Double_val );
  1660.       }
  1661.       else if ( arg_type(ad) == argFloat ) {
  1662.          fprintf( fp, "%f", val->value.Float_val );
  1663.       }
  1664.       else if ( arg_type(ad) == argLong ) {
  1665.          fprintf( fp, "%ld", val->value.Long_val );
  1666.       }
  1667.       else if ( arg_type(ad) == argInt ) {
  1668.          fprintf( fp, "%d", val->value.Int_val );
  1669.       }
  1670.       else if ( arg_type(ad) == argShort ) {
  1671.          fprintf( fp, "%ld", val->value.Short_val );
  1672.       }
  1673.       else if ( ARG_isBOOLEAN(ad) ) {
  1674.          fprintf( fp, "%s", (val->value.Bool_val) ? StrTrue : StrFalse );
  1675.       }
  1676.    }/*else !vector*/
  1677. }
  1678.  
  1679.  
  1680. /***************************************************************************
  1681. ** ^FUNCTION: print_argvector - print shell variable settings for an ARGVEC
  1682. **
  1683. ** ^SYNOPSIS:
  1684. */
  1685. #ifndef __ANSI_C__
  1686.    static VOID print_argvector( ad, val )
  1687. /*
  1688. ** ^PARAMETERS:
  1689. */
  1690.    ARGDESC *ad;
  1691. /*    -- the argument-descriptor of the vector to print
  1692. */
  1693.    cmdarg_t *val;
  1694. /*    -- the value of the vector to print
  1695. */
  1696. #endif  /* !__ANSI_C__ */
  1697.  
  1698. /* ^DESCRIPTION:
  1699. **    Parseargs treats ARGLIST arguments in a special way. The method used
  1700. **    for setting up an argument list depends largely upon the syntax of
  1701. **    shell that was specified on the command line via the -s option
  1702. **    (although ARGLIST arguments are treated exactly the same as ARGVEC
  1703. **    arguments).  With the exception perl which always uses a comma to
  1704. **    separate array elements, all shells will use the string specified
  1705. **    with the -S option as the field separator between elements of an
  1706. **    array (the default field separator is a space character).
  1707. **
  1708. ** ^Resetting_the_Positional_Parameters_to_an_Argument_List:
  1709. **    For the Bourne, Bourne-Again, and Korn shells, if the variable name
  1710. **    corresponding to the ARGLIST argument is "--", then the positional
  1711. **    parameters of the calling program will be re-assigned to the contents
  1712. **    of the argument list ($1 will be the first item, $2 the second item,
  1713. **    and so on). In this particular case, the calling program may wish to
  1714. **    use the -u option to reset the positional parameters to NULL before
  1715. **    making any shell-variable assignments (this way, the positional
  1716. **    parameters will be unset if the associated list of command line
  1717. **    arguments is not encountered).
  1718. **
  1719. **    Similarly for the C & Z shells (zsh, csh, tcsh, itcsh), if the
  1720. **    variable name corresponding to the ARGLIST argument is "argv", then
  1721. **    the positional parameters of the calling program will be re-assigned
  1722. **    to the contents of the argument list.
  1723. **
  1724. **    For the Plan 9 shell (rc), if the variable name corresponding to the
  1725. **    ARGLIST argument is "*", then the positional parameters of then calling
  1726. **    program will be re-assigned to the contents of the argument list.
  1727. **
  1728. **    For awk and perl, if the variable name corresponding to the ARGLIST
  1729. **    argument is "ARGV", then the positional parameters of the calling
  1730. **    program will be re-assigned to the contents of the argument list.
  1731. **
  1732. ** ^Bourne_Shell_Argument_Lists:
  1733. **    For the Bourne shell, if the associated variable name is NOT "--"
  1734. **    and the -A option was NOT specified, then that variable is treated as
  1735. **    a regular shell variable and is assigned using the following syntax:
  1736. **
  1737. **         name='arg1 arg2  ...'
  1738. **
  1739. **    After invoking parseargs, if you wish to go through all the words in
  1740. **    the variable name and one of the words in name contains an IFS
  1741. **    character (such as a space or a tab), then that particular word will
  1742. **    be treated by the Bourne shell as two distinct words.
  1743. **    Also for the Bourne shell, If the associated variable name is NOT
  1744. **    "--" and the -A option WAS specified, then that variable is treated
  1745. **    as the root name of an array that is set using the following syntax:
  1746. **
  1747. **         name1='arg1'
  1748. **         name2='arg2'
  1749. **             ...
  1750. **
  1751. **    and the variable "name_count" will be set to contain the number of
  1752. **    items in the array.  The user may then step through all the items in
  1753. **    the array using the following syntax:
  1754. **
  1755. **         i=1
  1756. **         while [ $i -le $name_count ] ; do
  1757. **           eval echo "item #$i is: " \$name$i
  1758. **           i=`expr $i + 1`
  1759. **         done
  1760. **
  1761. ** ^Korn_Shell_Argument_Lists:
  1762. **    For the Korn shell, if the associated variable name is NOT "--",
  1763. **    then that variable is treated as an array and is assigned using the -A
  1764. **    option of the set command. The first item will be in ${name[0]}, the
  1765. **    second item will be in ${name[1]}, etc ..., and all items may be given
  1766. **    by ${name[*]} or ${name[@]}.  If the associated variable name is NOT
  1767. **    "--" and the -A option WAS specified, then that variable is assigned
  1768. **    using the +A option of the set command (which preserves any array
  1769. **    elements that were not overwritten by the set command).
  1770. **    It should be noted that there is a bug in versions of the Korn shell
  1771. **    earlier than 11/16/88a, in which the following:
  1772. **
  1773. **         set  -A  name  'arg1'  'arg2'  ...
  1774. **
  1775. **    causes the positional parameters to be overwritten as an unintentional
  1776. **    side-effect. If your version of the Korn shell is earlier than this
  1777. **    and you wish to keep the contents of your positional parameters after
  1778. **    invoking parseargs than you must save them yourself before you call
  1779. **    parseargs. This may be accomplished by the following:
  1780. **
  1781. **         set  -A  save_parms  "$@"
  1782. **
  1783. ** ^C_Shell_Argument_Lists:
  1784. **    For the C shells (csh, tcsh, itcsh), ARGLIST variables are treated as
  1785. **    word-lists and are assigned using the following syntax:
  1786. **
  1787. **         set  name = ( 'arg1'  'arg2'  ... )
  1788. **
  1789. **    The first item will be in $name[1], the second item will be in
  1790. **    $name[2], etc ..., and all items may be given by $name.  Notice that
  1791. **    Korn shell arrays start at index zero whereas C shell word-lists start
  1792. **    at index one.
  1793. **
  1794. ** ^Bourne-Again_Shell_Argument_Lists:
  1795. **    At present, the Free Software Foundation's Bourne-Again shell is
  1796. **    treated exactly the same as the Bourne Shell. This will change when
  1797. **    bash supports arrays.
  1798. **
  1799. ** ^Plan_9_Shell_Argument_Lists:
  1800. **    For the Plan 9 shell, if the associated variable name is not "*"
  1801. **    then it is considered to be a word-list and set using the following
  1802. **    syntax:
  1803. **
  1804. **         name=( 'arg1'  'arg2'  ... )
  1805. **
  1806. ** ^Z_Shell_Argument_Lists:
  1807. **    For the Z shell, ARGLIST variables are treated as word-lists and are
  1808. **    assigned using the following syntax:
  1809. **
  1810. **         name = ( 'arg1'  'arg2'  ... )
  1811. **
  1812. **    The first item will be in $name[1], the second item will be in
  1813. **    $name[2], etc ..., and all items may be given by $name.  Notice that
  1814. **    Korn shell arrays start at index zero whereas Z and C shell word-lists
  1815. **    start at index one.
  1816. **
  1817. ** ^Awk_Argument_Lists:
  1818. **    For awk, if the -A option is not given, then the output for the
  1819. **    variable-list will be a line with the variable name, followed by a
  1820. **    line with each of the values (each value will be separated with the
  1821. **    field separator specified using the -S option - which defaults to a
  1822. **    space).
  1823. **
  1824. **         name
  1825. **         arg1  arg2  ...
  1826. **
  1827. **    If the -A option is given, then the associated variable is considered
  1828. **    the root name of an array. The ouput for the array will consist of two
  1829. **    lines for each item in the list (as in the following example):
  1830. **
  1831. **         name1
  1832. **         arg1
  1833. **
  1834. **         name2
  1835. **         arg2
  1836. **
  1837. **    and the variable "name_count" will have an output line showing the
  1838. **    number of items in the array.
  1839. **
  1840. ** ^Perl_Argument_Lists:
  1841. **    For perl, each argument list is considered an array and is set using
  1842. **    the following syntax:
  1843. **
  1844. **         @name=( 'arg1' , 'arg2' ,  ... );
  1845. **
  1846. ** ^A_Final_Note_on_Argument_Lists:
  1847. **    The word-lists used by the C and Z shells, the arrays used by the Korn
  1848. **    shell, the Plan 9 shell, awk, perl, and the positional parameters used
  1849. **    by all shells (if overwritten by parseargs) will preserve any IFS
  1850. **    characters in their contents.  That is to say that if an item in one
  1851. **    of the aforementioned multi-word lists contains any IFS characters, it
  1852. **    will not be split up into multiple items but will remain a single item
  1853. **    which contains IFS characters.
  1854. **
  1855. ** ^REQUIREMENTS:
  1856. **    <val> should correspond to the vlue of the argument indicated by <ad>
  1857. **
  1858. ** ^SIDE-EFFECTS:
  1859. **    prints the array assignment statement on standard output
  1860. **
  1861. ** ^RETURN-VALUE:
  1862. **    None.
  1863. **
  1864. ** ^ALGORITHM:
  1865. **    - print the beginning of the array assigment statement
  1866. **    - print each item in the array (escaping characters where needed)
  1867. **    - print the end of the array assignment statement
  1868. ***^^**********************************************************************/
  1869. #ifdef __ANSI_C__
  1870.    static void print_argvector( const ARGDESC *ad, const cmdarg_t *val )
  1871. #endif
  1872. {
  1873.    register  shell_t  cli = Shell[ UsrSh ].type;
  1874.    BOOL   set_printed = FALSE;
  1875.    int    i;
  1876.    char   *varname;
  1877.  
  1878.       /* need to check for '--' for sh, ksh, and bash */
  1879.    if ( strEQ("--", val->name) && (cli == SH || cli == BASH || cli == KSH) ) {
  1880.       printf( "set -- " );
  1881.       set_printed = TRUE;
  1882.    }
  1883.       /* if faking arrays for sh, bash, or awk -- do it now! */
  1884.    else if ( ModArr  &&  (cli == SH || cli == BASH || cli == AWK) ) {
  1885.       i = strlen( val->name );
  1886.       varname = (char *)ckalloc( (i + 4) * sizeof(char) );
  1887.       for ( i = 0 ; i < val->value.Vector.count ; i++ ) {
  1888.          sprintf( varname, "%s%d", val->name, i+1 );
  1889.          printf( Shell[ UsrSh ].sclset, varname );
  1890.          printf( Shell[ UsrSh ].sclpfx );
  1891.          put_arg( stdout, ad, val, i );
  1892.          printf( "%s", Shell[ UsrSh ].sclsfx );
  1893.       }
  1894.       sprintf( varname, "%s_count", val->name );
  1895.       printf( Shell[ UsrSh ].sclset, varname );
  1896.       printf( Shell[ UsrSh ].sclpfx );
  1897.       printf( "%d", val->value.Vector.count );
  1898.       printf( "%s", Shell[ UsrSh ].sclsfx );
  1899.       return;
  1900.    }
  1901.  
  1902.       /* print the array already */
  1903.    if ( !set_printed )  {
  1904.       if ( cli == KSH ) {
  1905.          printf( Shell[ UsrSh ].aryset, ((ModArr) ? '+' : '-'), val->name );
  1906.       }
  1907.       else {
  1908.          printf( Shell[ UsrSh ].aryset, val->name );
  1909.       }
  1910.    }
  1911.    printf( Shell[ UsrSh ].arypfx );
  1912.    for ( i = 0 ; i < val->value.Vector.count ; i++ ) {
  1913.       put_arg( stdout, ad, val, i );
  1914.       if ( (i + 1)  !=  val->value.Vector.count ) {
  1915.          printf( Shell[ UsrSh ].arysep, FieldSep );
  1916.       }
  1917.    }
  1918.    printf( Shell[ UsrSh ].arysfx );
  1919. }
  1920.  
  1921.  
  1922. /***************************************************************************
  1923. ** ^FUNCTION: print_args - print the shell variable settings for the usr args
  1924. **
  1925. ** ^SYNOPSIS:
  1926. */
  1927. #ifndef __ANSI_C__
  1928.    static VOID print_args( vals, argd )
  1929. /*
  1930. ** ^PARAMETERS:
  1931. */
  1932.    cmdarg_t *vals;
  1933. /*    -- the table of argument values.
  1934. */
  1935.    ARGDESC *argd;
  1936. /*    -- the table of argument-descriptors.
  1937. */
  1938. #endif  /* !__ANSI_C__ */
  1939.  
  1940. /* ^DESCRIPTION:
  1941. **    Print_args prints the actual shell variable assignment statement(s) for
  1942. **    each argument found on the command line. If a command-line argument was
  1943. **    specified withch may take an optional value, then regargdless of whether
  1944. **    or not the optional value was supplied, the variable <name>_flag is set 
  1945. **    to the value indicated by StrTrue.
  1946. **
  1947. ** ^REQUIREMENTS:
  1948. **    The argument values have already been set due to the fact that parseargs
  1949. **    should already have been invoked to parse the command-line
  1950. **
  1951. ** ^SIDE-EFFECTS:
  1952. **    Variable assignment statements are printed on standard output.
  1953. **
  1954. ** ^RETURN-VALUE:
  1955. **    None.
  1956. **
  1957. ** ^ALGORITHM:
  1958. **    - for each argument in the argument table
  1959. **      - if this argument was supplied on the command-line
  1960. **        - if the argument takes an optional value
  1961. **            then set argname_flag = TRUE
  1962. **        - if the argument is a vector
  1963. **          - call print_argvector to print the vector elements
  1964. **        - else
  1965. **          - print the beginning of the variable assignment statement for 
  1966. **            the shell indicated by UsrSh
  1967. **          - print the argument value using put_arg
  1968. **          - print the end of the variable assignment statement for the shell
  1969. **            indicated by UsrSh
  1970. **        end-if vector
  1971. **      end-if supplied
  1972. **    end-for
  1973. ***^^**********************************************************************/
  1974. #ifdef __ANSI_C__
  1975.    static void print_args( const cmdarg_t *vals, const ARGDESC *argd )
  1976. #endif
  1977. {
  1978.    register CONST  ARGDESC *ad;
  1979.    register  int i;
  1980.    argName_t  buf;
  1981.  
  1982.       /* print values for all options given */
  1983.    for ( ad = ARG_FIRST(argd), i = 0 ; !ARG_isEND(ad) ; ARG_ADVANCE(ad), i++ ) {
  1984.       if ( ARG_isGIVEN(ad) ) {
  1985.          /******************************************************************
  1986.          ** ^SECTION: ARGVALOPT
  1987.          **    Options that may take an optional argument need special
  1988.          **    consideration.  The shell programmer needs to know whether
  1989.          **    or not the option was given, and (if given) if it was
  1990.          **    accompanied by an argument. In order to accommodate this
  1991.          **    need, parseargs will set an additional shell variable for
  1992.          **    each argument that is given the ARGVALOPT flag if it is
  1993.          **    supplied on the command line regardless of whether or not
  1994.          **    it was accompanied by its optional argument.  If the user
  1995.          **    has defined an option which may optionally take an argument 
  1996.          **    and the option appears on the command line with or without
  1997.          **    its associated argument, then the shell variable <name>_flag
  1998.          **    will be assigned the value "TRUE" (or the value supplied with
  1999.          **    the -T option to parseargs) where <name> is the name of the
  2000.          **    shell variable associated with the option in the argument
  2001.          **    description string.
  2002.          ***^^*************************************************************/
  2003.          if ( ARG_isVALOPTIONAL(ad) ) {
  2004.             sprintf(buf, "%s_flag", vals[i].name);
  2005.             printf( Shell[ UsrSh ].sclset, buf);
  2006.             printf( Shell[ UsrSh ].sclpfx );
  2007.             printf( "%s%s", StrTrue, Shell[ UsrSh ].sclsfx );
  2008.  
  2009.             if ( !ARG_isVALGIVEN(ad) )  continue;
  2010.          }/*if OPTARG*/
  2011.  
  2012.             /* vectors are special */
  2013.          if ( ARG_isVEC(ad) ) {
  2014.             print_argvector( ad, (vals + i) );
  2015.             continue;
  2016.          }
  2017.  
  2018.             /* print shell-specific variable prefix and name */
  2019.          printf( Shell[ UsrSh ].sclset, vals[i].name );
  2020.          printf( Shell[ UsrSh ].sclpfx );
  2021.  
  2022.             /* print shell-variable value */
  2023.          put_arg( stdout, ad, (vals + i), 0 );
  2024.  
  2025.             /* print the shell-specific suffix */
  2026.          printf( "%s", Shell[ UsrSh ].sclsfx );
  2027.       }/*if ARGGIVEN*/
  2028.    }/* end-for */
  2029. }
  2030.  
  2031.  
  2032. /***************************************************************************
  2033. ** ^FUNCTION: unset_positional_parameters - unset shell parameters
  2034. **
  2035. ** ^SYNOPSIS:
  2036. */
  2037. #ifndef __ANSI_C__
  2038.    static void unset_positional_parameters()
  2039. #endif
  2040. /*
  2041. ** ^PARAMETERS:
  2042. **    None.
  2043. **
  2044. ** ^DESCRIPTION:
  2045. **    Unset_positional_parameters will print (on standard output) the
  2046. **    shell commands to unset the positional parameters of the invoking
  2047. **    shell_script.
  2048. **
  2049. ** ^REQUIREMENTS:
  2050. **    The currenty shell-type has already been determined.
  2051. **
  2052. ** ^SIDE-EFFECTS:
  2053. **    Prints on stdout.
  2054. **
  2055. ** ^RETURN-VALUE:
  2056. **    None.
  2057. **
  2058. ** ^ALGORITHM:
  2059. **    - Use the syntax of the current shell to unset the positional parameters
  2060. ***^^**********************************************************************/
  2061. #ifdef __ANSI_C__
  2062.    static void unset_positional_parameters( void )
  2063. #endif
  2064. {
  2065.    printf( Shell[ UsrSh ].unset );
  2066. }
  2067.  
  2068.  
  2069. /***************************************************************************
  2070. ** ^FUNCTION: ck_cmd_errs - check for command syntax errors
  2071. **
  2072. ** ^SYNOPSIS:
  2073. */
  2074. #ifndef __ANSI_C__
  2075.    static int ck_cmd_errs()
  2076. #endif
  2077. /*
  2078. ** ^PARAMETERS:
  2079. **    None.
  2080. **
  2081. ** ^DESCRIPTION:
  2082. **    Ck_cmd_errs will check for the improper specification of arguments
  2083. **    from the command-line.
  2084. **
  2085. ** ^REQUIREMENTS:
  2086. **    The command-line should already have been parsed by parseargs(3)
  2087. **
  2088. ** ^SIDE-EFFECTS:
  2089. **    - Exits the program if an error is encountered.
  2090. **    - Assigns any needed defaults for StrTrue and StrFalse.
  2091. **    - Gets the argd-string from an environment variable if needed
  2092. **      (or sets UseStdin if it is to be read from standard input)
  2093. **    - Determines the shell specified by the user (default=Bourne)
  2094. **
  2095. ** ^RETURN-VALUE:
  2096. **    e_SUCCESS if everything checks out all right
  2097. **    Exits with one of the following exit-codes upon failure:
  2098. **
  2099. **      e_SYNTAX : command-line sytntax error
  2100. **
  2101. **      e_NOENV : AN environment variable was "purported" to contain the
  2102. **                description string that describes all the arguments but
  2103. **                upon examination, the variable was unset or empty.
  2104. **
  2105. ** ^ALGORITHM:
  2106. **    - make sure only one of '-a', '-e', and '-f' was given
  2107. **    - turn OFF UseStdin if any of the above were given
  2108. **    - make sure only one of '-l' and '-o' was given
  2109. **    - if '-e' was specified, read the environment-variable and
  2110. **      make sure it is non-NULL and non-empty
  2111. **    - assign default values for StrTrue and StrFalse if they were not
  2112. **      supplied on the command-line
  2113. **    - determine the type of the user's shell
  2114. **    - return
  2115. ***^^**********************************************************************/
  2116. #ifdef __ANSI_C__
  2117.    static int ck_cmd_errs( void )
  2118. #endif
  2119. {
  2120.       /* make sure certain arg-combos were NOT used */
  2121.    if ( (ArgdString && (ArgdFname || ArgdEnv)) || (ArgdFname && ArgdEnv) )  {
  2122.       eprintf( "%s: only one of `-a', `-e', or `-f' may be given.\n\n",
  2123.                Cmd_Name );
  2124.       usage( Args );
  2125.       exit( e_SYNTAX );
  2126.    }
  2127.  
  2128.       /* make sure at least ONE of KeyWords and Options are enabled */
  2129.    if ( OptsOnly  &&  KwdsOnly ) {
  2130.       eprintf( "%s: only one of `-o' or `l' may be given.\n\n",
  2131.                Cmd_Name );
  2132.       exit( e_SYNTAX );
  2133.    }
  2134.  
  2135.       /* turn OFF UseStdin if `-a', `-e', or `-f' was given */
  2136.    if (ArgdString || ArgdEnv || ArgdFname)
  2137.       UseStdin = FALSE;
  2138.  
  2139.       /* get the argd-string from an environment variable if so specified */
  2140.    if ( ArgdEnv ) {
  2141.       ArgdString = getenv( ArgdEnv );
  2142.       if ( !ArgdString   ||   !*ArgdString ) {
  2143.          eprintf( "%s: variable \"%s\" is NULL or does not exist\n",
  2144.                   Cmd_Name, ArgdEnv);
  2145.          exit( e_NOENV );
  2146.       }
  2147.    }
  2148.  
  2149.       /* set up default boolean value strings if needed */
  2150.    if ( !StrTrue ) {
  2151.       StrTrue  = (char *)Default_StrTrue;
  2152.    }
  2153.    if ( !StrFalse ) {
  2154.       StrFalse = (char *)Default_StrFalse;
  2155.    }
  2156.  
  2157.       /* see if we need to "default" the shell name */
  2158.    if ( !PrUsage   &&   !PrManual ) {
  2159.       if ( !ShellName ) {
  2160.          UsrSh = get_shell_index( "sh" );  /* default to Bourne Shell */
  2161.       }
  2162.       else {
  2163.          UsrSh = get_shell_index( basename( ShellName ) );
  2164.       }
  2165.    }
  2166.  
  2167.       /* everything is a-ok */
  2168.    return   e_SUCCESS;
  2169. } /* ck_cmd_errs */
  2170.  
  2171.  
  2172. /***************************************************************************
  2173. ** ^FUNCTION: main
  2174. **
  2175. ** ^SYNOPSIS:
  2176. **    main( argc, argv )
  2177. **
  2178. ** ^PARAMETERS:
  2179. **    int argc;
  2180. **    -- the number of arguments on the command-line
  2181. **
  2182. **    char *argv[];
  2183. **    -- the NULL terminated vector of arguments from the command-line
  2184. **       (the first of which is the name of the paraseargs(1) command).
  2185. **
  2186. ** ^DESCRIPTION:
  2187. **    This is the main program for parseargs(1). It parses the user's command-
  2188. **    line and outputs the approriate variable assignment statements (or prints
  2189. **    a usage message or manual template).
  2190. **
  2191. ** ^REQUIREMENTS:
  2192. **    All the static local variables that are used to define the argument
  2193. **    table and the values it points to (for parseargs(1) not for the user's
  2194. **    command) should be properly initialized).
  2195. **
  2196. ** ^SIDE-EFFECTS:
  2197. **    Shell variable assignment statements, A usage message, or a manual
  2198. **    page template is printed on standard output. Any diagnostic messages
  2199. **    are printed on standard error.
  2200. **
  2201. ** ^RETURN-VALUE:
  2202. **    Execution is terminated and the corresponding exit-code is returned
  2203. **    to the calling progarm (see the RETURN-CODES section).
  2204. **
  2205. ** ^ALGORITHM:
  2206. **    - save the name of this program
  2207. **    - parse the command-line
  2208. **    - read the argument descriptor string into memory (if needed)
  2209. **    - build the argument and value tables
  2210. **    - set ProgName to the name of the user's program
  2211. **    - if need to print usage, then do so and exit with status e_USAGE
  2212. **    - if need to print manual template, then do so, exit-status=e_USAGE
  2213. **    - modify parsing-behavior as specified on the command-line
  2214. **    - parse the user's command-line arguments
  2215. **    - unset the positional parameters if required
  2216. **    - print thye resulting shell variable assignments
  2217. **    - deallocate any remaining storage
  2218. **    - exit, status=e_SUCCESS
  2219. ***^^**********************************************************************/
  2220. MAIN( argc, argv )
  2221. {
  2222.    int  rc;
  2223.    argMask_t   flags = pa_ARGV0;
  2224.  
  2225.    Cmd_Name = *argv = basename( *argv );
  2226.  
  2227.       /* parse command-line */
  2228.    parseargs( argv, Args );
  2229.  
  2230.       /* see if there is any reason to exit now */
  2231.    (VOID) ck_cmd_errs();
  2232.  
  2233.       /* set desired option-syntax */
  2234.    if ( KwdsOnly )   BSET(flags, pa_KWDSONLY);
  2235.    if ( OptsOnly )   BSET(flags, pa_OPTSONLY);
  2236.  
  2237.       /* if needed - allocate and read in the argument descriptor string */
  2238.    if ( !ArgdString ) {
  2239.       ArgdString = get_argtable_string();
  2240.       if ( ArgdString ) {
  2241.          strrtrim( ArgdString, WhiteSpace );
  2242.          if ( !*ArgdString )   ArgdString = CHARNULL;
  2243.       }
  2244.    }
  2245.  
  2246.       /* fill in the argument tables from the environment variable */
  2247.    if ( ArgdString )   UsrArgc = build_tables( ArgdString );
  2248.  
  2249.       /* if no arguments or options taken, use NULL */
  2250.    if ( !UsrArgc )   UsrArgd = ARGDESCNULL;
  2251.  
  2252.    ProgName = UsrName;      /* set up program name */
  2253.    ProgNameLen = strlen(ProgName);
  2254.  
  2255.    if ( PrUsage ) {   /* just print usage and exit */
  2256.       usage( UsrArgd );
  2257.    }
  2258.    else if ( PrManual ) { /* print man pages and exit */
  2259.       manpage( UsrArgd );
  2260.       exit( e_USAGE );
  2261.    }
  2262.    else {   /* parse callers command-line & print variable settings */
  2263.       if ( Prompt )    BSET(flags, pa_PROMPT);
  2264.       if ( Ignore )    BSET(flags, pa_IGNORE);
  2265.       if ( AnyCase )   BSET(flags, pa_ANYCASE);
  2266.       if ( Flags1st )  BSET(flags, pa_FLAGS1ST);
  2267.       if ( flags )  (VOID) parsecntl( UsrArgd, pc_PARSEFLAGS, pc_WRITE, flags );
  2268.  
  2269. #if ( 0 == 1 )
  2270.       /* for TCL only -- send stderr to stdout */
  2271.       if ( UsrSh == TCL ) {
  2272.          close( fileno(stderr) );
  2273.          dup( fileno(stdout) );
  2274.       }
  2275. #endif
  2276.  
  2277.       if ( (rc = parseargs( UsrArgv.array, UsrArgd )) != 0 ) {
  2278.          if ( rc > 0 ) {
  2279.             rc = e_SYNTAX;
  2280.          }
  2281.          else {  /* (rc < 0) means a system error */
  2282.             cleanup();
  2283.             if ( errno )  perror( UsrName );
  2284.             exit( e_SYSTEM );
  2285.          }
  2286.       }/*if*/
  2287.  
  2288.       if ( Unset )  unset_positional_parameters();
  2289.  
  2290.       print_args( UsrVals, UsrArgd );
  2291.    }
  2292.  
  2293.    cleanup();
  2294.    exit( rc );
  2295. } /* main */
  2296.