home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume29 / parsearg / part10 < prev    next >
Encoding:
Text File  |  1992-05-18  |  78.1 KB  |  2,350 lines

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