home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume29 / parseargs / part06 / vms_args.c < prev   
C/C++ Source or Header  |  1992-05-19  |  32KB  |  1,078 lines

  1. /*************************************************************************
  2. ** ^FILE: vms_args.c - parse VMS/DCL argument vectors
  3. **
  4. ** ^DESCRIPTION:
  5. **    This file contains the routines used to parse VMS/DCL argument
  6. **    vectors and to print VMS/DCL usage messages.
  7. **
  8. ** ^HISTORY:
  9. **    11/21/91    Brad Appleton    <brad@ssd.csd.harris.com>
  10. **    - added Mike Levins fix to is_cmdline() to check for 0 length
  11. **      returned by lib$get_foreign.
  12. **    - added check of ps_NOTCMDLINE state-flag before calling is_cmdline()
  13. **    - fixed problem in vms_parse() where ARGVALGIVEN was getting set in
  14. **      place of ARGGIVEN.
  15. **
  16. **    08/27/91     Earl Chew     <cechew@bruce.cs.monash.edu.au>
  17. **    - Use ProgNameLen when accessing ProgName
  18. **    - Use get_argdesc() to access description
  19. **
  20. **    12/03/90    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  21. ***^^**********************************************************************/
  22.  
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <useful.h>
  26.  
  27. #ifdef vms
  28. # include <descrip.h>
  29. #endif
  30.  
  31. #include "strfuncs.h"
  32. #include "pgopen.h"
  33. #include "exit_codes.h"
  34.  
  35. #define PARSEARGS_PRIVATE   /* include private definitions */
  36. #include "parseargs.h"
  37.  
  38. EXTERN  VOID  syserr       ARGS((const char *, ...));
  39. EXTERN  VOID  usrerr       ARGS((const char *, ...));
  40. EXTERN  VOID  get_winsize  ARGS((int, int *, int *));
  41. EXTERN  BOOL  argInput     ARGS((ARGDESC *, char *, BOOL));
  42. EXTERN  BOOL  argOutput    ARGS((ARGDESC *, char *, BOOL));
  43.  
  44. VERSIONID("$Header: vms_args.c,v 1.1 90/08/23 18:00:00 brad Exp $");
  45.  
  46. /***************************************************************************
  47. ** ^GLOBAL-VARIABLE: Usage_Requested
  48. **
  49. ** ^VISIBILITY:
  50. **    static-global (visible to all functions in this file).
  51. **
  52. ** ^DESCRIPTION:
  53. **    Indicates whether a usage message was requested by the user
  54. **    (as opposed to triggerred by a syntax error).  If the message
  55. **    is requested by the user then it is always printed in verbose
  56. **    mode and does not return an error-status-code.
  57. ***^^**********************************************************************/
  58. static  BOOL  Usage_Requested = FALSE;
  59.  
  60.  
  61. #define MAXCMDLINE 255
  62. #define VNULL (VOID *) 0
  63.  
  64. #define TOGGLE(flag)  flag = (flag) ? FALSE : TRUE
  65.  
  66.    /* define mappings */
  67. #define  c_SPACE   '\001'   /* whitespace */
  68. #define  c_QUAL    '\002'   /* qualifier-delimiter */
  69. #define  c_EQUAL   '\003'   /* qualifier-argument separator */
  70. #define  c_COLON   '\004'   /* qualifier-argument separator */
  71. #define  c_PLUS    '\005'   /* list-item separator character */
  72. #define  c_COMMA   '\006'   /* list-item separator character */
  73.  
  74.  
  75. typedef enum {
  76.    Parameter,   /* token is a parameter */
  77.    Qualifier,   /* token is a qualifier */
  78.    EndOfLine    /* NUL-token (signifies end of tokens) */
  79. } dcl_arg_t;
  80.  
  81.  
  82. typedef struct {
  83.    dcl_arg_t  type;   /* token type */
  84.    char      *token;  /* token value */
  85. } dcl_token_t;
  86.  
  87.  
  88.  
  89. /***************************************************************************
  90. ** ^FUNCTION: is_cmdline - retrieve the original command-line
  91. **
  92. ** ^SYNOPSIS:
  93. */
  94. #ifndef __ANSI_C__
  95.    static BOOL is_cmdline( argv, result )
  96. /*
  97. ** ^PARAMETERS:
  98. */
  99.    char *argv[];
  100. /*    -- array of strings
  101. */
  102.    char **result;
  103. /*    -- pointer to resultant command-line
  104. */
  105. #endif  /* !__ANSI_C__ */
  106.  
  107. /* ^DESCRIPTION:
  108. **    Is_cmdline will compare the given vector of strings to the actual
  109. **    string given on the command-line. If the two are approximately 
  110. **    equivalent (modulo quotes and case) then the original command-line
  111. **    is copied to result, otherwise all the elements of argv are concat-
  112. **    enated together (in order and separated by whitespace) and assigned
  113. **    to *result.
  114. **
  115. ** ^REQUIREMENTS:
  116. **    argv must be non-null
  117. **
  118. ** ^SIDE-EFFECTS:
  119. **    *result is assigned to either the concatenated argv string or the
  120. **    original command-line. The result should be freed using free().
  121. **
  122. ** ^RETURN-VALUE:
  123. **    FALSE if the argv given is different from the command-line;
  124. **    TRUE otherwise.
  125. **
  126. ** ^CAVEATS:
  127. **    The comparison is case blind and double quotes are ignored in the
  128. **    command-line.  This is because lib$get_foreign returns double quotes
  129. **    intact, while VAX-C strips them off.
  130. **
  131. ** ^ACKNOWLEDGEMENTS:
  132. **    Thanx to Jim Barbour for writing most of this code. --BDA
  133. **
  134. ** ^ALGORITHM:
  135. **    - Make a single string out of argv
  136. **    - compare the "big" string to the command-line
  137. **    - IF they are "equivalent" assign command-line to result & return TRUE.
  138. **      ELSE assign the "big" string to result and return FALSE.
  139. ***^^**********************************************************************/
  140. #ifdef __ANSI_C__
  141.    static BOOL is_cmdline( const char *argv[], char **result )
  142. #endif
  143. {
  144.    register CONST char *avstr;
  145. #ifdef vms
  146.    unsigned long int stat;
  147.    unsigned short int len;
  148.    register CONST char *aptr, *sptr;
  149.    static char str[ MAXCMDLINE ];
  150.    static BOOL got_cmd_line = FALSE;
  151.    $DESCRIPTOR(str_d, str);
  152. #endif
  153.  
  154.       /* make a single string out of argv */
  155.    avstr = strjoin( argv, " " );
  156.  
  157. #ifndef vms
  158.    *result = (char *)avstr;
  159.    return  FALSE;
  160.  
  161. #else
  162.       /* get the original command-line */
  163.    if ( ! got_cmd_line ) {
  164.       stat = lib$get_foreign( &str_d, VNULL, &len, VNULL );
  165.       str[len] = '\0';
  166.       got_cmd_line = TRUE;
  167.       if (! (stat & 1))  exit( stat );
  168.    }
  169.  
  170.       /* if we didnt have a command-line, dont bother comparing */
  171.    if ( !*str ) {
  172.       *result = (char *)avstr;
  173.       return  FALSE;
  174.    }
  175.  
  176.       /* compare the two */
  177.    for ( aptr = avstr, sptr = str ; *aptr && *sptr ; sptr++ ) {
  178.       if ( toupper(*sptr) == toupper(*aptr) ) {
  179.          ++aptr;
  180.       }
  181.       else  if ( *sptr != '"' ) {
  182.          *result = (char *)avstr;
  183.          return  FALSE;
  184.       }
  185.    }
  186.  
  187.    *result = strdup( str );
  188.    free( avstr );
  189.    return  TRUE;
  190. #endif
  191. }
  192.  
  193.  
  194. /***************************************************************************
  195. ** ^FUNCTION: dcl_strxlat - translate a string according to DCL syntax
  196. **
  197. ** ^SYNOPSIS:
  198. */
  199. #ifndef __ANSI_C__
  200.    static char *dcl_strxlat( str )
  201. /*
  202. ** ^PARAMETERS:
  203. */
  204.    char *str;
  205. /*    -- the string to translate.
  206. */
  207. #endif  /* !__ANSI_C__ */
  208.  
  209. /* ^DESCRIPTION:
  210. **    Dcl_strxlat will attempt to convert the given string to canonical
  211. **    form by escaping any unquoted special characters, and removing any
  212. **    unquoted whitespace around special characters (such as '=' and '/').
  213. **    Since the special characters are replaced with special codes, quotes
  214. **    are also removed.
  215. **
  216. ** ^REQUIREMENTS:
  217. **    <str> should be non-null and non-empty
  218. **
  219. ** ^SIDE-EFFECTS:
  220. **    <str> is "trimmed" to canonical form and special characters are mapped
  221. **    to a unique code.
  222. **
  223. ** ^RETURN-VALUE:
  224. **    The address of the translated string.
  225. **
  226. ** ^ALGORITHM:
  227. **    - remove all unquoted whitespace following any unquoted "/:=+("
  228. **    - remove all unquoted whitespace preceding any unquoted "/:=+)"
  229. **    - compress all unquoted whitespace,
  230. **    - remove all unquoted parentheses,
  231. **    - re-map all other unquoted special characters and remove quotes.
  232. **      use the following mapping:
  233. **           whitespace   ==>   '\001'
  234. **             '/'        ==>   '\002'
  235. **             ':' & '='  ==>   '\003'
  236. **             ',' & '+'  ==>   '\004'
  237. ***^^**********************************************************************/
  238. #ifdef __ANSI_C__
  239.    static char *dcl_strxlat( char *str )
  240. #endif
  241. {
  242.    register char c, *pread = str, *pwrite = str;
  243.    BOOL quoted = FALSE;
  244.  
  245.    /*
  246.    ** pass1 - scan forward, removing all whitespace after unquoted "/:=+,("
  247.    */
  248.    while ( c = *pwrite++ = *pread++ ) {
  249.       if ( c == '"' )   TOGGLE(quoted);
  250.       if ( !quoted   &&   strchr("/:=+,(", c) )
  251.          while( isspace(*pread) )   ++pread;
  252.    }
  253.    *--pwrite = '\0';   /* NUL terminate */
  254.  
  255.    /*
  256.    ** pass2 - scan backward, removing all whitespace before unquoted "/:=+,)"
  257.    */
  258.    pread = --pwrite;  /* set to last NON-NUL char */
  259.    quoted = FALSE;
  260.    while ( pread >= str ) {
  261.       c = *pwrite-- = *pread--;
  262.       if ( c == '"' )   TOGGLE(quoted);
  263.       if ( !quoted   &&   strchr("/:=+,)", c) )
  264.          while( isspace(*pread) )   --pread;
  265.    }
  266.    strcpy(str, ++pwrite);   /* reset BOS */
  267.  
  268.    /*
  269.    ** pass3 - compress all unquoted whitespace,
  270.    **         remove all unquoted parentheses,
  271.    **         re-map all other unquoted special characters and remove quotes.
  272.    **           use the following mapping:
  273.    **    whitespace   ->   '\001'
  274.    **      '/'        ->   '\002'
  275.    **      ':' & '='  ->   '\003'
  276.    **      ',' & '+'  ->   '\004'
  277.    */
  278.    pread = pwrite = str;
  279.    quoted = FALSE;
  280.    while ( c = *pread++ ) {
  281.       if ( c == '"' )
  282.          TOGGLE(quoted);
  283.       else if ( !quoted   &&   isspace(c) ) {
  284.          *pwrite++ = c_SPACE;
  285.          while( isspace(*pread) )   ++pread;
  286.       }
  287.       else if ( !quoted   &&   (c == '(' || c == ')') )
  288.          continue;
  289.       else if ( !quoted   &&   c == '/' )
  290.          *pwrite++ = c_QUAL;
  291.       else if ( !quoted   &&   c == ':' )
  292.          *pwrite++ = c_COLON;
  293.       else if ( !quoted   &&   c == '=' )
  294.          *pwrite++ = c_EQUAL;
  295.       else if ( !quoted   &&   c == '+' )
  296.          *pwrite++ = c_PLUS;
  297.       else if ( !quoted   &&   c == ',' )
  298.          *pwrite++ = c_COMMA;
  299.       else
  300.          *pwrite++ = c;
  301.    }/*while*/
  302.  
  303.    *pwrite = '\0';   /* NUL-terminate */
  304.    return   str;
  305. }
  306.  
  307.  
  308. /***************************************************************************
  309. ** ^FUNCTION: dcl_split - split a string up into a vector of DCL tokens
  310. **
  311. ** ^SYNOPSIS:
  312. */
  313. #ifndef __ANSI_C__
  314.    static dcl_token_t  *dcl_split( str )
  315. /*
  316. ** ^PARAMETERS:
  317. */
  318.    char *str;
  319. /*    -- the string to split up into tokens
  320. */
  321. #endif  /* !__ANSI_C__ */
  322.  
  323. /* ^DESCRIPTION:
  324. **    Dcl_split will split a string up into tokens (according to DCL grammar
  325. **    rules) and will additionally associate each token with a type (namely:
  326. **    a qualifier, a positional paramater, or the End-of-Tokens symbol).
  327. **
  328. ** ^REQUIREMENTS:
  329. **    Assume dcl_strxlat(str) has already been performed.
  330. **
  331. ** ^SIDE-EFFECTS:
  332. **    <str> is modified in much the same manner as it would have 
  333. **    been modified if it were passed as the vector_string to strsplit().
  334. **
  335. ** ^RETURN-VALUE:
  336. **    A vector of dcl_tokens.
  337. **
  338. ** ^ALGORITHM:
  339. **    - first count the number of tokens and also try to interpret stuff
  340. **      like  "parm1.1/qual1,parm1.2" by replacing the comma with a space.
  341. **    - allocate space for the vector of DCL tokens.
  342. **    - assign the approriate value and type for each token.
  343. **
  344. ** ^CAVEATS:
  345. **    Does not treate "/qual=(val1,val2/str,..)" as illegal
  346. **        ( parses it as if it were "/qual=(val1,val2)/str" )
  347. **
  348. **    Replaces "parm1.1/qual,parm1.2" with "parm1.1/qual parm1.2"
  349. **        which works only because parseargs requires a VMS
  350. **        positional list to be comma OR whitespace separated
  351. **        (not just comma separated).
  352. ***^^**********************************************************************/
  353. #ifdef __ANSI_C__
  354.    static dcl_token_t  *dcl_split( char *str )
  355. #endif
  356. {
  357.    int tokc = 1;  /* number of tokens */
  358.    dcl_token_t *tokv = (dcl_token_t *)NULL;  /* vector of tokens */
  359.    register char *pread, c;
  360.    register int i;
  361.  
  362.    if ( !str  ||  !(*str) )  return  (dcl_token_t *)NULL;
  363.  
  364.     /* 1st pass (left-to-right) : count tokens */
  365.    pread = ( *str == c_QUAL ) ? (str + 1) : str;
  366.    while ( c = *pread++ ) {
  367.       if ( c == c_QUAL  ||  c == c_SPACE )  ++tokc;
  368.       if ( c == c_QUAL ) {
  369.            /* replace "p1.1/qual,p1.2" with "p1.1/qual p1.2" */
  370.         char *p, delims[5];
  371.  
  372.         sprintf( delims, "%c%c%c%c", c_EQUAL, c_PLUS, c_QUAL, c_SPACE );
  373.         if ( (p = strpbrk((str + 1), delims))  &&
  374.              ((*p == c_PLUS) || (*p == c_COMMA)) )
  375.            *p == c_SPACE;
  376.       }
  377.    }
  378.  
  379.  
  380.     /* allocate vector */
  381.    tokv = (dcl_token_t *)malloc( (tokc + 1) * sizeof(dcl_token_t) );
  382.    if ( tokv == (dcl_token_t *)NULL ) {
  383.       syserr( "malloc() failed in dcl_split()" );
  384.    }
  385.    tokv[ tokc ].type = EndOfLine;
  386.    tokv[ tokc ].token = CHARNULL;
  387.  
  388.     /* 2nd pass (right-to-left) : assign tokens to strings */
  389.    for ( i = 1, --pread ; pread >= str ; pread-- ) {
  390.       if ( *pread == c_SPACE  ||  *pread == c_QUAL ) {
  391.          tokv[ tokc - i ].token = pread + 1;
  392.          tokv[ tokc - i ].type = ( *pread == c_QUAL ) ? Qualifier : Parameter;
  393.          *pread = '\0';
  394.          ++i;
  395.       }
  396.    }
  397.  
  398.    if ( *str ) {  /* then 1st char could NOT have been '/' */
  399.      tokv -> token = str;
  400.      tokv -> type  = Parameter;
  401.    }
  402.  
  403.    return  tokv;
  404. }
  405.  
  406.  
  407. /***************************************************************************
  408. ** ^FUNCTION: dcl_restore - restore the `escaped' characters in a token
  409. **
  410. ** ^SYNOPSIS:
  411. */
  412. #ifndef __ANSI_C__
  413.    static char *dcl_restore( tokstr )
  414. /*
  415. ** ^PARAMETERS:
  416. */
  417.    char *tokstr;
  418. /*    -- the token string to restore
  419. */
  420. #endif  /* !__ANSI_C__ */
  421.  
  422. /* ^DESCRIPTION:
  423. **    Dcl_restore will attempt to restore any DCL special characters (such as
  424. **    '/' and '=') that may have been escaped by dcl_strxlat().
  425. **
  426. ** ^REQUIREMENTS:
  427. **    tokstr should be non-null and non-empty
  428. **
  429. ** ^SIDE-EFFECTS:
  430. **    Any escape characters (such as c_QUAL) are restored to their ascii
  431. **    representation.
  432. **
  433. ** ^RETURN-VALUE:
  434. **    The address of the restored string
  435. **
  436. ** ^ALGORITHM:
  437. **    - for each character in tokstr
  438. **      - if it is special then replace it with its ascii code
  439. **        end-if
  440. **      end-for
  441. **
  442. ** ^CAVEATS:
  443. **    The string is not restored to way it was before it was processed by
  444. **    dcl_strxlat(). Any characters that were removed are still missing.
  445. ***^^**********************************************************************/
  446. #ifdef __ANSI_C__
  447.    static char *dcl_restore( char *tokstr )
  448. #endif
  449. {
  450.    register  char *str = tokstr;
  451.  
  452.    if ( !str || !*str )  return  str;
  453.  
  454.    for ( ; *str ; str++ ) {
  455.       switch( *str ) {
  456.          case c_SPACE  : *str = ' '; break;
  457.          case c_QUAL   : *str = '/'; break;
  458.          case c_EQUAL  : *str = '='; break;
  459.          case c_COLON  : *str = ':'; break;
  460.          case c_PLUS   : *str = '+'; break;
  461.          case c_COMMA  : *str = ','; break;
  462.          default : break;
  463.       }
  464.    }
  465.  
  466.    return  tokstr;
  467. }
  468.  
  469.  
  470.  
  471.  
  472. /***************************************************************************
  473. ** ^FUNCTION: split_list - function to handle ARGLISTs and ARGVECs
  474. **
  475. ** ^SYNOPSIS:
  476. */
  477. #ifndef __ANSI_C__
  478.    static BOOL split_list( ad, vp, cmd )
  479. /*
  480. ** ^PARAMETERS:
  481. */
  482.    ARGDESC *ad;
  483. /*    -- the argument which takes multiple values
  484. */
  485.    char *vp;
  486. /*    -- the string of values for the argument
  487. */
  488.    ARGDESC *cmd;
  489. /*    -- the command to which the argument belongs
  490. */
  491. #endif  /* !__ANSI_C__ */
  492.  
  493. /* ^DESCRIPTION:
  494. **    Split_list will split the string containing the set of values into
  495. **    a set of tokens and will then attempt to convert each token (in the
  496. **    order given) using the ad_type function of the argument structure.
  497. **
  498. ** ^REQUIREMENTS:
  499. **    <vp> must already be preprocessed by dcl_strxlat to escape any quoted
  500. **    characters and to map special characters to their corresponding values.
  501. **
  502. ** ^SIDE-EFFECTS:
  503. **    Ad has some of its flags modified as well as any modifications that
  504. **    are made by the ad_type function.
  505. **
  506. **    <vp> is modified by strsplit().
  507. **
  508. ** ^RETURN-VALUE:
  509. **    TRUE if all is hunky-dory; FALSE otherwise
  510. **
  511. ** ^ALGORITHM:
  512. **    - Split vp into a vector of tokens
  513. **    - foreach token
  514. **      - call ad_type(ad, token, copyf)
  515. **      end-if
  516. **    - set the ARGGIVEN and ARGVALGIVEN flags accordingly
  517. ***^^**********************************************************************/
  518. #ifdef __ANSI_C__
  519.    static BOOL  split_list( ARGDESC *ad, char *vp, ARGDESC *cmd )
  520. #endif
  521. {
  522.    char **arg_vec = (char **)NULL;
  523.    int  i, arg_num = 0;
  524.    BOOL err = FALSE;
  525.    char delims[3];
  526.  
  527.       /* set-up delimiter string */
  528.    *delims = c_PLUS;
  529.    *(delims + 1) = c_COMMA;
  530.    *(delims + 2) = '\0';
  531.  
  532.       /* break string up into to tokens and handle each one */
  533.    arg_num = strsplit( &arg_vec, vp, delims );
  534.    for ( i = 0 ; i < arg_num ; i++ ) {
  535.       vp = arg_vec[i];
  536.  
  537.          /* try to convert the type */
  538.       if ( !HANDLE(ad, dcl_restore(vp), cmd_flags(cmd)) )  err = TRUE;
  539.    }
  540.    if ( !err )  BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  541.  
  542.    if ( ARG_isPOSITIONAL(ad) ) {
  543.       cmd_list(cmd) = ad;
  544.    }
  545.    else {
  546.       cmd_list(cmd) = ARGDESCNULL;
  547.    }
  548.  
  549.    free( arg_vec );
  550.  
  551.    return  !err;
  552. }
  553.  
  554.  
  555. /***************************************************************************
  556. ** ^FUNCTION: vms_parse - parse VMS/DCL arg-vectors
  557. **
  558. ** ^SYNOPSIS:
  559. */
  560. #ifndef __ANSI_C__
  561.    int vms_parse( argv, argd )
  562. /*
  563. ** ^PARAMETERS:
  564. */
  565.    char *argv[];
  566. /*    -- the vector of string arguments from the command-line
  567. */
  568.    ARGDESC argd[];
  569. /*    -- the programmer description of the command and its args
  570. */
  571. #endif  /* !__ANSI_C__ */
  572.  
  573. /* ^DESCRIPTION:
  574. **    Vms_parse will parse the arguments in the given vector of strings,
  575. **    assign the corresponding values to the command-line arguments specified
  576. **    in argd, and check the syntax of the command-line.
  577. **
  578. ** ^REQUIREMENTS:
  579. **    The final element in argv must be a NULL pointer.
  580. **
  581. ** ^SIDE-EFFECTS:
  582. **    argd is modified according to the command-line description and parameters
  583. **
  584. ** ^RETURN-VALUE:
  585. **    pe_SUCCESS (0) if no errors are encountered
  586. **    pe_SYSTEM (-1) if a system error is encountered
  587. **    pe_SYNTAX if a syntax error is encountered
  588. **
  589. ** ^ALGORITHM:
  590. **    - compare argv to the command-line (use the command-line if equal)
  591. **    - put argv back into a single string and translate it using dcl_strxlat
  592. **    - reparse the string into DCL tokens using dcl_strsplit
  593. **    - for each DCL token
  594. **       - attempt to match the token as a qualifier
  595. **       - if it is a qualifier
  596. **          - record and convert its value (if any)
  597. **       - else it is a positional parameter
  598. **          - record and convert its value (if any)
  599. **       - else there are too many arguments
  600. **          - return pe_SYNTAX
  601. **         end-if
  602. **       end-for
  603. ***^^**********************************************************************/
  604. #ifdef __ANSI_C__
  605.    int  vms_parse( char *argv[], ARGDESC argd[] )
  606. #endif
  607. {
  608.    register ARGDESC *ad, *args, *cmd;
  609.    register char *p;
  610.    char *avstr;
  611.    BOOL  is_match = FALSE;
  612.    int  parse_error = pe_SUCCESS;
  613.    dcl_token_t  *tok, *tokvec;
  614.    argName_t  keyword;
  615.    argMask_t  saveflags, flags;
  616.  
  617.    if ( !argd )  return  parse_error;
  618.  
  619.       /* initialize command-structure */
  620.    if ( !CMD_isINIT(argd) )  init_args( argd );
  621.    cmd = argd;
  622.    saveflags = cmd_flags(cmd);
  623.  
  624.    if ( !argv || !*argv )  return  parse_error;
  625.  
  626.    if ( !BTEST(cmd_state(cmd), ps_NOTCMDLINE) ) {
  627.       (VOID) is_cmdline( (CONST char **)argv, &avstr );
  628.    }
  629.    else {
  630.       avstr = strjoin( argv, " " );
  631.    }
  632.  
  633.    BSET( cmd_flags(cmd), pa_COPYF );
  634.    (VOID) dcl_strxlat( avstr );
  635.    if ( !avstr || !*avstr )  return  parse_error;
  636.    tokvec = dcl_split( avstr );
  637.  
  638.       /* run through the token vector */
  639.    for ( tok = tokvec ; (p = tok -> token) ; tok++ ) {
  640.  
  641.       if ( tok -> type == Qualifier  &&  !BTEST(cmd_state(cmd), ps_NOFLAGS) ) {
  642.          char c = '\0', *s, delims[3];
  643.  
  644.             /* set-up delimiter string */
  645.          *delims = c_EQUAL;
  646.          *(delims + 1) = c_COLON;
  647.          *(delims + 2) = '\0';
  648.  
  649.             /* skip past qualifier prefix and look for possible argument */
  650.          s  = strpbrk(p, delims);
  651.          if (s) {
  652.             c = *s;
  653.             *s++ = '\0';
  654.          }
  655.  
  656.          is_match = FALSE;
  657.          for ( args = argd ; args && !is_match ; args = cmd_defargs(args) ) {
  658.             for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  659.                if (arg_type(ad) == argDummy)  continue;
  660.  
  661.                if (!ARG_isPOSONLY(ad)  &&  match(p, arg_sname(ad)) == 0) {
  662.                   is_match = TRUE;
  663.                   break;
  664.                }/*if*/
  665.             }
  666.          }
  667.  
  668.          if (c)  *(s-1) = c;  /* restore the equal sign */
  669.  
  670.          if ( !is_match ) {
  671.             if (s)  *(s-1) = '\0';
  672.             usrerr( "undefined qualifier %s", s_KWD_PFX, p );
  673.             if (s)  *(s-1) = c;
  674.             parse_error = pe_SYNTAX;
  675.             continue;
  676.          }
  677.  
  678.             /* end-qualifiers */
  679.          if ( arg_type(ad) == argEnd ) {
  680.             BSET( cmd_state(cmd), ps_NOFLAGS );
  681.             continue;
  682.          }
  683.             /* if usage - just print usage and exit */
  684.          if ( arg_type(ad) == argUsage ) {
  685.             Usage_Requested = TRUE;
  686.             usage( argd );
  687.             free( avstr );
  688.             if ( tokvec )  free( tokvec );
  689.             cmd_flags(cmd) = saveflags;
  690.             exit(exit_USAGE);
  691.          }
  692.          /* reset the argument flags - if this arg was already given, some
  693.          ** of its flags may be set to indicate how it was given before.
  694.          ** we need to know how it was given now (but save the old ones
  695.          ** just in case the new one fails).
  696.          */
  697.          flags = arg_flags(ad);
  698.          if ( ARG_isGIVEN(ad) ) {
  699.             BCLEAR( arg_flags(ad), ARGVALSEP );
  700.             if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  701.          }
  702.  
  703.             /* ARGNOVALs are special, having no value */
  704.          if ( ! ARG_isVALTAKEN(ad) ) {
  705.             if ( !HANDLE(ad, dcl_restore(s), cmd_flags(cmd)) ) {
  706.                arg_flags(ad) = flags;
  707.                parse_error = pe_SYNTAX;
  708.             }
  709.             else {
  710.                BSET( arg_flags(ad), ARGGIVEN );
  711.                ad = ARGDESCNULL;
  712.             }
  713.             continue;
  714.          }/*if ARGNOVAL*/
  715.  
  716.             /* now get the real value */
  717.          if ( !s || !(*s) ) {
  718.             if ( ARG_isVALOPTIONAL(ad) ) {
  719.                BSET( arg_flags(ad), ARGGIVEN );
  720.             }
  721.             else {
  722.                (VOID) get_kwdname( arg_sname(ad), keyword );
  723.                usrerr("qualifier %s requires an argument", keyword);
  724.                arg_flags(ad) = flags;
  725.                parse_error = pe_SYNTAX;
  726.             }
  727.             continue;
  728.          }/*if*/
  729.  
  730.          if( ARG_isMULTIVAL(ad) ) {
  731.             if( !split_list(ad, s, cmd) ) {
  732.                arg_flags(ad) = flags;
  733.                parse_error = pe_SYNTAX;
  734.             }
  735.             else {
  736.                BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  737.             }
  738.             continue;
  739.          }/*if list*/
  740.  
  741.             /* try to convert the type */
  742.          if ( !HANDLE(ad, dcl_restore(s), cmd_flags(cmd)) ) {
  743.             arg_flags(ad) = flags;
  744.             parse_error = pe_SYNTAX;
  745.          }
  746.          else {
  747.             BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  748.          }
  749.  
  750.          continue;
  751.       }/*if qual*/
  752.       else {
  753.             /* parsing a vector of arguments */
  754.          if ( cmd_list(cmd) ) {
  755.             ad = cmd_list(cmd);
  756.             flags = arg_flags(ad);
  757.             if ( ARG_isGIVEN(ad) ) {
  758.                BCLEAR( arg_flags(ad), ARGVALSEP );
  759.             }
  760.  
  761.             BSET( arg_flags(ad), ARGVALSEP );
  762.  
  763.             if( ARG_isMULTIVAL(ad) ) {
  764.                if( !split_list(ad, p, cmd) )  parse_error = pe_SYNTAX;
  765.             }
  766.             else if ( !HANDLE(ad, dcl_restore(p), cmd_flags(cmd)) ) {
  767.                arg_flags(ad) = flags;
  768.                parse_error = pe_SYNTAX;
  769.             }
  770.  
  771.             if ( !parse_error )  BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  772.  
  773.             continue;
  774.          }
  775.             /* positional argument */
  776.          is_match = FALSE;
  777.          for ( args = argd ; args && !is_match ; args = cmd_defargs(args) ) {
  778.             for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  779.                if (arg_type(ad) == argDummy)  continue;
  780.  
  781.                if ( ARG_isPOSITIONAL(ad)  &&
  782.                     (!ARG_isGIVEN(ad) || ARG_isMULTIVAL(ad)) ) {
  783.                   is_match = TRUE;
  784.                   break;
  785.                }/*if*/
  786.             }
  787.          }
  788.  
  789.          if ( !is_match ) {
  790.             usrerr("too many arguments");
  791.             parse_error = pe_SYNTAX;
  792.             continue;
  793.          }
  794.  
  795.          /* if FLAGS1ST is set then first positional marks end-of-flags */
  796.          if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) ) {
  797.             BSET( cmd_state(cmd), ps_NOFLAGS );
  798.          }
  799.  
  800.          /* reset the argument flags - if this arg was already given, some
  801.          ** of its flags may be set to indicate how it was given before.
  802.          ** we need to know how it was given now (but save the old ones
  803.          ** just in case the new one fails).
  804.          */
  805.          flags = arg_flags(ad);
  806.          if ( ARG_isGIVEN(ad) ) {
  807.             BCLEAR( arg_flags(ad), ARGVALSEP );
  808.             if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  809.          }
  810.  
  811.          BSET( arg_flags(ad), ARGVALSEP );
  812.  
  813.          if( ARG_isMULTIVAL(ad) ) {
  814.             if( !split_list(ad, p, cmd) ) {
  815.                arg_flags(ad) = flags;
  816.                parse_error = pe_SYNTAX;
  817.             }
  818.             else {
  819.                BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  820.             }
  821.             continue;
  822.          }/*if list*/
  823.  
  824.             /* try to convert */
  825.          if ( !HANDLE(ad, dcl_restore(p), cmd_flags(cmd)) ) {
  826.             arg_flags(ad) = flags;
  827.             parse_error = TRUE;
  828.          }
  829.          else {
  830.             BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  831.          }
  832.  
  833.       }/*if parameter*/
  834.    }/*while*/
  835.  
  836.    free( avstr );
  837.    if ( tokvec )  free( tokvec );
  838.    cmd_flags(cmd) = saveflags;
  839.    return  parse_error;
  840. }
  841.  
  842.  
  843. /***************************************************************************
  844. ** ^FUNCTION: fmtarg - format command-argument syntax
  845. **
  846. ** ^SYNOPSIS:
  847. */
  848. #ifndef __ANSI_C__
  849.    static int fmtarg( ad, buf )
  850. /*
  851. ** ^PARAMETERS:
  852. */
  853.    ARGDESC *ad;
  854. /*    -- pointer to the argument to format
  855. */
  856.    char *buf;
  857. /*    -- character buffer to hold the formatted result
  858. */
  859. #endif  /* !__ANSI_C__ */
  860.  
  861. /* ^DESCRIPTION:
  862. **    Fmtarg will determine the proper command-line syntax for the
  863. **    given argument and write the result to the given buffer.
  864. **
  865. ** ^REQUIREMENTS:
  866. **    buf must be large enough to hold the formatted result (100 characters
  867. **    should do the trick).
  868. **
  869. ** ^SIDE-EFFECTS:
  870. **    buf is overwritten.
  871. **
  872. ** ^RETURN-VALUE:
  873. **    The number of printable characters in the argument-syntax-string
  874. **
  875. ** ^ALGORITHM:
  876. **    Print argument usage based on whether or not the argument is
  877. **    positional, hidden, multi-valued (list or vector), etc ....
  878. **    Optional arguments and values are enclosed in square braces.
  879. ***^^**********************************************************************/
  880. #ifdef __ANSI_C__
  881.    static int fmtarg( const ARGDESC *ad, char *buf )
  882. #endif
  883. {
  884.    /* buf must already be large enough */
  885.    char * pos;
  886.    argName_t  keyword, name;
  887.  
  888.    (VOID) get_argname( arg_sname(ad), name );
  889.  
  890.    if (ARG_isPOSITIONAL(ad)) {
  891.       sprintf( buf, "<%s>", name );
  892.    }
  893.    else {
  894.       (VOID) get_kwdname( arg_sname(ad), keyword );
  895.       sprintf( buf, "%c%s", *s_KWD_PFX, keyword );
  896.       pos = buf + strlen(buf);
  897.  
  898.       if ( ARG_isVALTAKEN(ad) && !ARG_isBOOLEAN(ad) && !ARG_isPSEUDOARG(ad) ) {
  899.          if  ( ARG_isVALOPTIONAL(ad)) {
  900.             sprintf( pos, "[%c<%s>]", *s_ARG_SEP, name );
  901.          }
  902.          else {
  903.             sprintf( pos, "%c<%s>", *s_ARG_SEP, name );
  904.          }
  905.       }/*if*/
  906.    }/*else*/
  907.  
  908.    return  (int) strlen(buf);
  909. }
  910.  
  911.  
  912. /***************************************************************************
  913. ** ^FUNCTION: vms_usage - print a usage message
  914. **
  915. ** ^SYNOPSIS:
  916. */
  917. #ifndef __ANSI_C__
  918.    VOID vms_usage( argd, usage_flags )
  919. /*
  920. ** ^PARAMETERS:
  921. */
  922.    ARGDESC *argd;
  923. /*    -- the command-descriptor array
  924. */
  925.    argMask_t usage_flags;
  926. /*    -- flags set by $USAGECNTL
  927. */
  928. #endif  /* !__ANSI_C__ */
  929.  
  930. /* ^DESCRIPTION:
  931. **    Vms_usage will print the VMS/DCL command-line usage of the given
  932. **    command on standard diagnostic output (stderr). The content of the
  933. **    usage message is controlled by the bitmasks in usage_flags which
  934. **    correspond to the settings in the user's USAGECNTL symbol.
  935. **
  936. ** ^REQUIREMENTS:
  937. **    argd should be a non-null command-line argument-descriptor array
  938. **
  939. ** ^SIDE-EFFECTS:
  940. **    Prints on stderr.
  941. **
  942. ** ^RETURN-VALUE:
  943. **    None.
  944. **
  945. ** ^ALGORITHM:
  946. **    - if no usage is desired then exit
  947. **    - if paging is requested print to the pager instead of stderr
  948. **    - print the command-line syntax
  949. **    - if the description is requested print it
  950. **    - if verbose mode is requested, print the description of each argument
  951. ***^^**********************************************************************/
  952. #ifdef __ANSI_C__
  953.    void vms_usage( const ARGDESC *argd, argMask_t usage_flags )
  954. #endif
  955. {
  956.    register CONST ARGDESC  *ad, *args, *cmd;
  957.    int  max_cols = 80, max_lines  = 24;
  958.    int  margin, ll, pl, qualifiers, longest, positionals;
  959.    BOOL first = TRUE;
  960.    FILE *fp;
  961.  
  962.    if ( !argd )  return;
  963.  
  964.       /* initialize command-structure */
  965.    if ( !CMD_isINIT(argd) )  init_args( (ARGDESC *)argd );
  966.    cmd = argd;
  967.  
  968.       /* get screen size */
  969.    get_winsize( fileno(stderr), &max_lines, &max_cols );
  970.  
  971.       /* force verbose-mode if requested */
  972.    if ( Usage_Requested )   BSET( usage_flags, usg_VERBOSE );
  973.  
  974.    if ( BTEST(usage_flags, usg_NONE) )  return;
  975.  
  976.    fp = ( BTEST(usage_flags, usg_PAGED) )
  977.       ? pgopen( stderr, getenv("USAGE_PAGER") )
  978.       : stderr;
  979.  
  980.       /* allow null argument descriptor */
  981.    fprintf(fp, "Format: %.*s", ProgNameLen, (ProgName) ? ProgName : "");
  982.  
  983.    ll = ProgNameLen + 8;
  984.    margin = ll + 1;
  985.    longest = 0;
  986.  
  987.    for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
  988.       for ( args = argd ; args ; args = cmd_defargs(args) ) {
  989.          for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  990.             argName_t  buf, name;
  991.  
  992.                /* don't display hidden arguments */
  993.             if ( ARG_isHIDDEN(ad) )  continue;
  994.             if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
  995.             if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
  996.  
  997.                /* figure out how wide this parameter is (for printing) */
  998.             pl = fmtarg(ad, buf);
  999.  
  1000.             if ( pl > longest )  longest = pl;
  1001.  
  1002.  
  1003.             if ( ARG_isMULTIVAL(ad) ) {
  1004.                (VOID) get_argname( arg_sname(ad), name );
  1005.                strcat(buf, "[,<");
  1006.                strcat(buf, name);
  1007.                strcat(buf, ">...]");
  1008.                pl += 8 + strlen(name);
  1009.             }
  1010.             if ( !ARG_isREQUIRED(ad) ) {
  1011.                pl += 2;  /* [] */
  1012.             }
  1013.  
  1014.             /* see if this will fit */
  1015.             if ( (ll + pl + 1) > (max_cols - first) ) {
  1016.                   /* no... start a new line */
  1017.                fprintf(fp, "\n%*s", margin, "");
  1018.                ll = margin;
  1019.             }
  1020.             else {
  1021.                   /* yes... just throw in a space */
  1022.                fputc(' ', fp);
  1023.                ++ll;
  1024.             }
  1025.             ll += pl;
  1026.  
  1027.                 /* show the argument */
  1028.             if ( !ARG_isREQUIRED(ad) )  fputc('[', fp);
  1029.             fprintf(fp, buf);
  1030.             if ( !ARG_isREQUIRED(ad) )  fputc(']', fp);
  1031.  
  1032.             first = FALSE;  /* not first line anymore */
  1033.          }/*for each ad */
  1034.       }/* for each argd */
  1035.    }/* for each parm-type */
  1036.  
  1037.    fputc('\n', fp);
  1038.  
  1039.    if ( BTEST(usage_flags, usg_DESCRIPTION) ) {
  1040.       CONST char *description = cmd_description(cmd);
  1041.  
  1042.       if ( description  &&  *description ) {
  1043.          fprintf( fp, "Description:\n" );
  1044.          indent_para(fp, max_cols, 8, "", 0, description, 0);
  1045.          fputc( '\n', fp );
  1046.       }
  1047.    }/*if*/
  1048.  
  1049.    if ( !BTEST(usage_flags, usg_VERBOSE) )  {
  1050.       if ( pgactive(fp) )  (VOID) pgclose( fp );
  1051.       return;
  1052.    }
  1053.  
  1054.    qualifiers = 0;
  1055.    for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
  1056.       for ( args = argd ; args ; args = cmd_defargs(args) ) {
  1057.          for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  1058.             argName_t  buf;
  1059.         char  *desc;
  1060.         int  desclen;
  1061.  
  1062.                /* don't display hidden arguments */
  1063.             if ( ARG_isHIDDEN(ad) )  continue;
  1064.             if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
  1065.             if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
  1066.  
  1067.             if ( !qualifiers++ )  fprintf(fp, "Qualifiers/Parameters:\n");
  1068.             (VOID) fmtarg(ad, buf);
  1069.         desc = get_argdesc(arg_description(ad), &desclen);
  1070.             indent_para(fp, max_cols, 8, buf, longest+2, desc, desclen );
  1071.          }/*for each ad */
  1072.       }/* for each argd */
  1073.    }/* for each parm-type */
  1074.  
  1075.    if ( pgactive(fp) )  (VOID) pgclose( fp );
  1076. }
  1077.  
  1078.