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

  1. /*************************************************************************
  2. ** ^FILE: xparse.c - library functions for the parsargs(3) package
  3. **
  4. ** ^DESCRIPTION:
  5. **    This file implements the following functions in the parseargs library:
  6. **
  7. **       init_args()  -- constructor for a command-object
  8. **       usage()      -- pretty-print the command-usage
  9. **       parsecntl()  -- control parsing, get/set command-attributes
  10. **       parseargs()  -- parse arguments from a string vector
  11. **       fparseargs() -- parse arguments from a file pointer
  12. **       lparseargs() -- parse arguments from an arglist
  13. **       sparseargs() -- parse arguyments in a string
  14. **       vparseargs() -- parse arguments from a variable argument list
  15. **
  16. **    It should be noted that sparseargs() splits the given string up into
  17. **    a whitespace separated series of tokens, whereas vparseargs assumes
  18. **    that each parameter is already a single token (hence performs no
  19. **    token splitting).
  20. **
  21. **    Each of these functions returns 0 upon success and non-zero otherwise
  22. **    (except for usage() & init_args, which have no return value).
  23. **
  24. ** ^SIDE-EFFECTS:
  25. **    Each of the functions in the parseargs library will set the external
  26. **    character string ProgName to be the name of the last command that was
  27. **    operated upon by any of the library routines.
  28. **
  29. **    When an argument-descriptor array is first encountered by any of the
  30. **    parseargs library routines, it is initially compiled into an inter-
  31. **    mediate form that is more convenient to manipulate. As a direct
  32. **    result, it is not advisable to attempt to index directly into the
  33. **    array to manipulate one of the argument descriptors (because the
  34. **    argdesc that you thought was there may actually be somewhere else).
  35. **    After the array has been given its initial value(s), only parsecntl(3)
  36. **    should be used to manipulate or query the attributes of an argument
  37. **    descriptor.
  38. **
  39. ** ^FILES:
  40. **    <useful.h>
  41. **    <parseargs.h>
  42. **
  43. ** ^SEE_ALSO:
  44. **    argtype(3), parseargs(1), parseargs(3)
  45. **
  46. ** ^CAVEATS:
  47. **    Because of the way argument parsing is implemented under UNIX, MS-DOS
  48. **    and OS/2, option arguments which contain a leading dash (`-') (or
  49. **    whatever the option prefix character is defined to be) may not be
  50. **    specified as a separate argument on the command line, it must be part
  51. **    of the same argument.  That is to say that if a program has a -f option
  52. **    that requires a string argument, then the following:
  53. **          -f-arg
  54. **
  55. **    will properly assign the string "-arg" to the option whereas the
  56. **    following:
  57. **          -f -arg
  58. **
  59. **    will be interpreted by parseargs as two option strings: the first of
  60. **    which ("-f") is missing a required argument and the second of which
  61. **    ("-arg") will most likely be flagged as an invalid option.
  62. **
  63. **    Similarly, if the user requires an ARGLIST option to take multiple
  64. **    arguments with leading dashes then the following method must be used:
  65. **    It is a "feature" of parseargs that ARGLIST arguments are always
  66. **    appended to the current list of arguments for the given option. Thus,
  67. **    if "-f" is an option taking a list of arguments, then the following
  68. **    are all equivalent:
  69. **          -farg1 arg2
  70. **          -f arg1 arg2
  71. **          -farg1 -farg2
  72. **          -f arg1 -f arg2
  73. **
  74. **     Hence multiple "leading dash" arguments may specified as follows:
  75. **          -f-dash_arg1 -f-dash_arg2  ...
  76. **
  77. ** ^BUGS:
  78. **    When a non-multivalued argument appears more than once on the
  79. **    command-line then only the last value supplied is used. A problem
  80. **    occurs however in the following scenario: suppose `-s' is an option
  81. **    that takes an optional string argument (and suppose `-x' is some
  82. **    boolean flag). Then if the following command-line is issued:
  83. **
  84. **         command  -s string  -x  -s
  85. **
  86. **    then, the argument flags will properly correspond to the second
  87. **    instance of the `-s' option (namely ARGGIVEN will be set but ARGVAL-
  88. **    GIVEN will be unset) but the value associated with the option will be
  89. **    "string" (because the first instance overwrote the default).
  90. **    Because of this, it may be safest to reassign the default value if
  91. **    ARGGIVEN is set but ARGVALGIVEN is unset.
  92. **
  93. ** ^HISTORY:
  94. **    01/02/91     Brad Appleton     <brad@ssd.csd.harris.com>     Created
  95. **
  96. **    04/03/91     Brad Appleton     <brad@ssd.csd.harris.com>
  97. **    - fixed bug in [fvsl]parseargs() and parseargs().
  98. **      previous parse-flags should not be reset until AFTER required
  99. **      arguments are checked for, otherwise we may forget to prompt
  100. **      for them if $PARSECNTL asked us to do so.
  101. **
  102. **    08/27/91     Earl Chew     <cechew@bruce.cs.monash.edu.au>
  103. **    - split out get_description().
  104. **    - add ProgNameLen
  105. **    - support for non-writable strings
  106. **    - allow sparseargs() to parse empty strings like parseargs()
  107. **
  108. **    04/03/91     Brad Appleton     <brad@ssd.csd.harris.com>
  109. **    - changed vms_style stuff to use ps_NOTCMDLINE
  110. ***^^**********************************************************************/
  111.  
  112. #include <stdio.h>
  113. #include <ctype.h>
  114. #include <useful.h>
  115. #include "strfuncs.h"
  116. #include "exit_codes.h"
  117.  
  118. #define PARSEARGS_PRIVATE   /* include private definitions */
  119. #define PARSEARGS_NEXTERNS  /* exclude external declarations */
  120. #include "parseargs.h"
  121.  
  122. #ifdef amiga_style
  123. #  define  pa_DEFAULTS  0x000
  124. #  define  parse_argv_style   amiga_parse
  125. #  define  print_usage_style  amiga_usage
  126.    EXTERN  int   amiga_parse  ARGS(( char **, ARGDESC * ));
  127.    EXTERN  VOID  amiga_usage  ARGS(( const ARGDESC *, argMask_t ));
  128. #endif
  129. #ifdef ibm_style
  130. #  define  pa_DEFAULTS  pa_ANYCASE
  131. #  define  parse_argv_style   ibm_parse
  132. #  define  print_usage_style  ibm_usage
  133.    EXTERN  int   ibm_parse  ARGS(( char **, ARGDESC * ));
  134.    EXTERN  VOID  ibm_usage  ARGS(( const ARGDESC *, argMask_t ));
  135. #endif
  136. #ifdef unix_style
  137. #  define  pa_DEFAULTS  pa_FLAGS1ST
  138. #  define  parse_argv_style   unix_parse
  139. #  define  print_usage_style  unix_usage
  140.    EXTERN  int   unix_parse   ARGS(( char **, ARGDESC * ));
  141.    EXTERN  VOID  unix_usage   ARGS(( const ARGDESC *, argMask_t ));
  142. #endif
  143. #ifdef vms_style
  144. #  define  pa_DEFAULTS  pa_PROMPT
  145. #  define  parse_argv_style   vms_parse
  146. #  define  print_usage_style  vms_usage
  147.    EXTERN  int   vms_parse    ARGS(( char **, ARGDESC * ));
  148.    EXTERN  VOID  vms_usage    ARGS(( const ARGDESC *, argMask_t ));
  149. #endif
  150.  
  151.  
  152. #ifdef vms
  153. #  define  USER_VARIABLE "symbol"
  154. #else
  155. #  define  USER_VARIABLE "environment variable"
  156. #endif
  157.  
  158.  
  159. /***************************************************************************
  160. ** ^MACRO: SYNTAX_ERROR -  check for syntax errors & missing required arguments
  161. **
  162. ** ^SYNOPSIS:
  163. **    SYNTAX_ERROR(status, argd)
  164. **
  165. ** ^PARAMETERS:
  166. **    status
  167. **    -- current parsing status returned by last xparsexxx() call
  168. **
  169. **    argd
  170. **    --argdesc-array
  171. **
  172. ** ^RETURN-VALUE:
  173. **    Evaluates to TRUE if need to exit, FALSE otherwise
  174. **
  175. ** ^ALGORITHM:
  176. **    - if (!pa_NOCHECK) and (verify_argreqs == error) then return TRUE
  177. **    - else return FALSE
  178. ***^^**********************************************************************/
  179. #define  SYNTAX_ERROR(status, argd)  ( !verify_argreqs(argd, &status) )
  180.  
  181.  
  182. /***************************************************************************
  183. ** ^GLOBAL-VARIABLE: ProgName, ProgNameLen
  184. **
  185. ** ^VISIBILITY:
  186. **    external global (visible to functions in all files)
  187. **
  188. ** ^DESCRIPTION:
  189. **    ProgName (which is initially NULL) will be used to point to the
  190. **    command-name (specified on the command-line) of the command that
  191. **    has most recently invoked a function in the parseargs library.
  192. **    ProgNameLen will be set to the length of the string.
  193. ***^^**********************************************************************/
  194. CONST char *ProgName = CHARNULL;
  195. int ProgNameLen = 0;
  196.  
  197. EXTERN  VOID  syserr   ARGS((const char *, ...));
  198. EXTERN  VOID  usrerr   ARGS((const char *, ...));
  199. EXTERN  VOID  eprintf  ARGS((const char *, ...));
  200.  
  201. #ifdef vms_style
  202.    EXTERN BOOL argInput        ARGS((ARGDESC *, char *, BOOL));
  203.    EXTERN BOOL argOutput       ARGS((ARGDESC *, char *, BOOL));
  204. #endif
  205.  
  206. #define  MAXLINE  256
  207.  
  208.  
  209. /* override argument descriptor, if none given by user */
  210. static ARGDESC  Empty_ArgDesc[] = { START_ARGUMENTS, END_ARGUMENTS };
  211.  
  212. /***************************************************************************
  213. ** ^SECTION: DEFAULT-ARGUMENTS
  214. **    Each argdesc-array has an initial default argument list (which may be
  215. **    reset using the pc_DEFARGS function code with parsecntl). This initial
  216. **    default argument-list contains `?' and `H' which may be used as single
  217. **    character keywords to display command-usage for all command-line
  218. **    styles.  Similarly, "?", "H", and "Help" may be used as long-keywords
  219. **    to display command-usage for all command-line styles.  In Addition,
  220. **    for VMS style commands, the qualifiers /INPUT=file, /OUTPUT=file, and
  221. **    /ERROR=file, may be used to redirect stdin, stdout, and stderr
  222. **    (respectively) to a file.  For AmigaDOS style commands, the keyword
  223. **    "ENDKWDS" may be used to disable parsing for any more keywords on
  224. **    the command-line.
  225. ***^^**********************************************************************/
  226. static  ARGDESC  Default_ArgDesc[] = {
  227.    START_ARGUMENTS,
  228.  
  229. /* <name> <flags>    <type>     <valp>         <prompt>      */
  230.    { '?', ARGHIDDEN, argUsage, __ NULL,    "? (print usage and exit)" },
  231.    { 'H', ARGHIDDEN, argUsage, __ NULL,    "Help (print usage and exit)" },
  232.  
  233. #ifdef amiga_style
  234.    { '-', ARGHIDDEN, argEnd,   __ NULL,    "ENDKeyWorDS" },
  235. #endif
  236.  
  237. #ifdef vms_style
  238. # ifdef vms
  239.    { '<', ARGHIDDEN, argInput, __ &stdin,   "sysINPUT (redirect SYS$INPUT)" },
  240.    { '>', ARGHIDDEN, argOutput, __ &stdout, "sysOUTPUT (redirect SYS$OUTPUT)" },
  241.    { '%', ARGHIDDEN, argOutput, __ &stderr, "sysERROR (redirect SYS$ERROR)" },
  242. # else
  243.    { '<', ARGHIDDEN, argInput, __ stdin,   "sysINPUT (redirect SYS$INPUT)" },
  244.    { '>', ARGHIDDEN, argOutput, __ stdout, "sysOUTPUT (redirect SYS$OUTPUT)" },
  245.    { '%', ARGHIDDEN, argOutput, __ stderr, "sysERROR (redirect SYS$ERROR)" },
  246. # endif
  247. #endif
  248.  
  249.    END_ARGUMENTS
  250. };
  251.  
  252.  
  253. #ifdef AmigaDOS
  254. #  define  getenv(s)   CHARNULL
  255. #  define  envfree(s)  s = CHARNULL
  256. #endif
  257.  
  258. #if ( defined(MSDOS) || defined(OS2) )
  259.    EXTERN  char *getenv ARGS(( const char * ));
  260. #  define  envfree(s)  s = CHARNULL
  261. #endif
  262.  
  263. #ifdef unix
  264.    EXTERN  char *getenv ARGS(( const char * ));
  265. #  define  envfree(s)  s = CHARNULL
  266. #endif
  267.  
  268. #ifdef vms
  269. #  define  getenv(s)   get_symbol(s)
  270. #  define  envfree(s)  (s) = ( !(s) ) ? CHARNULL : (free(s), CHARNULL)
  271. #  include <descrip.h>
  272. #  include <libdef.h>
  273.  
  274. #  define  MAXLEN 255
  275.  
  276.    /***************************************************************************
  277.    ** ^FUNCTION: get_symbol - retrieve the value of a VMS symbol
  278.    **
  279.    ** ^SYNOPSIS:
  280.    */
  281. #  ifndef __ANSI_C__
  282.       char *get_symbol( sym_name )
  283.    /*
  284.    ** ^PARAMETERS:
  285.    */
  286.       char *sym_name;
  287.    /*    -- name of the symbol to retrieve
  288.    */
  289. #  endif  /* !__ANSI_C__ */
  290.  
  291.    /* ^DESCRIPTION:
  292.    **    Get_symbol will lookup the named symbol and return its value
  293.    **    as a string.
  294.    **
  295.    ** ^REQUIREMENTS:
  296.    **    sym_name should correspond to the name of a pre-defined symbol.
  297.    **
  298.    ** ^SIDE-EFFECTS:
  299.    **    None.
  300.    **
  301.    ** ^RETURN-VALUE:
  302.    **    NULL if the symbol is not found, otherwise it copies the symbol
  303.    **    value and returns the address of the copy (which may later be
  304.    **    deallocated using free()).
  305.    **
  306.    ** ^ACKNOWLEDGEMENTS:
  307.    **    Thanx to Jim Barbour for writing most of this code. --BDA
  308.    **
  309.    ** ^ALGORITHM:
  310.    **    Trivial - just use the LIB$GET_SYMBOL system service.
  311.    ***^^**********************************************************************/
  312. #  ifdef __ANSI_C__
  313.       char *get_symbol( const char *sym_name )
  314. #  endif
  315.    {
  316.       unsigned long stat, lib$get_symbol();
  317.       unsigned short buflen;
  318.       char sym_value[ MAXLEN ];
  319.       $DESCRIPTOR( sym_name_d, sym_name );
  320.       $DESCRIPTOR( sym_value_d, sym_value );
  321.  
  322.       sym_value_d.dsc$w_length =  MAXLEN;
  323.       sym_name_d.dsc$w_length = strlen( sym_name );
  324.       stat = lib$get_symbol( &sym_name_d, &sym_value_d, &buflen, (void *)0 );
  325.       if ( stat == LIB$_NOSUCHSYM ) {
  326.          return  CHARNULL;
  327.       }
  328.       else if ( ! (stat & 1) ) {
  329.          exit(stat);
  330.       }
  331.       sym_value[ buflen ] = '\0';
  332.       return  strdup( sym_value );
  333.    }
  334. #endif /* vms */
  335.  
  336.  
  337. /***************************************************************************
  338. ** ^FUNCTION: is_interactive - determine if a stream is "interactive"
  339. **
  340. ** ^SYNOPSIS:
  341. */
  342. #ifndef __ANSI_C__
  343.    static BOOL is_interactive( fd )
  344. /*
  345. ** ^PARAMETERS:
  346. */
  347.    int fd;
  348. /*    -- file descriptor associated with the input stream
  349. */
  350. #endif  /* !__ANSI_C__ */
  351.  
  352. /* ^DESCRIPTION:
  353. **    Is_interactive determines whether or not the given i/o stream is
  354. **    associated with a terminal.
  355. **
  356. ** ^REQUIREMENTS:
  357. **    Fd must correspond to a valid, open, file-descriptor.
  358. **
  359. ** ^SIDE-EFFECTS:
  360. **    None.
  361. **
  362. ** ^RETURN-VALUE:
  363. **    TRUE if fd is associated with a terminal, FALSE otherwise.
  364. **
  365. ** ^ALGORITHM:
  366. **    Trivial - just use isatty and restore errno if necessary
  367. ***^^**********************************************************************/
  368. #ifdef __ANSI_C__
  369.    static BOOL is_interactive( int fd )
  370. #endif
  371. {
  372. #ifdef unix
  373.    EXTERN int isatty  ARGS((int));
  374.    extern int errno;
  375.    int saverr = errno;  /* save errno */
  376.  
  377.    if ( isatty(fd) )
  378.       return  TRUE;
  379.    else {
  380.       errno = saverr;
  381.       return  FALSE;
  382.    }
  383. #else
  384. #ifdef vms
  385.    EXTERN int isatty  ARGS((int));
  386.    int ret = isatty( fd );
  387.  
  388.    if ( ret == -1 ) /* error with fd */
  389.       syserr( "error on file descriptor #%d", fd );
  390.    else if ( ret )
  391.       return  TRUE;
  392.    else
  393.       return  FALSE;
  394. #else
  395.    return  TRUE;
  396. #endif
  397. #endif
  398. }
  399.  
  400.  
  401. /***************************************************************************
  402. ** ^FUNCTION: init_args - Initialize the command object
  403. **
  404. ** ^SYNOPSIS:
  405. */
  406. #ifndef __ANSI_C__
  407.    VOID init_args( argd )
  408. /*
  409. ** ^PARAMETERS:
  410. */
  411.    ARGDESC argd[];
  412. /*    -- the array of command-arguments to initialize.
  413. */
  414. #endif  /* !__ANSI_C__ */
  415.  
  416. /* ^DESCRIPTION:
  417. **    Init_args performs all the actions that are required to prepare an
  418. **    argdesc-array for use by any of the parseargs functions. Storrage
  419. **    is allocated and initialized and argument descriptions are compiled.
  420. **
  421. ** ^REQUIREMENTS:
  422. **    <argd> must point to an array that has been declared using the CMD_XXXX
  423. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  424. **
  425. ** ^SIDE-EFFECTS:
  426. **    The argd is initialized for use.
  427. **
  428. ** ^RETURN-VALUE:
  429. **    None.
  430. **
  431. ** ^ALGORITHM:
  432. **    - compile the command name, purpose, and description if they were given
  433. **    - if ENDOFARGS was used without STARTOFARGS, then shift each item in
  434. **      the array down by one position.
  435. **    - initialize the parse-flags to the default settings
  436. **    - initialize the default argument search-list
  437. **    - allocate and initialize the command-context
  438. **    - for each command-line argument in argd
  439. **      - compile the ad_prompt field and set the default initial ARGXXX flags
  440. **      end-for
  441. ***^^**********************************************************************/
  442. #ifdef __ANSI_C__
  443.    void init_args( ARGDESC argd[] )
  444. #endif
  445. {
  446.    register ARGDESC *ad, *anchor;
  447.    int  ad_count = 0;
  448.    BOOL old_style = FALSE;
  449.    char *description = CHARNULL, *purpose = CHARNULL;
  450.    int  desclen;
  451.  
  452.    if ( !argd )  return;
  453.  
  454.       /* dont initialize if its already been done */
  455.    if ( CMD_isINIT(argd) )  return;
  456.  
  457.    if ( !ARG_isEND(argd) )  old_style = TRUE;
  458.  
  459.       /* determine the argument count and preprocess each ad-entry */
  460.    anchor = ( old_style ) ? argd : ARG_FIRST(argd);
  461.    for ( ad = anchor ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) )  {
  462.       ad_count++;
  463.  
  464.          /* set-up any positional args that we know of */
  465.       if ( ARG_isPOSONLY(ad) )  BSET( arg_flags(ad), ARGPOS );
  466.  
  467.          /* set-up any default ARGNOVALs that we know of */
  468.       if (ARG_isBOOLEAN(ad) || ARG_isPSEUDOARG(ad))
  469.          BSET( arg_flags(ad), ARGNOVAL );
  470.  
  471.       if ( get_argdesc( (char *)arg_sname(ad), &desclen ) )
  472.          BSET(arg_flags(ad), ARGDESCRIBED);
  473.    }
  474.  
  475.       /* shift all the entries down one to make room for a new 1st-entry
  476.       ** It would've been nice to just swap the 1st and last entries but
  477.       ** I have to preserve the order that all positional parameters are
  478.       ** given in.
  479.       */
  480.    if ( old_style  &&  ad_count > 0 ) {
  481.       anchor = ad + 1;  /* save this position */
  482.       for ( ; ad > argd ; ARG_RETREAT(ad) ) {
  483.          memcpy( (ARBPTR)ad, (ARBPTR)(ad - 1), sizeof(ARGDESC) );
  484.       }/*for*/
  485.       memcpy( (ARBPTR)argd, (ARBPTR)anchor, sizeof(ARGDESC) );
  486.    }
  487.    else  anchor = ad;
  488.  
  489.      /* set default parse-flags */
  490. #ifndef ibm_style
  491.    cmd_flags(argd) = pa_DEFAULTS;
  492. #else
  493.    {
  494.       char *pfx = getenv( "SWITCHAR" );
  495.       if ( pfx  &&  *pfx == '-' )
  496.          cmd_flags(argd) = pa_FLAGS1ST;
  497.       else
  498.          cmd_flags(argd) = pa_DEFAULTS;
  499.    }
  500. #endif
  501.  
  502.    if ( cmd_name(argd) )  cmd_name(argd) = strdup( cmd_name(argd) );
  503.      /* if new-style, get the purpose from the command name */
  504.    if ( !old_style  &&  cmd_name(argd) ) {
  505.       purpose = cmd_name(argd);
  506.    }
  507.  
  508.       /* set the program name */
  509.    if ( ProgName  &&  *ProgName  &&  !cmd_name(argd) ) {
  510.       cmd_name(argd) = ProgName;
  511.    }
  512.  
  513.       /* set context */
  514.    if ( !cmd_context(argd) )  {
  515.       cmd_context(argd)  = (CTXDESC *) anchor;
  516.       cmd_state(argd)    = ( old_style ) ? ps_OLDSTYLE : (ps_flags_t) 0;
  517.       cmd_argv0(argd)    = cmd_name(argd);
  518.       cmd_purpose(argd)  = purpose;
  519.       cmd_ptrs(argd) = (ARGDPTRS *)malloc( sizeof(ARGDPTRS) );
  520.       if ( !cmd_ptrs(argd) ) {
  521.          syserr( "malloc failed in init_args()" );
  522.       }
  523.       if ( argd == Default_ArgDesc ) {
  524.          cmd_defargs(argd)  = ARGDESCNULL;
  525.       }
  526.       else {
  527.          if ( !CMD_isINIT(Default_ArgDesc) )  init_args( Default_ArgDesc );
  528.          cmd_defargs(argd)  = Default_ArgDesc;
  529.       }
  530.       cmd_list(argd) = ARGDESCNULL;
  531. #ifdef amiga_style
  532.       cmd_prev(argd) = ARGDESCNULL;
  533. #endif
  534.    }
  535. }
  536.  
  537.  
  538. /***************************************************************************
  539. ** ^FUNCTION: reset_args - (re)set a command for parsing
  540. **
  541. ** ^SYNOPSIS:
  542. */
  543. #ifndef __ANSI_C__
  544.    static VOID reset_args( argd )
  545. /*
  546. ** ^PARAMETERS:
  547. */
  548.    ARGDESC argd[];
  549. /*    -- array or command-line arguments to be reset
  550. */
  551. #endif  /* !__ANSI_C__ */
  552.  
  553. /* ^DESCRIPTION:
  554. **    Reset_args will prepare a command for parsing. The command-state is
  555. **    initialized and each argument is reset to "unmatched".
  556. **
  557. ** ^REQUIREMENTS:
  558. **    <argd> must point to an array that has been declared using the CMD_XXXX
  559. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  560. **
  561. ** ^SIDE-EFFECTS:
  562. **    resets the ARG flags of each argument and the command-state of argd.
  563. **
  564. ** ^RETURN-VALUE:
  565. **    None.
  566. **
  567. ** ^ALGORITHM:
  568. **    - reset the command-context to be (as of yet) unparsed
  569. **    - reset the ARG flags of the programmer & default argument descriptors
  570. ***^^**********************************************************************/
  571.  
  572.    /* arg-flags to be reset before parsing a new command-line */
  573. #define  ARGDEFAULTS  ( ARGGIVEN | ARGVALGIVEN | ARGKEYWORD | ARGVALSEP )
  574.  
  575. #ifdef __ANSI_C__
  576.    static void reset_args( ARGDESC argd[] )
  577. #endif
  578. {
  579.    register ARGDESC *args, *ad;
  580.  
  581.    if ( !CMD_isINIT(argd) )  init_args(argd);
  582.  
  583.       /* reset the command context */
  584.    BCLEAR( cmd_state(argd), (ps_NOFLAGS|ps_NOCMDENV|ps_NOPARSECNTL) );
  585.    cmd_list(argd) = ARGDESCNULL;
  586. #ifdef amiga_style
  587.    cmd_prev(argd) = ARGDESCNULL; /* No argument requested */
  588. #endif
  589.  
  590.       /* clear out any cruft in the argument descriptors */
  591.    for ( args = argd ; args ; args = cmd_defargs(args) ) {
  592.       for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  593.          BCLEAR( arg_flags(ad), ARGDEFAULTS );
  594.  
  595. #if ( defined(vms_style)  ||  defined(amiga_style) )
  596.             /* vms and amiga use keywords only */
  597.          BSET( arg_flags(ad), ARGKEYWORD );
  598. #endif
  599.       }/*for*/
  600.    }/*for*/
  601. }
  602.  
  603. #undef ARGDEFAULTS
  604.  
  605.  
  606. /***************************************************************************
  607. ** ^FUNCTION: prompt_user - prompt the user for a missing argument
  608. **
  609. ** ^SYNOPSIS:
  610. */
  611. #ifndef __ANSI_C__
  612.    static BOOL prompt_user( ad, argname )
  613. /*
  614. ** ^PARAMETERS:
  615. */
  616.    ARGDESC *ad;
  617. /*    -- pointer to the argument to be supplied by the user
  618. */
  619.    char *argname;
  620. /*    -- name of the argument to be supplied by the user
  621. */
  622. #endif  /* !__ANSI_C__ */
  623.  
  624. /* ^DESCRIPTION:
  625. **    Prompt_user will attempt to prompt the user to supply (on standard input)
  626. **    the value for the given argument. If the argument is a list, then each
  627. **    item of the list should be given on a separate line (with a blank line
  628. **    terminating the list).
  629. **
  630. **    No special "escaping" or translation is performed on the resulting user
  631. **    input, hence any whitespace ro quotes will be considered as part of the
  632. **    argument value.
  633. **
  634. **    If stdin is NOT associated with a terminal then no prompting is performed
  635. **    and FALSE is returned.  If the user enters in invalid value for the
  636. **    argument then the value is discarded and FALSE is returned (so you
  637. **    better get it right the first time).
  638. **
  639. ** ^REQUIREMENTS:
  640. **    Only the first 255 characters of user input is used.
  641. **
  642. ** ^SIDE-EFFECTS:
  643. **    Modifies <ad> accordingly.
  644. **
  645. ** ^RETURN-VALUE:
  646. **    FALSE if there is an error, TRUE otherwise.
  647. **
  648. ** ^ALGORITHM:
  649. **    - if stdin is not connected to a terminal return FALSE
  650. **    - if argument is not a list then
  651. **      - get string from user
  652. **      - attempt to convert the string
  653. **      - return TRUE upon success and FALSE upon failure
  654. **    - else (the argument is a list)
  655. **      - while (user has not entered a blank line) do
  656. **        - get string from user
  657. **        - attempt to convert the string
  658. **        - return FALSE upon failure (otherwise continue to loop)
  659. **        end-while
  660. **      end-if
  661. ***^^**********************************************************************/
  662. #ifdef __ANSI_C__
  663.    static BOOL prompt_user( ARGDESC *ad, const char *argname )
  664. #endif
  665. {
  666.    BOOL  error = FALSE, first = TRUE;
  667.    int   end;
  668.    char  buf[ MAXLINE ];
  669.  
  670.    if ( !is_interactive(STDIN) )  return  FALSE;
  671.  
  672.    if ( ARG_isMULTIVAL(ad) ) {
  673.       fprintf(stderr, "Enter one %s per-line ", argname);
  674.       fprintf(stderr, "(enter a blank line to stop).\n");
  675.    }
  676.  
  677.    do {  /* need repeated prompting for an ARGLIST or an ARGVEC */
  678.       /* get argument from user */
  679.       *buf='\0';
  680. #ifdef vms_style
  681.       fprintf(stderr, "\r%s> ", argname);
  682. #else
  683.       fprintf(stderr, "\rEnter %s: ", argname);
  684. #endif
  685.       fflush( stderr );
  686.       if ( !fgets(buf, MAXLINE, stdin) )  *buf = '\0';
  687.  
  688.       /* discard the newline */
  689.       end = strlen(buf) - 1;
  690.       if ( end >= 0  &&  buf[end] == '\n' )
  691.          buf[ end ] = '\0';
  692.  
  693.       /* make sure we read something! */
  694.       if ( *buf == '\0' ) {
  695.          if ( first ) {
  696.             usrerr( "error - no %s given", argname );
  697.             error = TRUE;
  698.          }
  699.          continue;
  700.       }
  701.  
  702.       /* try to convert what we read (remember - buf is transient) */
  703.       if (  (*arg_type(ad))(ad, buf, TRUE) )
  704.          BSET(arg_flags(ad), ARGGIVEN | ARGVALGIVEN);
  705.       else
  706.          error = TRUE;
  707.  
  708.       first = FALSE;
  709.    } while ( !error &&  ARG_isMULTIVAL(ad)  &&  *buf );
  710.  
  711.    return  (error) ? FALSE : TRUE;
  712. }
  713.  
  714.  
  715. /***************************************************************************
  716. ** ^FUNCTION: verify_argreqs - check for any missing required arguments
  717. **
  718. ** ^SYNOPSIS:
  719. */
  720. #ifndef __ANSI_C__
  721.    static BOOL verify_argreqs( cmd, parse_status )
  722. /*
  723. ** ^PARAMETERS:
  724. */
  725.    ARGDESC *cmd;
  726. /*    -- the argdesc-array of command-line arguments.
  727. */
  728.    int *parse_status;
  729. /*    -- address of current parse_status
  730. */
  731. #endif  /* !__ANSI_C__ */
  732.  
  733. /* ^DESCRIPTION:
  734. **    Verify_argreqs will reset parse_status to a success-value if it 
  735. **    previously indicated a syntax error but pa_IGNORE is set to ignore
  736. **    syntax error.  Verify_argreqs will then verify that any required
  737. **    command-line arguments have been supplied (if they were not and
  738. **    pa_PROMPT is set, then the missing values will be prompted for).
  739. **
  740. ** ^REQUIREMENTS:
  741. **    <cmd> must point to an array that has been declared using the CMD_XXXX
  742. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  743. **
  744. **    parse_status should be a pointer to the result of a previous call to
  745. **    {f,l,s,v,}parseargs.
  746. **
  747. ** ^SIDE-EFFECTS:
  748. **    The arg-descs for missing arguments may be modified by prompt_user.
  749. **    parse_status will be modified to indicate whether or not a syntax
  750. **    error really has occurred (it will be pe_SUCCESS if all is hunky-dory).
  751. **
  752. ** ^RETURN-VALUE:
  753. **    FALSE if there is an error, TRUE otherwise.
  754. **
  755. ** ^ALGORITHM:
  756. **    - if parse_status is a syntax error but pa_IGNORE is set
  757. **      - ignore the error by resetting the status to pe_SUCCESS
  758. **      end-if
  759. **    - if pa_NOCHECK is not set then check for any missing required
  760. **      arguments (using prompt_user to get the values if pa_PROMPT is set).
  761. ***^^**********************************************************************/
  762. #ifdef __ANSI_C__
  763.    static BOOL verify_argreqs( ARGDESC *cmd, int *parse_status )
  764. #endif
  765. {
  766.    register ARGDESC *ad;
  767.    BOOL error;
  768.    argName_t  s;
  769.  
  770.    if ( !CMD_isINIT(cmd) )  init_args(cmd);
  771.  
  772.    if ( *parse_status == pe_SYNTAX  &&  BTEST(cmd_flags(cmd), pa_IGNORE) ) {
  773.       *parse_status = pe_SUCCESS;
  774.    }
  775.    error = ( *parse_status != pe_SUCCESS ) ? TRUE : FALSE;
  776.  
  777.    if ( !BTEST(cmd_flags(cmd), pa_NOCHECK) ) {
  778.       for (ad = ARG_FIRST(cmd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
  779.          if (arg_type(ad) == argDummy)  continue;
  780.  
  781.          if ( ARG_isREQUIRED(ad)  &&  !ARG_isGIVEN(ad) ) {
  782.                /* still didn't get a value... sigh */
  783.             if ( ARG_isPOSITIONAL(ad) ) {
  784.                (VOID) get_argname( arg_sname(ad), s );
  785.                usrerr("%s required", s);
  786.             }
  787.             else {
  788. #ifdef amiga_style
  789.                (VOID) get_kwdname( arg_sname(ad), s );
  790.                usrerr("argument required for %s keyword", s);
  791. #endif
  792. #ifdef ibm_style
  793.                (VOID) get_argname( arg_sname(ad), s );
  794.                {
  795.                   char c, *pfx = getenv( "SWITCHAR" );
  796.                   c = ( pfx &&  *pfx ) ? *pfx : '/';
  797.                   usrerr("%s required for %c%c switch", s, c, arg_cname(ad));
  798.                }
  799. #endif
  800. #ifdef unix_style
  801.                (VOID) get_argname( arg_sname(ad), s );
  802.                usrerr("%s required for %c%c flag", s, c_OPT_PFX, arg_cname(ad));
  803. #endif
  804. #ifdef vms_style
  805.                (VOID) get_kwdname( arg_sname(ad), s );
  806.                usrerr("value required for %c%s qualifier", *s_KWD_PFX, s);
  807. #endif
  808.             }
  809.  
  810.             if ( !error  &&  BTEST(cmd_flags(cmd), pa_PROMPT) ) {
  811.                if ( !prompt_user(ad, s) )  error = TRUE;
  812.             }
  813.             else if ( !error ) {
  814.                error = TRUE;
  815.             }
  816.          }/*if*/
  817.       }/*for*/
  818.    }/*if*/
  819.  
  820.    return  (error) ? FALSE : TRUE;
  821. }
  822.  
  823.  
  824. /***************************************************************************
  825. ** ^FUNCTION: read_flags - read bitmask-flags in a string
  826. **
  827. ** ^SYNOPSIS:
  828. */
  829. #ifndef __ANSI_C__
  830.    static argMask_t read_flags( str, c_tbl, m_tbl )
  831. /*
  832. ** ^PARAMETERS:
  833. */
  834.    char *str;
  835. /*    -- the string to parse
  836. */
  837.    char c_tbl[];
  838. /*    -- a (NUL-terminated) array of alpha-numeric characters Each character
  839. **    is the first letter/number of a token.  If the character is lowercase,
  840. **    then the corresponding mask is set, if it is uppercase, the corresponding
  841. **    mask is cleared.
  842. */
  843.    argMask_t m_tbl[];
  844. /*    -- a table of bitmasks for the given character-table. The mask to use
  845. **       for c_tbl[i] is m_tbl[i].
  846. */
  847. #endif  /* !__ANSI_C__ */
  848.  
  849. /* ^DESCRIPTION:
  850. **    Read_flags will parse the given string for any flags present in c_tbl[].
  851. **    When a flag is matched, its corresponding entry in m_tbl[] is set (or
  852. **    cleared) in the currently matched set of flags.
  853. **
  854. **    When the given string has been completely scanned, the resulting
  855. **    combination of masks that were matched is returned.
  856. **
  857. ** ^REQUIREMENTS:
  858. **    A '\0' or a ';' indicates the end of parsing. A token/mask may be negated
  859. **    by preceding it with one of '!', '^', or '~'. Tokens must be separated by
  860. **    one or more non-alphanumerics (other than '!', '~', and '^').
  861. **
  862. ** ^SIDE-EFFECTS:
  863. **    None.
  864. **
  865. ** ^RETURN-VALUE:
  866. **    The combination of bitmasks indicated by the given string.
  867. **
  868. ** ^ALGORITHM:
  869. **    - set masks = 0
  870. **    - for each "token" in str
  871. **      - if token doesnt match any entries in c_tbl then continue
  872. **      - let i = index of matched token in c_tbl
  873. **      - if c_tbl[i] is lowercase then OR masks with m_tbl[i]
  874. **      - if c_tbl[i] is uppercase then AND masks with NOT(m_tbl[i])
  875. **      end-for
  876. **    - return masks
  877. ***^^**********************************************************************/
  878.  
  879.       /* macros for parsing flags */
  880. #define  IS_NEGCHAR(c)  ( c == '!' || c == '^' || c == '~' )
  881. #define  SKIP_TOK(s)    while ( isalnum(*s) || *s == '_' || *s == '-' ) s++
  882. #define  SKIP_SEP(s)    while ( *s && !IS_NEGCHAR(*s) && !isalnum(*s) ) s++
  883. #define  NEXT_TOK(s)    SKIP_TOK(s) ; SKIP_SEP(s)
  884. #define  TOGGLE(x)      x = (x) ? FALSE : TRUE
  885.  
  886. #ifdef __ANSI_C__
  887.    static argMask_t read_flags(
  888.       const char *str, const char c_tbl[], const argMask_t m_tbl[]
  889.    )
  890. #endif
  891. {
  892.    char *pos = CHARNULL;
  893.    BOOL  negated = FALSE;
  894.    argMask_t  mask = 0, flags = 0;
  895.  
  896.    while ( *str  &&  *str != ';' ) {
  897.       mask = 0;
  898.       negated = FALSE;
  899.  
  900.       while ( IS_NEGCHAR(*str) ) {
  901.          TOGGLE(negated);
  902.          ++str;
  903.          SKIP_SEP(str);
  904.       }
  905.  
  906.       if ( (pos = strchr( c_tbl, TOLOWER(*str) )) != CHARNULL ) {
  907.          mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
  908.       }
  909.       else if ( (pos = strchr( c_tbl, TOUPPER(*str) )) != CHARNULL ) {
  910.          TOGGLE(negated);
  911.          mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
  912.       }
  913.       else {
  914.          negated = FALSE;
  915.       }
  916.  
  917.       NEXT_TOK(str);
  918.  
  919.       if ( mask || negated ) {
  920.          if   (   negated  )  BCLEAR(flags, mask);
  921.          else /* !negated */  BSET(flags, mask);
  922.       }/*if*/
  923.    }/*while*/
  924.  
  925.    return  flags;
  926. }
  927.  
  928. #undef  IS_NEGCHAR
  929. #undef  SKIP_TOK
  930. #undef  SKIP_SEP
  931. #undef  NEXT_TOK
  932. #undef  TOGGLE
  933.  
  934.  
  935. /***************************************************************************
  936. ** ^FUNCTION: get_usage_flags - determine user-defined usage-message format
  937. **
  938. ** ^SYNOPSIS:
  939. */
  940. #ifndef __ANSI_C__
  941.    static argMask_t get_usage_flags( cmd )
  942. /*
  943. ** ^PARAMETERS:
  944. */
  945.    ARGDESC *cmd;
  946. /*    -- command-structure to determine usage-flags for
  947. */
  948. #endif  /* !__ANSI_C__ */
  949.  
  950. /* ^DESCRIPTION:
  951. **    Through the use of an environment variable (or a VMS symbol), the user
  952. **    may control the syntax and the verbosity of the command-usage messages
  953. **    that are printed by parseargs. The desired level of verbosity may be
  954. **    set by defining the environment variable "USAGECNTL" to be a com-
  955. **    bination of strings (case insensitive). The value of each string con-
  956. **    trols one of three different "modes" of behavior in the displaying
  957. **    of usage messages:  The first "mode" is "verbose" mode, which con-
  958. **    trols whether or not a detailed description of each argument should
  959. **    accompany the usual command-line sysnopsis. If verbose mode is
  960. **    "off", then only a command-line synopsis is printed (this is also
  961. **    refferred to as "terse" mode). The other two "modes" control the
  962. **    displaying of option syntax and long-option syntax. A mode may be
  963. **    explicitly disabled by preceding its corresponding string with the `!'
  964. **    or `-' character. The "modes" which correspond to the possible values of
  965. **    the "USAGECNTL" environment variable are given by the following table.
  966. **
  967. **    "Quiet"
  968. **       No usage message of any kind is displayed.
  969. **
  970. **    "Silent"
  971. **       Same as Quiet.
  972. **
  973. **    "Paged"
  974. **       The usage message is piped to a pager. The pager used is named by
  975. **       the "USAGE_PAGER" environment variable. If this variable is unset
  976. **       or empty (or is not the name of an executable program), the pager
  977. **       named by the "PAGER" environment variable us used.  If this variable
  978. **       is unset or empty (or is not the name of an executable program) then
  979. **       the program "/usr/ucb/more" is used.
  980. **
  981. **    "Description"
  982. **       The command description is printed.
  983. **
  984. **    "Terse"
  985. **       Terse mode, just print command-line synopsis.
  986. **
  987. **    "Verbose"
  988. **       Verbose mode, print descriptions for each argument
  989. **
  990. **    "Options"
  991. **       Option syntax is displayed.
  992. **
  993. **    "LongOpts"
  994. **       Long-option syntax is displayed.
  995. **
  996. **    "KeyWords"
  997. **       Same as "LongOpts".
  998. **
  999. **    If the environment variable "USAGECNTL" is empty or undefined, then
  1000. **    the default usage level (which is presently "Verbose + Options")
  1001. **    will be used.
  1002. **
  1003. ** ^REQUIREMENTS:
  1004. **    <cmd> must point to an array that has been declared using the CMD_XXXX
  1005. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  1006. **
  1007. ** ^SIDE-EFFECTS:
  1008. **    None.
  1009. **
  1010. ** ^RETURN-VALUE:
  1011. **    The usage-flags corresponding to the value of $USAGECNTL
  1012. **
  1013. ** ^ALGORITHM:
  1014. **    - If $USAGECNTL is NULL or empty, return the default flags
  1015. **    - get the value of $USAGECNTL and call read_flags to parse it.
  1016. **    - return the resulting flags.
  1017. ***^^**********************************************************************/
  1018. #ifdef __ANSI_C__
  1019.    static argMask_t get_usage_flags( const ARGDESC *cmd )
  1020. #endif
  1021. {
  1022.    register  char  *usage_env;
  1023.    argMask_t  usg_ctl = 0;
  1024.    static CONST char  usage_tokens[] = {
  1025.       'n', 'q', 's',
  1026.       'T', 'v',
  1027.       'o',
  1028.       'l', 'k',
  1029.       'd',
  1030.       'p',
  1031.       '\0'
  1032.    };
  1033.    static CONST argMask_t  usage_masks[] = {
  1034.       usg_NONE,     usg_NONE,     usg_NONE,
  1035.       usg_VERBOSE,  usg_VERBOSE,
  1036.       usg_OPTS,
  1037.       usg_LONGOPTS, usg_LONGOPTS,
  1038.       usg_DESCRIPTION,
  1039.       usg_PAGED
  1040.    };
  1041.  
  1042.    usage_env = getenv("USAGECNTL");
  1043.    if ( !usage_env ) {
  1044.       usg_ctl = usg_VERBOSE;
  1045.    }
  1046.    else {
  1047.       usg_ctl = read_flags( usage_env, usage_tokens, usage_masks );
  1048.    }
  1049.  
  1050.    envfree( usage_env );
  1051.  
  1052. #ifdef unix_style
  1053.       /* make sure we print at least one of options/keywords */
  1054.    if ( !BTEST(usg_ctl, usg_OPTS|usg_LONGOPTS) ) {
  1055.       if ( BTEST(cmd_flags(cmd), pa_KWDSONLY) ) {
  1056.          BSET(usg_ctl, usg_LONGOPTS);
  1057.       }
  1058.       else {
  1059.          BSET(usg_ctl, usg_OPTS);
  1060.       }
  1061.    }
  1062. #endif /* unix_style */
  1063.  
  1064.    return  usg_ctl;
  1065. }
  1066.  
  1067.  
  1068. #ifndef LITE
  1069.  
  1070. /***************************************************************************
  1071. ** ^FUNCTION: get_parse_flags - determine user-defined parsing behavior
  1072. **
  1073. ** ^SYNOPSIS:
  1074. */
  1075. #ifndef __ANSI_C__
  1076.    static VOID get_parse_flags( cmd )
  1077. /*
  1078. ** ^PARAMETERS:
  1079. */
  1080.    ARGDESC *cmd;
  1081. /*    -- command-structure to determine parse-flags for
  1082. */
  1083. #endif  /* !__ANSI_C__ */
  1084.  
  1085. /* ^DESCRIPTION:
  1086. **    The programmer may control parsing behavior through the use of
  1087. **    parsecntl(3). The user may set this behavior through the use of the
  1088. **    PARSECNTL environment variable.  By indicating any number of flags
  1089. **    (possibly negated) the user will directly modify the behavior of the
  1090. **    parseargs library. Flags may be combined by placing a `+' or `|'
  1091. **    character in between flags. A switch is negated by immediately preceding
  1092. **    it with a `!' or `-' character.  The possible "flags" are given by
  1093. **    the following table. Flags are case-insensitive.
  1094. **
  1095. **    "Prompt"
  1096. **       Prompt the user for any missing arguments that are required on the
  1097. **       command-line. No special escaping or quoting is performed on the
  1098. **       user input. Required arguments that expect a list of values will
  1099. **       be repeatedly prompted for (one item per line) until a blank line
  1100. **       (followed by a carriage return) is entered.
  1101. **
  1102. **    "Ignore"
  1103. **       Ignore any unrecognized or improperly specified command-line arguments
  1104. **       and continue execution of the program. Normally, if a required
  1105. **       argument is unmatched (or an argument is improperly specified), a
  1106. **       usage message is printed and program execution is terminated.
  1107. **
  1108. **    "OptsOnly"
  1109. **       Under UNIX, setting this flag will disable the parsing of long-option
  1110. **       syntax. This will cause all arguments starting with '+' to always be
  1111. **       treated as a positional parameter (instead of a long-option).
  1112. **
  1113. **    "KwdsOnly"
  1114. **       Under UNIX, setting this flag disables the parsing of single-character
  1115. **       options.  This will cause all arguments starting with '-' to always be
  1116. **       treated as a positional parameter (instead of an option).
  1117. **
  1118. **    "LoptsOnly"
  1119. **       Same as KwdsOnly.
  1120. **
  1121. **    "Flags1st"
  1122. **       Setting this flag causes the parseargs library to force any and all
  1123. **       non-positional arguments to be specified before any positional ones.
  1124. **       As an example, under UNIX, if this flag is SET then parseargs will
  1125. **       consider the command line "cmd -x arg" to consist of one option and
  1126. **       one positional argument; however the command line "cmd arg -x" would
  1127. **       be considered to consist of two positional arguments (the -x option
  1128. **       will be unmatched).
  1129. **
  1130. **       If this flag is UNSET, then both of the previous examples are
  1131. **       considered to consist of one option and one positional argument.
  1132. **
  1133. **    "CaseIgnore"
  1134. **       Setting this flag causes character-case to be ignored when attempting
  1135. **       to match single-character argument names (i.e. causes "-i" and "-I"
  1136. **       will be considered equivalent).
  1137. **
  1138. **    If the environment variable "PARSECNTL" is empty or undefined, then the
  1139. **    parsing behavior set by the programmer is used.  If the programmer has
  1140. **    not explicitly used parsecntl(3) to modify the parsing behavior, then
  1141. **    the default behavior will be "Flags1st" for Unix Systems,
  1142. **    "!Prompt + !Ignore" for AmigaDOS systems, "Prompt" for VMS systems,
  1143. **    and "CaseIgnore" for MS-DOS and OS/2 systems.
  1144. **
  1145. ** ^REQUIREMENTS:
  1146. **    <cmd> must point to an array that has been declared using the CMD_XXXX
  1147. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  1148. **
  1149. ** ^SIDE-EFFECTS:
  1150. **    Modifies the parse-flags of <cmd> is $PARSECNTL is non-null & non-empty.
  1151. **
  1152. ** ^RETURN-VALUE:
  1153. **    None.
  1154. **
  1155. ** ^ALGORITHM:
  1156. **    - If $PARSECNTL is NULL or empty, return
  1157. **    - Else turn off all flags that may be set by $PARSECNTL
  1158. **    - get the value of $PARSECNTL and call read_flags to parse it.
  1159. **    - set the returned flags in the cmd-structure.
  1160. **    - return the resulting flags.
  1161. ***^^**********************************************************************/
  1162.  
  1163.    /* the combination of parse-flags that may be set via $PARSECNTL */
  1164. #define  pa_USRFLAGS \
  1165.    (pa_PROMPT|pa_IGNORE|pa_ANYCASE|pa_OPTSONLY|pa_KWDSONLY|pa_FLAGS1ST)
  1166.  
  1167. #ifdef __ANSI_C__
  1168.    static void get_parse_flags( ARGDESC *cmd )
  1169. #endif
  1170. {
  1171.    register argMask_t  flags = 0;
  1172.    char *parse_env;
  1173.    static CONST char  parse_tokens[] = {
  1174.       'c',
  1175.       'd',
  1176.       'f',
  1177.       'i',
  1178.       'p',
  1179.       'o',
  1180.       'l','k',
  1181.       '\0'
  1182.    };
  1183.    static CONST argMask_t  parse_masks[] = {
  1184.       pa_ANYCASE,
  1185.       pa_DEFAULTS,
  1186.       pa_FLAGS1ST,
  1187.       pa_IGNORE,
  1188.       pa_PROMPT,
  1189.       pa_OPTSONLY,
  1190.       pa_KWDSONLY, pa_KWDSONLY
  1191.    };
  1192.  
  1193.    if ( !CMD_isINIT(cmd) )  init_args( cmd );
  1194.  
  1195.    if ( (parse_env = getenv("PARSECNTL"))  &&  *parse_env ) {
  1196.       flags = read_flags( parse_env, parse_tokens, parse_masks );
  1197.  
  1198.          /*
  1199.          ** mask off all the flags that may be set by $PARSECNTL
  1200.          ** (including any default flags).
  1201.          */
  1202.       BCLEAR( cmd_flags(cmd), pa_USRFLAGS );
  1203.  
  1204.       cmd_flags(cmd) |= flags;
  1205.    }/*if*/
  1206.  
  1207.    envfree( parse_env );
  1208. }
  1209.  
  1210. #undef  pa_USRFLAGS
  1211.  
  1212.  
  1213. /***************************************************************************
  1214. ** ^FUNCTION: parse_user_defaults - parse user-supplied default arguments
  1215. **
  1216. ** ^SYNOPSIS:
  1217. */
  1218. #ifndef __ANSI_C__
  1219.    static VOID parse_user_defaults( cmd )
  1220. /*
  1221. ** ^PARAMETERS:
  1222. */
  1223.    ARGDESC *cmd;
  1224. /*    -- the command-line object to parse
  1225. */
  1226. #endif  /* !__ANSI_C__ */
  1227.  
  1228. /* ^DESCRIPTION:
  1229. **    Programs that use parseargs may be given default arguments under
  1230. **    Unix and PCs through the use of environment variables (symbols
  1231. **    are used for VMS systems). If a  C-program or shell-script uses
  1232. **    parseargs to implement a command named "cmd" then the environment
  1233. **    variable CMD_ARGS will be parsed for any "default" arguments before
  1234. **    the command-line is parsed.  The command-line will over-ride any
  1235. **    options that are specified in this environment variable (except that
  1236. **    ARGLISTs and ARGVECs set in "CMD_ARGS" will be appended from the
  1237. **    command-line if they are selected).
  1238. **
  1239. **    It is important to note that the contents of the CMD_ARGS environ-
  1240. **    ment variable are NOT expanded by the shell and hence any special
  1241. **    characters (such as quotes or back-slashes) will NOT be escaped or
  1242. **    removed by parseargs. Furthermore, it will not be possible to try and
  1243. **    use a tab, space, or newline character in the environment variable as
  1244. **    anything other than an argument separator.
  1245. **
  1246. **    Lastly, parts of an option specification in CMD_ARGS may NOT be
  1247. **    continued on the command-line. As an example, if -f requires an
  1248. **    argument and CMD_ARGS="-f", then the command-line "cmd  bah" will
  1249. **    NOT assign "bah" as the argument to -f but will instead complain
  1250. **    about a missing argument for -f. Similarly, if -l takes a list of
  1251. **    arguments and CMD_ARGS="-l item1 item2", then the command-line
  1252. **    "cmd  bah", will NOT assign "bah" to the end of the list containing
  1253. **    "item1" and "item2" but will instead treat "bah" as the first
  1254. **    positional parameter on the command-line.
  1255. **
  1256. ** ^REQUIREMENTS:
  1257. **    <cmd> should be an array of ARGDESCs declared using the CMD_XXXX macros
  1258. **    or with the ENDOFARGS (and possible STARTOFARGS) macros.
  1259. **
  1260. ** ^SIDE-EFFECTS:
  1261. **    Any matched arguments have their ARGDESCs modified accordingly.
  1262. **    Also, the command-state is changed to reflect the fact that the
  1263. **    environment variable has been parsed. Also, after parsing the
  1264. **    variable, the command is partially parsed. Hence the pa_CONTINUE
  1265. **    is set to prevent arguments from being reset when the actual command
  1266. **    line is parsed.
  1267. **
  1268. ** ^RETURN-VALUE:
  1269. **    None.
  1270. **
  1271. ** ^ALGORITHM:
  1272. **    - Get the value of the environment variable
  1273. **    - if the variable is null or empty then return.
  1274. **    - turn OFF the VARIABLE-NOT-YET-PARSED flag
  1275. **    - turn on the NOCHECK flag so we dont check for missing required
  1276. **      arguments in the variable (there may be more on the command-line).
  1277. **    - parse the string and convert any arguments matched
  1278. **    - set the NOCHECK flag back to its previous setting
  1279. **    - set the CONTINUE flag since the command-line is now partially parsed.
  1280. **    - return
  1281. ***^^**********************************************************************/
  1282.  
  1283. #define  ENV_SUFFIX  "_ARGS"  /* suffix for env-variable with default args */
  1284.  
  1285. #ifdef __ANSI_C__
  1286.    static VOID parse_user_defaults( ARGDESC *cmd )
  1287. #endif
  1288. {
  1289.    int  rc;
  1290.    char      *env_args;
  1291.    argName_t  env_name;
  1292.    VOID usage();
  1293.  
  1294.    if ( !CMD_isINIT(cmd) )  init_args( cmd );
  1295.  
  1296.       /* if ignoring the <CMD>_ARGS variable then just return */
  1297.    if ( BTEST(cmd_state(cmd), ps_NOCMDENV) )  return;
  1298.  
  1299.       /* build the name of the environment variable */
  1300.    strncpy( env_name, ProgName, ProgNameLen );
  1301.    env_name[ProgNameLen] = 0;
  1302.    (VOID) strupr( env_name );
  1303.    strcat( env_name, ENV_SUFFIX );
  1304.  
  1305.       /* get the value of the environment variable,
  1306.       ** split it up into tokens, and parse it.
  1307.       */
  1308.    env_args = getenv(env_name);
  1309.    if ( env_args ) {
  1310.       char **argv = (char **)NULL;
  1311.       char *env_val = strdup( env_args );
  1312.       argMask_t  saveflags = cmd_flags(cmd);
  1313.  
  1314.       BSET( cmd_flags(cmd), pa_NOCHECK | pa_ARGV0 | pa_COPYF );
  1315.       BSET( cmd_state(cmd), ps_NOCMDENV );
  1316.  
  1317.           /* split line up into whitespace separated tokens */
  1318.       if ( !strsplit( &argv, env_val, CHARNULL ) ) {
  1319.          free( argv );
  1320.          return;
  1321.       }
  1322.  
  1323. #ifdef vms_style
  1324.       BSET( cmd_state(cmd), ps_NOTCMDLINE );
  1325. #endif
  1326.  
  1327.       rc = parse_argv_style( argv, cmd );
  1328.       free( argv );
  1329.       cmd_list(cmd) = ARGDESCNULL;  /* dont allow lists to continue on */
  1330. #ifdef amiga_style
  1331.       cmd_prev(cmd) = ARGDESCNULL;
  1332. #endif
  1333.  
  1334. #ifdef vms_style
  1335.       BCLEAR( cmd_state(cmd), ps_NOTCMDLINE );
  1336. #endif
  1337.  
  1338.          /* check for errors */
  1339.       if ( rc  &&  !BTEST(cmd_flags(cmd), pa_IGNORE) ) {
  1340.          eprintf( "%.*s: syntax-error in %s \"%s\".\n",
  1341.                   ProgNameLen, ProgName, USER_VARIABLE, env_name );
  1342.          eprintf( "\t%s = \"%s\"\n\n", env_name, env_args );
  1343.          free( env_val );
  1344.          usage( cmd );
  1345.          exit( exit_SYNTAX );
  1346.       }
  1347.  
  1348.       free( env_val );
  1349.       cmd_flags(cmd) = (saveflags | pa_CONTINUE);
  1350.    }
  1351.  
  1352.    envfree( env_args );
  1353. }
  1354.  
  1355. #undef ENV_SUFFIX
  1356.  
  1357. #endif /* ! LITE */
  1358.  
  1359. /***************************************************************************
  1360. ** ^FUNCTION: parse_init -- initialize an ARGDESC for parsing
  1361. **
  1362. ** ^SYNOPSIS:
  1363. */
  1364. #ifndef __ANSI_C__
  1365.    static ARGDESC *parse_init( argdp )
  1366. /*
  1367. ** ^PARAMETERS:
  1368. */
  1369.    ARGDESC *argdp[];
  1370. /*    -- pointer to the argument descriptor array.
  1371. */
  1372. #endif  /* !__ANSI_C__ */
  1373.  
  1374. /* ^DESCRIPTION:
  1375. **    Parse_init will perform the usual pre-parsing actions such as checking
  1376. **    $PARSECNTL, parsing $<NAME>_ARGS, and resetting the command-line.
  1377. **
  1378. ** ^REQUIREMENTS:
  1379. **    <argdp> should point to an array of ARGDESCs declared using the CMD_XXXX
  1380. **    macros or with the ENDOFARGS (and possible STARTOFARGS) macros.
  1381. **
  1382. ** ^SIDE-EFFECTS:
  1383. **    Initialize argd and parses any default arguments.
  1384. **
  1385. ** ^RETURN-VALUE:
  1386. **    pointer to the arg-descriptors
  1387. **
  1388. ** ^ALGORITHM:
  1389. **    - check for a NULL argdesc-array
  1390. **    - initialize the command-object if necessary
  1391. **    - set ProgName
  1392. **    - read $PARSECNTL
  1393. **    - reset the command-line if necessary (and parse $<NAME>_ARGS)
  1394. **    - return the command-object
  1395. ***^^**********************************************************************/
  1396. #ifdef __ANSI_C__
  1397.    static ARGDESC *parse_init( ARGDESC *argdp[] )
  1398. #endif
  1399. {
  1400.    register ARGDESC *cmd;
  1401.    BOOL  unnamed = FALSE;
  1402.  
  1403.       /* allow null argument descriptor */
  1404.    if ( !(*argdp) )  *argdp = Empty_ArgDesc;
  1405.  
  1406.       /* initialize command-structure */
  1407.    if ( !CMD_isINIT(*argdp) )  init_args( *argdp );
  1408.    cmd = *argdp;
  1409.  
  1410.       /* save the name of this program (for error messages) */
  1411.    if ( cmd_argv0(cmd)  &&  *(cmd_argv0(cmd)) ) {
  1412.       ProgName = cmd_argv0(cmd);
  1413.       ProgNameLen = get_argpfx(ProgName);
  1414.    }
  1415.    else if ( cmd_name(cmd)  &&  *(cmd_name(cmd)) ) {
  1416.       ProgName = cmd_name(cmd);
  1417.       ProgNameLen = get_argpfx(ProgName);
  1418.    }
  1419.    else {
  1420.       unnamed = TRUE;
  1421.    }
  1422.  
  1423. #ifndef LITE
  1424.       /* read PARSECNTL environment variable */
  1425.    if ( !BTEST(cmd_state(cmd), ps_NOPARSECNTL) ) {
  1426.       get_parse_flags(*argdp);
  1427.    }
  1428. #endif
  1429.  
  1430.       /* reset argd if necessary */
  1431.    if ( !BTEST(cmd_flags(cmd), pa_CONTINUE) )  {
  1432.       reset_args( *argdp );
  1433.  
  1434. #ifndef LITE
  1435.          /* get any options from <CMDNAME>_ARGS environment variable */
  1436.       if ( !BTEST(cmd_flags(cmd), pa_NOCMDENV)  &&  !unnamed )  {
  1437.          parse_user_defaults( *argdp );
  1438.       }
  1439. #endif
  1440.    }
  1441.  
  1442.    return  *argdp;
  1443. }
  1444.  
  1445.  
  1446. /***************************************************************************
  1447. ** ^FUNCTION: usage -- print a usage message
  1448. **
  1449. ** ^SYNOPSIS:
  1450. */
  1451. #ifndef __ANSI_C__
  1452.    VOID usage( argd )
  1453. /*
  1454. ** ^PARAMETERS:
  1455. */
  1456.    ARGDESC argd[];
  1457. /*    -- the description of expected arguments.
  1458. */
  1459. #endif  /* !__ANSI_C__ */
  1460.  
  1461. /* ^DESCRIPTION:
  1462. **    Given an argdesc array, usage will print the usage for the given
  1463. **    command in the format specified by the user's USAGECNTL environment
  1464. **    variable.
  1465. **
  1466. ** ^REQUIREMENTS:
  1467. **    <argd> should be an array of ARGDESCs declared using the CMD_XXXX macros
  1468. **    or with the ENDOFARGS (and possible STARTOFARGS) macros.
  1469. **
  1470. ** ^SIDE-EFFECTS:
  1471. **    Prints on stderr.
  1472. **
  1473. ** ^RETURN-VALUE:
  1474. **    None.
  1475. **
  1476. ** ^ALGORITHM:
  1477. **    - initialize the command-object
  1478. **    - set ProgName
  1479. **    - read $USAGECNTL
  1480. **    - call <os>_usage()
  1481. ***^^**********************************************************************/
  1482. #ifdef __ANSI_C__
  1483.    void usage( const ARGDESC argd[] )
  1484. #endif
  1485. {
  1486.    register CONST ARGDESC *cmd;
  1487.    argMask_t  usg_ctl = 0;
  1488.  
  1489.       /* allow null argument descriptor */
  1490.    if ( !argd )  argd = Empty_ArgDesc;
  1491.  
  1492.    if ( !CMD_isINIT(argd) )   init_args( (ARGDESC *)argd );
  1493.    cmd = argd;
  1494.  
  1495.    if ( cmd_argv0(cmd) && *(cmd_argv0(cmd)) ) {
  1496.       ProgName = cmd_argv0(cmd);
  1497.       ProgNameLen = get_argpfx(ProgName);
  1498.    }
  1499.    else if ( cmd_name(cmd) && *(cmd_name(cmd)) ) {
  1500.       ProgName = cmd_name(cmd);
  1501.       ProgNameLen = get_argpfx(ProgName);
  1502.    }
  1503.  
  1504.    usg_ctl = get_usage_flags(cmd);
  1505.    if ( !BTEST(usg_ctl, usg_NONE) )  print_usage_style( argd, usg_ctl );
  1506. }
  1507.  
  1508.  
  1509. /***************************************************************************
  1510. ** ^FUNCTION: parsecntl - control various aspects of command-line parsing
  1511. **
  1512. ** ^SYNOPSIS:
  1513. */
  1514. #ifndef __ANSI_C__
  1515.    int  parsecntl( argd, cntl, mode, va_alist )
  1516. /*
  1517. ** ^PARAMETERS:
  1518. */
  1519.    ARGDESC *argd;
  1520. /*    --  The command-object to operate upon
  1521. */
  1522.    parsecntl_t cntl;
  1523. /*    --  The control-code corresponding to the desired operation
  1524. */
  1525.    parsemode_t mode;
  1526. /*    --  The mode of the operation (read, write, or both)
  1527. */
  1528.    va_dcl
  1529. /*    -- any further args followed by the item to be read/written
  1530. */
  1531. #endif  /* !__ANSI_C__ */
  1532.   
  1533. /* ^DESCRIPTION:
  1534. **    Parsecntl will read and/or write the desired attributes of the given
  1535. **    command-object. The attributes to be operated upon are specified by
  1536. **    the second parameter to parsecntl. The desired mode (read, write, or
  1537. **    both) are specified by the third parameter to parsecntl. If the
  1538. **    operation to be performed is pc_ARGFLAGS, then the fourth argument to
  1539. **    parsecntl should be the keyword name of the argument whose flags are to
  1540. **    be retrieved.  The last parameter to parsecntl is always the object to
  1541. **    contain the attribute(s) to be read/written.  If the attribute(s) are
  1542. **    to be read (regardless of whether or not they are also being changed)
  1543. **    then the last argument should be a pointer to an object, otherwise the
  1544. **    last argument should be the object itself.
  1545. **
  1546. **    If mode is pc_READ, then the desired attributes are copied into the
  1547. **    object pointed to by the last parameter. If the mode is pc_WRITE, then
  1548. **    the attributes from the last parameter are copied to the command-
  1549. **    object. If mode is pc_RDWR, then the attributes pointed to by the last
  1550. **    parameter are copied to the command-object, and then the previous
  1551. **    value of these attributes (before they were overwritten) is copied
  1552. **    into the object pointed to by the last parameter.
  1553. **
  1554. **    If cntl is pc_ARGFLAGS, then the only valid mode pc_READ. All other
  1555. **    attributes may be written or read by parsecntl.
  1556. **
  1557. ** ^REQUIREMENTS:
  1558. **    If mode is READ or READ+WRITE then the last argument should be the
  1559. **    address of the desired object, otherwise it should be the object itself.
  1560. **
  1561. ** ^SIDE-EFFECTS:
  1562. **    None if the mode is READ, otherwise, the desired attibutes are (re)set
  1563. **
  1564. ** ^RETURN-VALUE:
  1565. **    pe_SYSTEM
  1566. **       -- If a system error occurred
  1567. ** 
  1568. **    pe_SUCCESS
  1569. **       -- success, no errors encountered.
  1570. ** 
  1571. **    pe_DEFARGS
  1572. **       -- an attempt (using parsecntl()) was made to change the
  1573. **          default arg-search list of a command to point to an argdesc-array
  1574. **          which already has the given command on its default arg-search list
  1575. **          (which would cause an infinite loop when attempting to match an
  1576. **          unknown command-line argument).
  1577. ** 
  1578. **    pe_NOMATCH
  1579. **       -- unable to match the named argument for the pc_ARGFLAGS cntl
  1580. ** 
  1581. **    pe_BADMODE
  1582. **       -- bad mode for given command in parsecntl()
  1583. ** 
  1584. **    pe_BADCNTL
  1585. **       -- bad command for parsecntl
  1586. **
  1587. ** ^ALGORITHM:
  1588. **    - if cntl is pc_ARGFLAGS
  1589. **      - if mode is pc_WRITE or pc_RDWR then return pe_BADMODE
  1590. **      - get the argument name and try to match it.
  1591. **      - if no match, then return pe_NOMATCH
  1592. **      - copy the flags for the matched argument
  1593. **      - return pe_SUCCESS
  1594. **    - else if cntl is pc_PARSEFLAGS
  1595. **      - save the current parseflags.
  1596. **      - if requested, (re)set the current flags
  1597. **      - if requested, copy the old flags
  1598. **      - return pe_SUCCESS
  1599. **    - else if cntl is pc_DEFARGS
  1600. **      - save the current default-args
  1601. **      - make sure the given default-args does not already contain argd
  1602. **      - if the above assertion failed, return pe_DEFARGS
  1603. **      - if requested, (re)set the default-args
  1604. **      - if requested, copy the old default-args
  1605. **      - return pe_SUCCESS
  1606. **    - else ( cntl must be in {pc_NAME, pc_PURPOSE, pc_DESCRIPTION} )
  1607. **      - save the current setting of the attribute-string
  1608. **      - if requested, (re)set the current attribute-string
  1609. **      - if requested, copy the old attribute-string
  1610. **      - return pe_SUCCESS
  1611. **      endif
  1612. ***^^**********************************************************************/
  1613.  
  1614.    /*
  1615.    ** define some convenience macros to determine if we are
  1616.    ** reading/writing from/to the argdesc-array.
  1617.    */
  1618. #define isREADING(pmode)  (pmode == pc_READ  || pmode == pc_RDWR)
  1619. #define isWRITING(pmode)  (pmode == pc_WRITE || pmode == pc_RDWR)
  1620.  
  1621. #ifdef __ANSI_C__
  1622.    int parsecntl( ARGDESC *argd, parsecntl_t cntl, parsemode_t mode, ... )
  1623. #endif
  1624. {
  1625.    register ARGDESC  *cmd;
  1626.    register int  rc = pe_SUCCESS;
  1627.    va_list   ap;
  1628.  
  1629.    if ( !argd )   return   rc;
  1630.    if ( !CMD_isINIT(argd) )   init_args(argd);
  1631.    cmd = argd;
  1632.    VA_START( ap, mode );   /* get argument to match */
  1633.  
  1634.       /* now figure out what to do and go do it! */
  1635.    switch( cntl ) {
  1636.       case  pc_ARGFLAGS :  /* get/set arg-flags */
  1637.          {
  1638.             register ARGDESC *ad, *args;
  1639.             char *name = VA_ARG( ap, char * );
  1640.             argMask_t  *argflags;
  1641.             BOOL  is_match = FALSE;
  1642.  
  1643.                /* first we have to find the argument whose flags we need */
  1644.             for (args = argd ; args  &&  !is_match ; args = cmd_defargs(args)) {
  1645.                for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
  1646.                   if ( arg_type(ad) == argDummy )  continue;
  1647.  
  1648.                   if ( match(name, arg_sname(ad)) == 0 ) {
  1649.                      is_match = TRUE;
  1650.                      break;
  1651.                   }/*if*/
  1652.                }/*foreach arg*/
  1653.             }/*foreach argdesc*/
  1654.  
  1655.             if ( !is_match ) {
  1656.                VA_END(ap);
  1657.                return  pe_NOMATCH;
  1658.             }
  1659.  
  1660.                /* now that we found it - retrieve the argument flags */
  1661.             if ( isREADING(mode) ) {
  1662.               argflags = VA_ARG( ap, argMask_t * );
  1663.               *argflags = arg_flags(ad);
  1664.             }
  1665.             else {
  1666.               rc = pe_BADMODE;  /* parsecntl() wont set ARGFLAGS */
  1667.             }
  1668.          }/*block*/
  1669.          break;
  1670.  
  1671.       case  pc_PARSEFLAGS : /* get/set the parse-flags */
  1672.          {
  1673.             argMask_t *pflags, flags;
  1674.  
  1675.                /* get value from call-stack (dependent on the mode) */
  1676.             if ( isREADING(mode) ) {
  1677.               pflags = VA_ARG( ap, argMask_t * );
  1678.               flags = cmd_flags(cmd);
  1679.             }
  1680.             else {
  1681.               flags = (argMask_t) VA_ARG( ap, int );
  1682.               pflags = &flags;
  1683.             }
  1684.  
  1685.                /* perform the desired action(s) */
  1686.             if ( isWRITING(mode) )  cmd_flags(cmd) = *pflags;
  1687.             if ( isREADING(mode) )  *pflags = flags;
  1688.          }/*block*/
  1689.          break;
  1690.  
  1691.       case  pc_DEFARGS : /* get/set the default arguments */
  1692.          {
  1693.             ARGDESC **pdefargd, *defargd;
  1694.  
  1695.                /* get value from call-stack (dependent on the mode) */
  1696.             if ( isREADING(mode) ) {
  1697.                pdefargd = VA_ARG( ap, ARGDESC ** );
  1698.                defargd = cmd_defargs(cmd);
  1699.             }
  1700.             else {
  1701.                defargd = VA_ARG( ap, ARGDESC * );
  1702.                pdefargd = &defargd;
  1703.             }
  1704.  
  1705.             if ( isWRITING(mode) ) {
  1706.                ARGDESC *args;
  1707.  
  1708.                if ( !CMD_isINIT(*pdefargd) )  init_args( *pdefargd );
  1709.  
  1710.                   /* make sure we are not on the default-argdesc's
  1711.                   ** default-argument hierarchy (or an infinite loop
  1712.                   ** will result).
  1713.                   */
  1714.                for ( args = *pdefargd; rc && args; args = cmd_defargs(args) ) {
  1715.                   if ( args == cmd )  rc = pe_DEFARGS;
  1716.                }
  1717.                if ( !rc )  cmd_defargs(cmd) = *pdefargd;  /* set new defaults */
  1718.             }/*if*/
  1719.  
  1720.             if ( isREADING(mode) )  *pdefargd = defargd;
  1721.          }
  1722.          break;
  1723.  
  1724.       case  pc_NAME :  /* get/set name */
  1725.       case  pc_PURPOSE :  /* get/set purpose */
  1726.       case  pc_DESCRIPTION :  /* get/set description */
  1727.          {
  1728.             CONST char *str, **pstr;
  1729.  
  1730.                /* get value from call-stack (dependent on the mode) */
  1731.             if ( isREADING(mode) ) {
  1732.                pstr = VA_ARG( ap, CONST char ** );
  1733.                if      ( cntl == pc_NAME ) {
  1734.                   if ( !BTEST(cmd_state(cmd), ps_USERNAME|ps_FREENAME) ) {
  1735.                      if ( cmd_name(argd) ) {
  1736.                         int  n;
  1737.                         char  *p;
  1738.  
  1739.                         n = get_argpfx( (char *)cmd_name(argd) );
  1740.                         p = (char *)malloc( n+1 );
  1741.                         if ( !p ) {
  1742.                            syserr( "malloc failed in parsecntl()" );
  1743.                         }
  1744.                         strncpy(p, (char *)cmd_name(argd), n);
  1745.                         p[n] = 0;
  1746.                         cmd_name(argd) = p;
  1747.                         BSET(cmd_state(cmd), ps_FREENAME);
  1748.                      }
  1749.                   }
  1750.                   str = cmd_name(cmd);
  1751.                }
  1752.                else if ( cntl == pc_PURPOSE ) {
  1753.                   if ( !BTEST(cmd_state(cmd), ps_USERPURPOSE|ps_FREEPURPOSE) ) {
  1754.                      if ( cmd_purpose(argd) ) {
  1755.                         int  n;
  1756.                         char  *p, *q;
  1757.  
  1758.                         p = get_argdesc( (char *)cmd_purpose(argd), &n );
  1759.                         if ( p ) {
  1760.                            q = (char *)malloc( n+1 );
  1761.                            if ( !q ) {
  1762.                               syserr( "malloc failed in parsecntl()" );
  1763.                            }
  1764.                            strncpy(q, p, n);
  1765.                            q[n] = 0;
  1766.                            p = q;
  1767.                            BSET(cmd_state(cmd), ps_FREEPURPOSE);
  1768.                         }
  1769.                         cmd_purpose(cmd) = p;
  1770.                      }
  1771.                   }
  1772.                   str = cmd_purpose(cmd);
  1773.                }
  1774.                else /* cntl == pc_DESCRIPTION */  str = cmd_description(cmd);
  1775.             }
  1776.             else {
  1777.                str = VA_ARG( ap, CONST char * );
  1778.                pstr = &str;
  1779.             }
  1780.  
  1781.                /* perform the desired action(s) */
  1782.             if ( isWRITING(mode) )  {
  1783.                if ( cntl == pc_NAME ) {
  1784.                   if ( BTEST(cmd_state(cmd), ps_FREENAME) )
  1785.                      free( cmd_name(cmd) );
  1786.                      BCLEAR( cmd_state(cmd), ps_FREENAME );
  1787.                      BSET( cmd_state(cmd), ps_USERNAME );
  1788.                      cmd_name(cmd) = *pstr;
  1789.                   }
  1790.                else if ( cntl == pc_PURPOSE ) {
  1791.                   if ( BTEST(cmd_state(cmd), ps_FREEPURPOSE) ) {
  1792.                      free( cmd_purpose(cmd) );
  1793.                   }
  1794.                   BCLEAR( cmd_state(cmd), ps_FREEPURPOSE );
  1795.                   BSET( cmd_state(cmd), ps_USERPURPOSE );
  1796.                   cmd_purpose(cmd) = *pstr;
  1797.                }
  1798.                else /* cntl == pc_DESCRIPTION */  cmd_description(cmd) = *pstr;
  1799.             }
  1800.             if ( isREADING(mode) )  *pstr = str;
  1801.          }/*block*/
  1802.          break;
  1803.  
  1804.       default :
  1805.          rc = pe_BADCNTL;
  1806.          break;
  1807.    }/*switch*/
  1808.  
  1809.    VA_END( ap );
  1810.    return   rc;
  1811. }
  1812.  
  1813. #undef  isREADING
  1814. #undef  isWRITING
  1815.  
  1816.  
  1817. #ifndef LITE
  1818.  
  1819. /***************************************************************************
  1820. ** ^FUNCTION: sparseargs - parse arguments in a string
  1821. **
  1822. ** ^SYNOPSIS:
  1823. */
  1824. #ifndef __ANSI_C__
  1825.    int sparseargs( str, argd )
  1826. /*
  1827. ** ^PARAMETERS:
  1828. */
  1829.    char    *str;
  1830. /*    -- string to parse
  1831. */
  1832.    ARGDESC *argd;
  1833. /*    -- pointer to argument descriptor table
  1834. */
  1835. #endif  /* !__ANSI_C__ */
  1836.   
  1837. /* ^DESCRIPTION:
  1838. **    Given a single string and an argdesc array, sparseargs will parse
  1839. **    arguments from a string in the same manner as parseargs.
  1840. **    Sparseargs will split the given string up into a vector of whitespace
  1841. **    separated tokens and then attempt to parse the resultant vector as if
  1842. **    it were given as argv[] on the command-line.  NO special treatment is
  1843. **    given to characters such as single-quotes, double-quotes, or anything
  1844. **    else. Sparseargs will always assume that any whitespace characters are
  1845. **    intended as argument separators.
  1846. **
  1847. ** ^REQUIREMENTS:
  1848. **    <str> should be non-NULL and non-empty
  1849. **
  1850. ** ^SIDE-EFFECTS:
  1851. **    <str> is modified by strsplit().
  1852. **    <argd> is modified accordingly as arguments are matched.
  1853. **
  1854. ** ^RETURN-VALUE:
  1855. **    pe_SYSTEM
  1856. **       -- If a system error occurred
  1857. ** 
  1858. **    pe_SUCCESS
  1859. **       -- success, no errors encountered.
  1860. ** 
  1861. **    pe_SYNTAX
  1862. **       -- If a syntax error occurs in <str>
  1863. **
  1864. ** ^ALGORITHM:
  1865. **    - save current parse-flags
  1866. **    - add pa_ARGV0 to current parse-flags
  1867. **    - split string up into a vector of tokens
  1868. **    - call parse init and then parse the arguments
  1869. **    - if syntax-error, print usage and exit
  1870. **    - restore parse-flags
  1871. **    - return the status from parsing the vector
  1872. ***^^**********************************************************************/
  1873. #ifdef __ANSI_C__
  1874.    int sparseargs( char *str, ARGDESC *argd )
  1875. #endif
  1876. {
  1877.    argMask_t  saveflags;
  1878.    char **argv;
  1879.    int  rc = 0;
  1880.  
  1881.    if ( !argd )  return  pe_SUCCESS;
  1882.  
  1883.    if ( !CMD_isINIT(argd) )  init_args(argd);
  1884.  
  1885.       /* save old flags & initialize set parse flags */
  1886.    saveflags = cmd_flags(argd);
  1887.    BSET(cmd_flags(argd), pa_ARGV0);
  1888.  
  1889.       /* split line up into whitespace separated tokens */
  1890.    (void) strsplit( &argv, str, CHARNULL );
  1891.  
  1892. #ifdef vms_style
  1893.    BSET( cmd_state(argd), ps_NOTCMDLINE );
  1894. #endif
  1895.  
  1896.    rc = parse_argv_style( argv, parse_init( &argd ) );
  1897.    free( argv );
  1898.  
  1899. #ifdef vms_style
  1900.    BCLEAR( cmd_state(argd), ps_NOTCMDLINE );
  1901. #endif
  1902.  
  1903.       /* scan for missing required arguments */
  1904.    if ( SYNTAX_ERROR(rc, argd) ) {
  1905.       fputc( '\n', stderr );
  1906.       usage( argd );
  1907.       exit( exit_SYNTAX );
  1908.    }
  1909.  
  1910.       /* reset previous parse flags */
  1911.    cmd_flags(argd)  =  saveflags;
  1912.  
  1913.    return  rc;
  1914. }
  1915.  
  1916.  
  1917. /***************************************************************************
  1918. ** ^FUNCTION: fparseargs - parse arguments from a file
  1919. **
  1920. ** ^SYNOPSIS:
  1921. */
  1922. #ifndef __ANSI_C__
  1923.    int fparseargs( fp, argd )
  1924. /*
  1925. ** ^PARAMETERS:
  1926. */
  1927.    FILE    *fp;
  1928. /*    -- pointer to file to read (must already be open)
  1929. */
  1930.    ARGDESC *argd;
  1931. /*    -- pointer to argument descriptor table
  1932. */
  1933. #endif  /* !__ANSI_C__ */
  1934.  
  1935. /* ^DESCRIPTION:
  1936. **    Given a readable input stream and an argdesc array, fparseargs will
  1937. **    parse arguments in a file in the same manner as parseargs.  A
  1938. **    maximum-line length of 255 characters is imposed.  NO "escaping" of
  1939. **    any kind is performed. Comments of a limited form are permitted: if
  1940. **    the first non-whitespace character on a line is a '#' (or '!' for VMS)
  1941. **    then that entire line is considered a comment and is ignored.  If a
  1942. **    value is provided for an argument that is NOT a list or a vector, then
  1943. **    the value MUST be on the same line as the argument (in other words,
  1944. **    "-v val" is fine but "-v\nval" is a not).
  1945. **
  1946. ** ^REQUIREMENTS:
  1947. **    <fp> should be non-NULL, already opened-for-reading, file-pointer
  1948. **
  1949. ** ^SIDE-EFFECTS:
  1950. **    <argd> is modified accordingly as arguments are matched.
  1951. **
  1952. ** ^RETURN-VALUE:
  1953. **    pe_SYSTEM
  1954. **       -- If a system error occurred
  1955. ** 
  1956. **    pe_SUCCESS
  1957. **       -- success, no errors encountered.
  1958. ** 
  1959. **    pe_SYNTAX
  1960. **       -- if a syntax error occurs in the file
  1961. **
  1962. ** ^ALGORITHM:
  1963. **    - save current parse-flags
  1964. **    - add pa_ARGV0 to current parse-flags
  1965. **    - add pa_NOCHECK to current parse-flags (dont check until eof)
  1966. **    - call parse init
  1967. **    - parse the first line of the file
  1968. **    - add pa_CONTINUE to current parse-flags
  1969. **    - parse the rest of the file
  1970. **    - restore parse-flags
  1971. **    - now check for missing args if required
  1972. **    - if syntax-error, print usage and exit
  1973. **    - return
  1974. ***^^**********************************************************************/
  1975.  
  1976. #ifdef vms_style
  1977. #  define  c_COMMENT  '!'
  1978. #else
  1979. #  define  c_COMMENT  '#'
  1980. #endif
  1981.  
  1982. #ifdef __ANSI_C__
  1983.    int fparseargs( FILE *fp, ARGDESC *argd )
  1984. #endif
  1985. {
  1986.    int  rc;
  1987.    char  text[ MAXLINE ];
  1988.    argMask_t  saveflags;
  1989.  
  1990.    if ( !argd )  return  0;
  1991.  
  1992.    if ( !CMD_isINIT(argd) )  init_args(argd);
  1993.  
  1994.       /* We want the following scenario for the various calls to sparseargs():
  1995.       ** assume there are N lines in the input file. Then there will be N
  1996.       ** calls to sparseargs(). For all calls, we want pa_ARGV0 and pa_COPYF
  1997.       ** to be ON. Then for the ALL but the very last call, we want pa_NOCHECK
  1998.       ** to be off. For all but the very first call - we want pa_CONTINUE to
  1999.       ** be turned ON. So we have the following table:
  2000.       **
  2001.       **     Parse     ||           invocation of sparseargs
  2002.       **      Flag     ||   0    |     1     |     2 .. N-1    |     N
  2003.       **   ============++========+===========+=================+=============== 
  2004.       **      ARGV0    ||   on   |    on     |       on        |     on
  2005.       **   ------------++--------+-----------+-----------------+--------------- 
  2006.       **      COPYF    ||   on   |    on     |       on        |     on
  2007.       **   ------------++--------+-----------+-----------------+--------------- 
  2008.       **      NOCHECK  ||   on   |    on     |       on        |     OFF
  2009.       **   ------------++--------+-----------+-----------------+--------------- 
  2010.       **      CONTINUE ||   OFF  |    on     |       on        |     on
  2011.       **   ============++========+===========+=================+=============== 
  2012.       */
  2013.       
  2014.       /* save old flags & initialize parse flags for first call */
  2015.    saveflags = cmd_flags(argd);
  2016.    BSET(cmd_flags(argd), pa_ARGV0 | pa_NOCHECK | pa_COPYF);
  2017.  
  2018.    while ( !feof( fp ) ) {
  2019.       if ( !fgets( text, MAXLINE, fp ) ) {
  2020.          if ( ferror( fp ) )  {
  2021.             cmd_flags(argd)  =  saveflags;
  2022.             return  pe_SYSTEM;
  2023.          }
  2024.       }/*if*/
  2025.  
  2026.          /* trim leading and trailing whitespace and check for comments */
  2027.       (VOID) strtrim( text, CHARNULL );
  2028.       if ( !text  ||  !(*text)  ||  *text == c_COMMENT )  continue;
  2029.  
  2030.       rc = sparseargs( text, argd );
  2031.  
  2032.          /* set up parseflags for next call */
  2033.       if ( !BTEST(cmd_flags(argd), pa_CONTINUE) ) {
  2034.          BSET(cmd_flags(argd), pa_CONTINUE);
  2035.       }
  2036.    }/*while !EOF*/
  2037.  
  2038.    if ( !BTEST(saveflags, pa_NOCHECK) )  BCLEAR(cmd_flags(argd), pa_NOCHECK);
  2039.  
  2040.       /* scan for missing required args */
  2041.    if ( SYNTAX_ERROR(rc, argd) ) {
  2042.       fputc('\n', stderr);
  2043.       usage( argd );
  2044.       exit( exit_SYNTAX );
  2045.    }
  2046.  
  2047.       /* reset previous parse flags */
  2048.    cmd_flags(argd)  =  saveflags;
  2049.  
  2050.    return  rc;
  2051. }
  2052.  
  2053. #undef  c_COMMENT
  2054.  
  2055.  
  2056. /***************************************************************************
  2057. ** ^FUNCTION: lparseargs - parse arguments from a list
  2058. **
  2059. ** ^SYNOPSIS:
  2060. */
  2061. #ifndef __ANSI_C__
  2062.    int lparseargs( argls, argd )
  2063. /*
  2064. ** ^PARAMETERS:
  2065. */
  2066.    ArgList *argls;
  2067. /*    -- linked list of args to parse
  2068. */
  2069.    ARGDESC *argd;
  2070. /*    -- pointer to argument descriptor table
  2071. */
  2072. #endif  /* !__ANSI_C__ */
  2073.   
  2074. /* ^DESCRIPTION:
  2075. **    Given an ArgList and an argdesc array, lparseargs will parse arguments
  2076. **    in an ArgList in the same manner as parseargs.
  2077. **
  2078. ** ^REQUIREMENTS:
  2079. **    <argls> should be an ArgList of strings
  2080. **
  2081. ** ^SIDE-EFFECTS:
  2082. **    <argd> is modified accordingly as arguments are matched.
  2083. **
  2084. ** ^RETURN-VALUE:
  2085. **    pe_SYSTEM
  2086. **       -- If a system error occurred
  2087. ** 
  2088. **    pe_SUCCESS
  2089. **       -- success, no errors encountered.
  2090. ** 
  2091. **    pe_SYNTAX
  2092. **       -- if a syntax error occurs
  2093. **
  2094. ** ^ALGORITHM:
  2095. **    - save current parse-flags
  2096. **    - add pa_ARGV0 to current parse-flags
  2097. **    - make a vector out of the ArgList
  2098. **    - call parse init
  2099. **    - restore parse-flags
  2100. **    - if syntax-error, print usage and exit
  2101. **    - return
  2102. ***^^**********************************************************************/
  2103. #ifdef __ANSI_C__
  2104.    int lparseargs( ArgList *argls, ARGDESC *argd )
  2105. #endif
  2106. {
  2107.    int   i, argc = 0, rc = 0;
  2108.    char **argv = (char **)NULL;
  2109.    argMask_t   saveflags;
  2110.    register    ArgList *ls;
  2111.  
  2112.    if ( !argd )  return  0;
  2113.    if ( !CMD_isINIT(argd) )   init_args(argd);
  2114.  
  2115.       /* make 1st pass to count the args */
  2116.    for ( ls = argls; ls ; ls = L_NEXT(ls) ) {
  2117.       argc++;
  2118.    }
  2119.  
  2120.       /* allocate a NULL terminated arg-vector */
  2121.    argv = (char **)malloc( (argc + 1) * sizeof(char *) );
  2122.    if ( !argv )  return  pe_SYSTEM;
  2123.    argv[ argc ] = CHARNULL;
  2124.  
  2125.       /* make 2nd pass to assign the elements of the vector */
  2126.    for ( ls = argls, i = 0 ; ls ; ls = L_NEXT(ls), i++ ) {
  2127.       argv[i] = L_STRING(ls);
  2128.    }
  2129.  
  2130. #ifdef vms_style
  2131.    BSET( cmd_state(argd), ps_NOTCMDLINE );
  2132. #endif
  2133.  
  2134.       /* parse the list */
  2135.    saveflags = cmd_flags(argd);
  2136.    BSET(cmd_flags(argd), pa_ARGV0);
  2137.    rc = parse_argv_style( argv, parse_init( &argd ) );
  2138.    free( argv );
  2139.  
  2140. #ifdef vms_style
  2141.    BCLEAR( cmd_state(argd), ps_NOTCMDLINE );
  2142. #endif
  2143.  
  2144.       /* scan for missing required arguments */
  2145.    if ( SYNTAX_ERROR(rc, argd) ) {
  2146.       fputc( '\n', stderr );
  2147.       usage( argd );
  2148.       exit( exit_SYNTAX );
  2149.    }
  2150.  
  2151.       /* reset previous parse-flags */
  2152.    cmd_flags(argd) = saveflags;
  2153.  
  2154.    return   rc;
  2155. }
  2156.  
  2157.  
  2158. /***************************************************************************
  2159. ** ^FUNCTION: vparseargs - parse a variable-argument list
  2160. **
  2161. ** ^SYNOPSIS:
  2162. */
  2163. #ifndef __ANSI_C__
  2164.    int vparseargs( argd, argc, va_alist )
  2165. /*
  2166. ** ^PARAMETERS:
  2167. */
  2168.    ARGDESC  *argd;
  2169. /*    -- 
  2170. */
  2171.    int  argc;
  2172. /*    -- number of arguments to parse
  2173. */
  2174.    va_dcl
  2175. /*    -- the variable-list of arguments to parse
  2176. */
  2177. #endif  /* !__ANSI_C__ */
  2178.  
  2179. /* ^DESCRIPTION:
  2180. **    Vparseargs takes an argdesc array, the number of arguments to parse,
  2181. **    and a (possibly NULL terminated) list of argument-strings and parses
  2182. **    them in the same manner as parseargs.  Unlike sparseargs,
  2183. **    vparseargs assumes that all parameters are already split up into
  2184. **    tokens, hence any whitespace characters contained in any of the
  2185. **    string-parameters are used as is (and will be considered a part of
  2186. **    an argument name or value).
  2187. **
  2188. **
  2189. ** ^REQUIREMENTS:
  2190. **    argc must contain the number of arguments to be parsed (NOT including
  2191. **    any terminating NULL pointer).  If a NULL pointer is given as one of
  2192. **    the arguments, and this NULL pointer appears before argc indicated
  2193. **    the last argument would appear, then the NULL pointer will end the
  2194. **    the list of arguments and argc is ignored.
  2195. **    
  2196. ** ^SIDE-EFFECTS:
  2197. **    <argd> is modified accordingly as arguments are matched.
  2198. **
  2199. ** ^RETURN-VALUE:
  2200. **    pe_SYSTEM
  2201. **       -- If a system error occurred
  2202. ** 
  2203. **    pe_SUCCESS
  2204. **       -- success, no errors encountered.
  2205. ** 
  2206. **    pe_SYNTAX
  2207. **       -- if a syntax error occurs
  2208. **
  2209. ** ^ALGORITHM:
  2210. **    - save current parse-flags
  2211. **    - add pa_ARGV0 to current parse-flags
  2212. **    - make a vector out of the variable list
  2213. **    - call parse init
  2214. **    - restore parse-flags
  2215. **    - if syntax-error, print usage and exit
  2216. **    - return
  2217. ***^^**********************************************************************/
  2218. #ifdef __ANSI_C__
  2219.    int vparseargs( ARGDESC *argd, int argc, ... )
  2220. #endif
  2221. {
  2222.    register char   *arg;
  2223.    int   i, rc = 0;
  2224.    argMask_t   saveflags;
  2225.    char **argv = (char **)NULL;
  2226.    va_list   ap;
  2227.  
  2228.    if ( !argd )   return   0;
  2229.    if ( !CMD_isINIT(argd) )   init_args(argd);
  2230.  
  2231.    saveflags = cmd_flags(argd);
  2232.    BSET(cmd_flags(argd), pa_ARGV0 | pa_COPYF);
  2233.  
  2234.       /* allocate a NULL terminated arg-vector */
  2235.    argv = (char **) malloc( (argc + 1) * sizeof(char *) );
  2236.    if ( !argv )  return  pe_SYSTEM;
  2237.    argv[ argc ] = CHARNULL;
  2238.  
  2239.       /* assign the string into the array */
  2240.    VA_START(ap, argc);
  2241.    for ( i = 0; i < argc  &&  (arg = VA_ARG(ap, char *)) ; i++ ) {
  2242.       argv[i] = arg;
  2243.    }
  2244.    VA_END(ap);
  2245.  
  2246. #ifdef vms_style
  2247.    BSET( cmd_state(argd), ps_NOTCMDLINE );
  2248. #endif
  2249.  
  2250.       /* parse the arguments */
  2251.    rc = parse_argv_style( argv, parse_init( &argd ) );
  2252.    free( argv );
  2253.  
  2254. #ifdef vms_style
  2255.    BCLEAR( cmd_state(argd), ps_NOTCMDLINE );
  2256. #endif
  2257.  
  2258.       /* scan for missing required arguments */
  2259.    if ( SYNTAX_ERROR(rc, argd) ) {
  2260.       fputc( '\n', stderr );
  2261.       usage( argd );
  2262.       exit( exit_SYNTAX );
  2263.    }
  2264.  
  2265.       /* reset previous parse-flags */
  2266.    cmd_flags(argd) = saveflags;
  2267.  
  2268.    return   rc;
  2269. }
  2270.  
  2271. #endif /* ! LITE */
  2272.  
  2273.  
  2274. /***************************************************************************
  2275. ** ^FUNCTION: parseargs -- parse an argument vector
  2276. **
  2277. ** ^SYNOPSIS:
  2278. */
  2279. #ifndef __ANSI_C__
  2280.    int parseargs( argv, argd )
  2281. /*
  2282. ** ^PARAMETERS:
  2283. */
  2284.    char  *argv[];
  2285. /*    -- pointer to the argument vector as passed to main().
  2286. */
  2287.   ARGDESC argd[];
  2288. /*    -- the argument descriptor array.
  2289. */
  2290. #endif  /* !__ANSI_C__ */
  2291.  
  2292. /* ^DESCRIPTION:
  2293. **    Given a vector of string-valued arguments such as that passed to main
  2294. **    and a vector describing the possible arguments, parseargs matches
  2295. **    actual arguments to possible arguments, converts values to the
  2296. **    desired type, and diagnoses problems such as missing arguments, extra
  2297. **    arguments, and argument values that are syntactically incorrect.
  2298. **
  2299. ** ^REQUIREMENTS:
  2300. **    <argv> must be non-NULL and have a NULL pointer as its last item.
  2301. **
  2302. ** ^SIDE-EFFECTS:
  2303. **    <argd> is modified accordingly as arguments are matched.
  2304. **
  2305. ** ^RETURN-VALUE:
  2306. **    pe_SYSTEM
  2307. **       -- If a system error occurred
  2308. ** 
  2309. **    pe_SUCCESS
  2310. **       -- success, no errors encountered.
  2311. ** 
  2312. **    pe_SYNTAX
  2313. **       -- if a syntax error occurs
  2314. **
  2315. ** ^ALGORITHM:
  2316. **    - call parse init
  2317. **    - if syntax-error, print usage and exit
  2318. **    - return
  2319. ***^^**********************************************************************/
  2320. #ifdef __ANSI_C__
  2321.    int parseargs( char *argv[], ARGDESC argd[] )
  2322. #endif
  2323. {
  2324.    register ARGDESC *cmd;
  2325.    register char **av = argv;
  2326.    int rc = pe_SUCCESS;
  2327.    argMask_t  saveflags;
  2328.  
  2329.       /* allow null argument descriptor */
  2330.    if ( !argd )  argd = Empty_ArgDesc;
  2331.  
  2332.       /* initialize command-structure */
  2333.    if ( !CMD_isINIT(argd) )  init_args( argd );
  2334.    cmd = argd;
  2335.    saveflags = cmd_flags(cmd);
  2336.  
  2337.    if ( argv  &&  !BTEST(pa_ARGV0, cmd_flags(cmd)) ) {
  2338.       cmd_argv0(cmd) = basename( *av++ );
  2339.    }/*if*/
  2340.  
  2341.    rc = parse_argv_style( av, parse_init( &argd ) );
  2342.  
  2343.       /* scan for missing required arguments */
  2344.    if ( SYNTAX_ERROR(rc, argd) ) {
  2345.       fputc( '\n', stderr );
  2346.       usage( argd );
  2347.       exit( exit_SYNTAX );
  2348.    }
  2349.  
  2350.       /* reset previous parse-flags */
  2351.    cmd_flags(cmd) = saveflags;
  2352.  
  2353.    return  rc;
  2354. }
  2355.