home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume29 / parseargs / part05 / amiga_args.c next >
Encoding:
C/C++ Source or Header  |  1992-05-19  |  18.0 KB  |  554 lines

  1. /*************************************************************************
  2. ** ^FILE: amiga_args.c - parse AmigaDOS argument vectors
  3. **
  4. ** ^DESCRIPTION:
  5. **    This file contains the routines used to parse AmigaDOS argument
  6. **    vectors and to print AmigaDOS usage messages.
  7. **
  8. ** ^HISTORY:
  9. **    27/08/91     Earl Chew     <cechew@bruce.cs.monash.edu.au>
  10. **    - Use ProgNameLen when accessing ProgName
  11. **    - Use get_argdesc() to access description
  12. **
  13. **    01/02/91     Brad Appleton     <brad@ssd.csd.harris.com>
  14. **    - Added structured block comments
  15. **    - Added optional arguments to keywords
  16. **
  17. **    --/--/--  Peter da Silva  <peter@ferranti.com>    Created
  18. ***^^**********************************************************************/
  19.  
  20. #include <ctype.h>
  21. #include <useful.h>
  22. #include "strfuncs.h"
  23. #include "pgopen.h"
  24. #include "exit_codes.h"
  25.  
  26. #define PARSEARGS_PRIVATE   /* include private definitions */
  27. #include "parseargs.h"
  28.  
  29. EXTERN  VOID  syserr       ARGS((const char *, ...));
  30. EXTERN  VOID  usrerr       ARGS((const char *, ...));
  31. EXTERN  char *getenv       ARGS((const char *));
  32. EXTERN  VOID  get_winsize  ARGS((int, int *, int *));
  33.  
  34. VERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $");
  35.  
  36. /***************************************************************************
  37. ** ^GLOBAL-VARIABLE: Usage_Requested
  38. **
  39. ** ^VISIBILITY:
  40. **    static-global (visible to all functions in this file).
  41. **
  42. ** ^DESCRIPTION:
  43. **    Indicates whether a usage message was requested by the user
  44. **    (as opposed to triggerred by a syntax error).  If the message
  45. **    is requested by the user then it is always printed in verbose
  46. **    mode and does not return an error-status-code.
  47. ***^^**********************************************************************/
  48. static  BOOL  Usage_Requested = FALSE;
  49.  
  50.  
  51. /***************************************************************************
  52. ** ^FUNCTION: amiga_parse - parse Amiga_DOS arg-vectors
  53. **
  54. ** ^SYNOPSIS:
  55. */
  56. #ifndef __ANSI_C__
  57.    int amiga_parse( argv, argd )
  58. /*
  59. ** ^PARAMETERS:
  60. */
  61.    char *argv[];
  62. /*    -- the vector of string arguments from the command-line
  63. */
  64.    ARGDESC argd[];
  65. /*    -- the programmer description of the command and its args
  66. */
  67. #endif  /* !__ANSI_C__ */
  68.  
  69. /* ^DESCRIPTION:
  70. **    Amiga_parse will parse the arguments in the given vector of strings,
  71. **    assign the corresponding values to the command-line arguments specified
  72. **    in argd, and check the syntax of the command-line.
  73. **
  74. ** ^REQUIREMENTS:
  75. **    The final element in argv must be a NULL pointer.
  76. **
  77. ** ^SIDE-EFFECTS:
  78. **    argd is modified according to the command-line description and parameters
  79. **
  80. ** ^RETURN-VALUE:
  81. **    pe_SUCCESS (0) if no errors are encountered
  82. **    pe_SYSTEM (-1) if a system error is encountered
  83. **    pe_SYNTAX if a syntax error is encountered
  84. **
  85. ** ^ALGORITHM:
  86. **    - for each command-line argument
  87. **       - attempt to match the argument as a keyword
  88. **       - if it is a keyword argument
  89. **          - record and convert its value (if any)
  90. **         else it is a positional parameter
  91. **          - record and convert its value (if any)
  92. **         else there are too many arguments
  93. **          - return pe_SYNTAX
  94. **         end-if
  95. **       end-for
  96. ***^^**********************************************************************/
  97. #ifdef __ANSI_C__
  98.    int amiga_parse( char **argv, ARGDESC argd[] )
  99. #endif
  100. {
  101.    register ARGDESC *cmd, *args, *ad = ARGDESCNULL;
  102.    register char **av;
  103.    register char *p = CHARNULL;
  104.    argName_t   keyword;
  105.    argMask_t   flags;
  106.    int  parse_error = pe_SUCCESS;
  107.    BOOL is_match = FALSE;
  108.  
  109.    if ( !argd )  return  parse_error;
  110.  
  111.       /* initialize command-structure */
  112.    if ( !CMD_isINIT(argd) )  init_args( argd );
  113.    cmd = argd;
  114.    cmd_prev(cmd) = ARGDESCNULL;
  115.  
  116.       /* run through the argument vector */
  117.    for ( av = argv ; *av ; av++ ) {
  118.       char c = '\0';
  119.  
  120.          /* If looking for keywords, see if this is one */
  121.       if( !BTEST(cmd_state(cmd), ps_NOFLAGS) ) {
  122.          p = strpbrk(*av, s_ARG_SEP);
  123.          if ( p ) {
  124.             c = *p;
  125.             *p++ = '\0';  /* skip past arg-separator character */
  126.          }
  127.  
  128.          is_match = FALSE;
  129.          for ( args = argd ; args  &&  !is_match ; args = cmd_defargs(args) ) {
  130.             for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
  131.                if (arg_type(ad) == argDummy)  continue;
  132.  
  133.                if (!ARG_isPOSONLY(ad)  &&  match(*av, arg_sname(ad)) == 0) {
  134.                   is_match = TRUE;
  135.                   break;
  136.                }/*if*/
  137.             }
  138.          }
  139.  
  140.          if ( !is_match )  ad = ARGDESCNULL;
  141.       }/*if !NOFLAGS*/
  142.  
  143.       if (c)  *(p-1) = c;  /* restore the equal sign */
  144.  
  145.          /* If we have a keyword here */
  146.       if( !BTEST(cmd_state(cmd), ps_NOFLAGS)  &&  ad) {
  147.          if ( cmd_prev(cmd) ) { /* a value may have been given but wasnt */
  148.             if ( ARG_isVALOPTIONAL(cmd_prev(cmd)) ) {
  149.                BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN );
  150.             }
  151.             else {  /* value was required */
  152.                (VOID)get_kwdname( arg_sname(cmd_prev(cmd)), keyword );
  153.                usrerr( "value required for %s keyword", keyword );
  154.                parse_error = pe_SYNTAX;
  155.             }
  156.             cmd_prev(cmd) = ARGDESCNULL;
  157.          }
  158.  
  159.          if ( cmd_list(cmd) ) { /* end of list */
  160.             cmd_list(cmd) = ARGDESCNULL;
  161.          }
  162.  
  163.          flags = arg_flags(ad);    /* save flags */
  164.          if ( ARG_isGIVEN(ad) ) {
  165.             BCLEAR( arg_flags(ad), ARGVALSEP | ARGKEYWORD );
  166.             if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  167.          }
  168.  
  169.          if ( p ) { /* matched NAME=VALUE */
  170.             if ( ARG_isMULTIVAL(ad) )
  171.                cmd_list(cmd) = ad;
  172.             else
  173.                cmd_list(cmd) = ARGDESCNULL;
  174.  
  175.                /* try to convert the type */
  176.             if ( !HANDLE(ad, p, cmd_flags(cmd)) ) {
  177.                arg_flags(ad) = flags;  /* restore flags */
  178.                parse_error = pe_SYNTAX;
  179.             }
  180.             else
  181.                BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  182.             ad = ARGDESCNULL;
  183.          }
  184.          else {
  185.             if (arg_type(ad) == argUsage) {
  186.                Usage_Requested = TRUE;
  187.                usage(argd);
  188.                exit(exit_USAGE);
  189.             }
  190.             else if (arg_type(ad) == argEnd) {
  191.                BSET( cmd_state(cmd), ps_NOFLAGS );
  192.                BSET( arg_flags(ad), ARGGIVEN );
  193.             }
  194.             else if ( ARG_isVALTAKEN(ad) ) {
  195.                cmd_prev(cmd) = ad;
  196.             }
  197.             else if ( !HANDLE(ad, CHARNULL, cmd_flags(cmd)) ) {
  198.                arg_flags(ad) = flags;  /* restore flags */
  199.                parse_error = pe_SYNTAX;
  200.             }
  201.             else {
  202.                BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  203.             }
  204.             ad = ARGDESCNULL;
  205.          }/*else*/
  206.       }
  207.       else if (cmd_prev(cmd)) { /* expecting a vlue from previous arg */
  208.          /* reset the argument flags - if this arg was already given, some
  209.          ** of its flags may be set to indicate how it was given before.
  210.          ** we need to know how it was given now (but save the old ones
  211.          ** just in case the new one fails).
  212.          */
  213.          flags = arg_flags(cmd_prev(cmd));    /* save flags */
  214.          if ( ARG_isGIVEN(cmd_prev(cmd)) ) {   /* reset flags */
  215.             BCLEAR( arg_flags(cmd_prev(cmd)), ARGVALSEP );
  216.             if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  217.          }
  218.  
  219.             /* previous value may have required a keyword */
  220.          BSET( arg_flags(cmd_prev(cmd)), ARGVALSEP );
  221.  
  222.          if ( ARG_isMULTIVAL(cmd_prev(cmd)) )
  223.             cmd_list(cmd) = cmd_prev(cmd);
  224.          else
  225.             cmd_list(cmd) = ARGDESCNULL;
  226.  
  227.             /* try to convert the type */
  228.          if ( !HANDLE(cmd_prev(cmd), *av, cmd_flags(cmd)) ) {
  229.             arg_flags(cmd_prev(cmd)) = flags;  /* restore flags */
  230.             parse_error = pe_SYNTAX;
  231.          }
  232.          else
  233.             BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN | ARGVALGIVEN );
  234.  
  235.          ad = ARGDESCNULL;
  236.          cmd_prev(cmd) = ARGDESCNULL;
  237.          continue;
  238.       }
  239.       else {  /* it's a positional argument or a list item */
  240.          if ( cmd_list(cmd) ) { /* its a list item */
  241.             /* reset the argument flags - if this arg was already given, some
  242.             ** of its flags may be set to indicate how it was given before.
  243.             ** we need to know how it was given now (but save the old ones
  244.             ** just in case the new one fails).
  245.             */
  246.             flags = arg_flags(cmd_list(cmd));    /* save flags */
  247.             if ( ARG_isGIVEN(cmd_list(cmd)) ) {   /* reset flags */
  248.                BCLEAR( arg_flags(cmd_list(cmd)), ARGVALSEP );
  249.             }
  250.             BSET( arg_flags(cmd_list(cmd)), ARGVALSEP );
  251.  
  252.             if ( !HANDLE(cmd_list(cmd), *av, cmd_flags(cmd)) ) {
  253.                arg_flags(cmd_list(cmd)) = flags;  /* restore flags */
  254.                parse_error = pe_SYNTAX;
  255.             }
  256.  
  257.             BSET( arg_flags(cmd_list(cmd)), ARGGIVEN | ARGVALGIVEN );
  258.             continue;
  259.          }
  260.          else {  /* its a positional argument */
  261.  
  262.             /* if FLAGS1ST is set then first positional marks end-of-flags */
  263.             if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) )
  264.                BSET( cmd_state(cmd), ps_NOFLAGS );
  265.  
  266.             is_match = FALSE;
  267.             for ( args = argd; args && !is_match; args = cmd_defargs(args) ) {
  268.                for (ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
  269.                   if (arg_type(ad) == argDummy)  continue;
  270.  
  271.                   if ( ARG_isPOSITIONAL(ad)  &&
  272.                        (!ARG_isGIVEN(ad) || ARG_isMULTIVAL(ad)) ) {
  273.                      is_match = TRUE;
  274.                      break;
  275.                   }/*if*/
  276.                }
  277.             }
  278.  
  279.             if ( !is_match ) {
  280.                usrerr("too many arguments");
  281.                parse_error = pe_SYNTAX;
  282.                ad = ARGDESCNULL;
  283.             }
  284.             else {
  285.                /* reset the argument flags - if this arg was already given, some
  286.                ** of its flags may be set to indicate how it was given before.
  287.                ** we need to know how it was given now (but save the old ones
  288.                ** just in case the new one fails).
  289.                */
  290.                flags = arg_flags(ad);    /* save flags */
  291.                if ( ARG_isGIVEN(ad) ) {   /* reset flags for this appearance */
  292.                   BCLEAR( arg_flags(ad), ARGVALSEP );
  293.                   if (! ARG_isMULTIVAL(ad))  BCLEAR(arg_flags(ad), ARGVALGIVEN);
  294.                }
  295.                BSET( arg_flags(ad), ARGVALSEP );
  296.  
  297.                   /* try to convert */
  298.                if ( !HANDLE(ad, *av, cmd_flags(cmd)) ) {
  299.                   arg_flags(ad) = flags;  /* restore flags */
  300.                   parse_error = pe_SYNTAX;
  301.                }
  302.                else
  303.                   BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  304.                ad = ARGDESCNULL;
  305.             }
  306.          }/*else positional*/
  307.       }/*else not keyword*/
  308.    }/*while*/
  309.  
  310.    /* If last argument was a keyword and required an option
  311.    ** then complain about it
  312.    */
  313.    if ( cmd_prev(cmd) ) { /* a value may have been given but wasnt */
  314.       if ( ARG_isVALOPTIONAL(cmd_prev(cmd)) ) {
  315.          BSET( arg_flags(cmd_prev(cmd)), ARGGIVEN );
  316.       }
  317.       else {  /* value was required */
  318.          (VOID)get_kwdname( arg_sname(cmd_prev(cmd)), keyword );
  319.          usrerr( "value required for %s keyword", keyword );
  320.          parse_error = pe_SYNTAX;
  321.       }
  322.       cmd_prev(cmd) = ARGDESCNULL;
  323.    }
  324.  
  325.    return  parse_error;
  326. }
  327.  
  328.  
  329. /***************************************************************************
  330. ** ^FUNCTION: fmtarg - format command-argument syntax
  331. **
  332. ** ^SYNOPSIS:
  333. */
  334. #ifndef __ANSI_C__
  335.    static int fmtarg(ad, buf)
  336. /*  
  337. ** ^PARAMETERS:
  338. */
  339.    ARGDESC *ad;
  340. /*    -- pointer to the argument to format
  341. */
  342.    char *buf;
  343. /*    -- character buffer to hold the formatted result
  344. */
  345. #endif  /* !__ANSI_C__ */
  346.  
  347. /* ^DESCRIPTION:
  348. **    Fmtarg will determine the proper command-line syntax for the
  349. **    given argument and write the result to the given buffer.
  350. **
  351. ** ^REQUIREMENTS:
  352. **    buf must be large enough to hold the formatted result (100 characters
  353. **    should do the trick).
  354. **
  355. ** ^SIDE-EFFECTS:
  356. **    buf is overwritten.
  357. **
  358. ** ^RETURN-VALUE:
  359. **    The number of printable characters in the argument-syntax-string
  360. **
  361. ** ^ALGORITHM:
  362. **    Print argument usage based on whether or not the argument is
  363. **    positional, hidden, multi-valued (list or vector), etc ....
  364. **    Optional arguments and values are enclosed in square braces.
  365. ***^^**********************************************************************/
  366. #ifdef __ANSI_C__
  367.    static int fmtarg( const ARGDESC *ad, char *buf )
  368. #endif
  369. {
  370.       /* buf must already be large enough */
  371.    char * pos;
  372.    argName_t  keyword, name;
  373.  
  374.    (VOID) get_argname(arg_sname(ad), name);
  375.  
  376.    if ( ARG_isPOSITIONAL(ad) ) {
  377.       sprintf( buf, "<%s>", name );
  378.    }
  379.    else {
  380.       (VOID) get_kwdname(arg_sname(ad), keyword);
  381.       (VOID) strcpy( buf, keyword );
  382.       pos = buf + strlen(buf);
  383.  
  384.       if ( ARG_isVALTAKEN(ad) && !ARG_isBOOLEAN(ad) && !ARG_isPSEUDOARG(ad) ) {
  385.          if ( ARG_isVALOPTIONAL(ad) )
  386.             sprintf( pos, " [<%s>]", name );
  387.          else
  388.             sprintf( pos, " <%s>", name );
  389.       }
  390.    }/*else*/
  391.  
  392.    return  strlen(buf);
  393. }
  394.  
  395.  
  396. /***************************************************************************
  397. ** ^FUNCTION: amiga_usage - print a usage message
  398. **
  399. ** ^SYNOPSIS:
  400. */
  401. #ifndef __ANSI_C__
  402.    VOID amiga_usage( argd, usage_flags )
  403. /*  
  404. ** ^PARAMETERS:
  405. */
  406.    ARGDESC *argd;
  407. /*    -- the command-descriptor array
  408. */
  409.    argMask_t usage_flags;
  410. /*    -- flags set by $USAGECNTL
  411. */
  412. #endif  /* !__ANSI_C__ */
  413.  
  414. /* ^DESCRIPTION:
  415. **    Amiga_usage will print the AmigaDOS command-line usage of the given
  416. **    command on standard diagnostic output (stderr). The content of the
  417. **    usage message is controlled by the bitmasks in usage_flags which
  418. **    correspond to the settings in the user's USAGECNTL variable.
  419. **
  420. ** ^REQUIREMENTS:
  421. **    argd should be a non-null command-line argument-descriptor array
  422. **
  423. ** ^SIDE-EFFECTS:
  424. **    Prints on stderr.
  425. **
  426. ** ^RETURN-VALUE:
  427. **    None.
  428. **
  429. ** ^ALGORITHM:
  430. **    - if no usage is desired then exit
  431. **    - if paging is requested print to the pager instead of stderr
  432. **    - print the command-line syntax
  433. **    - if the description is requested print it
  434. **    - if verbose mode is requested, print the description of each argument
  435. ***^^**********************************************************************/
  436. #ifdef __ANSI_C__
  437.    void amiga_usage( const ARGDESC *argd, argMask_t usage_flags )
  438. #endif
  439. {
  440.    register CONST ARGDESC  *ad, *args, *cmd;
  441.    int  max_cols = 80, max_lines  = 24;
  442.    int  margin, ll, pl, keywords, longest, positionals;
  443.    BOOL first = TRUE;
  444.    FILE *fp;
  445.  
  446.    if ( !argd )  return;
  447.  
  448.       /* initialize command-structure */
  449.    if ( !CMD_isINIT(argd) )  init_args( (ARGDESC *)argd );
  450.    cmd = argd;
  451.  
  452.       /* force verbose-mode if requested */
  453.    if ( Usage_Requested )   BSET( usage_flags, usg_VERBOSE );
  454.  
  455.    if ( BTEST(usage_flags, usg_NONE) )  return;
  456.  
  457.    fp = ( BTEST(usage_flags, usg_PAGED) )
  458.       ? pgopen( stderr, getenv("USAGE_PAGER") )
  459.       : stderr;
  460.  
  461.       /* get screen size */
  462.    get_winsize( fileno(stderr), &max_lines, &max_cols );
  463.    fprintf(fp, "Format: %.*s", ProgNameLen, (ProgName) ? ProgName : "");
  464.    ll = ProgNameLen + 8;
  465.    margin = ll + 1;
  466.    longest = 0;
  467.  
  468.    for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
  469.       for ( args = argd ; args ; args = cmd_defargs(args) ) {
  470.          for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  471.             argName_t  buf;
  472.  
  473.                /* don't display hidden arguments */
  474.             if ( ARG_isHIDDEN(ad) )  continue;
  475.             if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
  476.             if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
  477.  
  478.                /* figure out how wide this parameter is (for printing) */
  479.             pl = fmtarg(ad, buf);
  480.  
  481.             if ( pl > longest)  longest = pl;
  482.  
  483.             if ( !ARG_isREQUIRED(ad) ) {
  484.                pl += 2;  /* [] */
  485.             }
  486.             if ( ARG_isMULTIVAL(ad) ) {
  487.                strcat( buf, "..." );
  488.                pl += 3;
  489.             }
  490.  
  491.                /* see if this will fit */
  492.             if ( (ll + pl + 1) > (max_cols - first) ) {
  493.                   /* no... start a new line */
  494.                fprintf(fp, "\n%*s", margin, "");
  495.                ll = margin;
  496.             }
  497.             else {
  498.                   /* yes... just throw in a space */
  499.                fputc(' ', fp);
  500.                ++ll;
  501.             }
  502.             ll += pl;
  503.  
  504.                /* show the argument */
  505.             if ( !ARG_isREQUIRED(ad) )  fputc('[', fp);
  506.             fprintf(fp, buf);
  507.             if ( !ARG_isREQUIRED(ad) )  fputc(']', fp);
  508.  
  509.             first = FALSE;  /* not first line anymore */
  510.          }/*for each ad */
  511.       }/* for each argd */
  512.    }/* for each parm-type */
  513.  
  514.    fputc('\n', fp);
  515.  
  516.    if ( BTEST(usage_flags, usg_DESCRIPTION) ) {
  517.       CONST char *description = cmd_description(cmd);
  518.  
  519.       if ( description  &&  *description ) {
  520.          fprintf( fp, "Description:\n" );
  521.          indent_para(fp, max_cols, 8, "", 0, description, 0);
  522.          fputc( '\n', fp );
  523.       }
  524.    }/*if*/
  525.  
  526.    if ( !BTEST(usage_flags, usg_VERBOSE) )  {
  527.       if ( pgactive(fp) )  (VOID) pgclose( fp );
  528.       return;
  529.    }
  530.  
  531.    keywords = 0;
  532.    for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
  533.       for ( args = argd ; args ; args = cmd_defargs(args) ) {
  534.          for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  535.             argName_t  buf;
  536.             char  *desc;
  537.             int  desclen;
  538.  
  539.                /* don't display hidden arguments */
  540.             if ( ARG_isHIDDEN(ad) )  continue;
  541.             if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
  542.             if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
  543.  
  544.             if( !(keywords++) )  fprintf(fp, "Keywords/Arguments:\n");
  545.             (VOID) fmtarg(ad, buf);
  546.             desc = get_argdesc(arg_description(ad), &desclen);
  547.             indent_para(fp, max_cols, 8, buf, longest+2, desc, desclen);
  548.          }/*for each ad */
  549.       }/* for each argd */
  550.    }/* for each parm-type */
  551.  
  552.    if ( pgactive(fp) )  (VOID) pgclose( fp );
  553. }
  554.