home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume29 / parseargs / part05 / unix_args.c < prev   
Encoding:
C/C++ Source or Header  |  1992-05-19  |  23.0 KB  |  750 lines

  1. /*************************************************************************
  2. ** ^FILE: unix_args.c - parse Unix argument vectors
  3. **
  4. ** ^DESCRIPTION:
  5. **    This file contains the routines used to parse Unix argument
  6. **    vectors and to print Unix usage messages.
  7. **
  8. ** ^HISTORY:
  9. **    12/05/91     Brad Appleton     <brad@ssd.csd.harris.com>
  10. **    - added #ifdef POSIX_SOURCE to use "--" instead of "+" as
  11. **      GNU conformant prefix for long options.
  12. **
  13. **    08/27/91     Earl Chew     <cechew@bruce.cs.monash.edu.au>
  14. **    - Use ProgNameLen when accessing ProgName
  15. **    - Use get_argdesc() to access description
  16. **
  17. **    01/02/91     Brad Appleton     <brad@ssd.csd.harris.com>
  18. **    - Added structured block comments
  19. **    - Added optional arguments to keywords and options
  20. **
  21. **    --/--/--    Peter da Silva    <peter@ferranti.com>    
  22. **
  23. **    --/--/--    Eric P. Allman    <eric@Berkeley.EDU>     Created
  24. ***^^**********************************************************************/
  25.  
  26. #include <ctype.h>
  27. #include <useful.h>
  28. #include "strfuncs.h"
  29. #include "pgopen.h"
  30. #include "exit_codes.h"
  31.  
  32. #define PARSEARGS_PRIVATE   /* include private definitions */
  33. #include "parseargs.h"
  34.  
  35. EXTERN  VOID  syserr       ARGS((const char *, ...));
  36. EXTERN  VOID  usrerr       ARGS((const char *, ...));
  37. EXTERN  char *getenv       ARGS((const char *));
  38. EXTERN  VOID  get_winsize  ARGS((int, int *, int *));
  39.  
  40. VERSIONID("$Header: parseargs.c,v 2.1 89/12/30 20:59:48 eric Exp $");
  41.  
  42.  
  43. /***************************************************************************
  44. ** ^GLOBAL-VARIABLE: Usage_Requested
  45. **
  46. ** ^VISIBILITY:
  47. **    static-global (visible to all functions in this file).
  48. **
  49. ** ^DESCRIPTION:
  50. **    Indicates whether a usage message was requested by the user
  51. **    (as opposed to triggered by a syntax error).  If the message
  52. **    is requested by the user then it is always printed in verbose
  53. **    mode and does not return an error-status-code.
  54. ***^^**********************************************************************/
  55. static  BOOL  Usage_Requested = (BOOL) FALSE;
  56.  
  57.  
  58.    /* macros to detect an option/keyword -- watch out for side effects!! */
  59. #define isOPT(s)  \
  60.    ( !BTEST(cmd_flags(cmd), pa_KWDSONLY)  && \
  61.      !BTEST(cmd_state(cmd), ps_NOFLAGS)  && \
  62.      (*s == c_OPT_PFX)  &&  *(s+1) \
  63.    )
  64.  
  65. #ifndef POSIX_SOURCE
  66. #define isKWD(s)  \
  67.    ( !BTEST(cmd_flags(cmd), pa_OPTSONLY)  && \
  68.      !BTEST(cmd_state(cmd), ps_NOFLAGS)  && \
  69.      (*s == c_KWD_PFX)  &&  *(s+1) \
  70.    )
  71. #else
  72. #define isKWD(s)  \
  73.    ( !BTEST(cmd_flags(cmd), pa_OPTSONLY)  && \
  74.      !BTEST(cmd_state(cmd), ps_NOFLAGS)  && \
  75.      (*s == c_OPT_PFX)  &&  (*(s+1) == c_OPT_PFX)  &&  *(s+2) \
  76.    )
  77. #endif
  78.  
  79.  
  80. /***************************************************************************
  81. ** ^FUNCTION: unix_parse - parse Unix arg-vectors
  82. **
  83. ** ^SYNOPSIS:
  84. */
  85. #ifndef __ANSI_C__
  86.    int unix_parse( argv, argd )
  87. /*  
  88. ** ^PARAMETERS:
  89. */
  90.    char *argv[];
  91. /*    -- the vector of string arguments from the command-line
  92. */
  93.    ARGDESC argd[];
  94. /*    -- the programmer description of the command and its args
  95. */
  96. #endif  /* !__ANSI_C__ */
  97.  
  98. /* ^DESCRIPTION:
  99. **    Unix_parse will parse the arguments in the given vector of strings,
  100. **    assign the corresponding values to the command-line arguments specified
  101. **    in argd, and check the syntax of the command-line.
  102. **
  103. ** ^REQUIREMENTS:
  104. **    The final element in argv must be a NULL pointer.
  105. **
  106. ** ^SIDE-EFFECTS:
  107. **    argd is modified according to the command-line description and parameters
  108. **
  109. ** ^RETURN-VALUE:
  110. **    pe_SUCCESS (0) if no errors are encountered
  111. **    pe_SYSTEM (-1) if a system error is encountered
  112. **    pe_SYNTAX if a syntax error is encountered
  113. **
  114. ** ^ALGORITHM:
  115. **    - for each command-line argument
  116. **       - attempt to match the argument as a keyword
  117. **       - if it is a keyword argument
  118. **          - record and convert its value (if any)
  119. **         else attempt to match the argument as an option
  120. **         if it is an option
  121. **          - record and convert its value (if any)
  122. **         else it is a positional parameter
  123. **          - record and convert its value (if any)
  124. **         else there are too many arguments
  125. **          - return pe_SYNTAX
  126. **         end-if
  127. **       end-for
  128. ***^^**********************************************************************/
  129. #ifdef __ANSI_C__
  130.    int unix_parse( char *argv[], ARGDESC argd[] )
  131. #endif
  132. {
  133.    register ARGDESC *ad, *args, *cmd;
  134.    register char **av = argv;
  135.    register char *p;
  136.    argName_t  name;
  137.    argMask_t  flags;
  138.    int  parse_error = pe_SUCCESS;
  139.    BOOL  ad_okay, is_match = FALSE;
  140.  
  141.    if ( !argd )  return  parse_error;
  142.  
  143.       /* initialize command-structure */
  144.    if ( !CMD_isINIT(argd) )  init_args( argd );
  145.    cmd = argd;
  146.  
  147.    while ( av  &&  (p = *av++) ) {
  148.       if ( isKWD(p) ) {  /* we have a keyword here */
  149.          char *s, c = '\0';
  150.  
  151. #ifndef POSIX_SOURCE
  152.          /* check for `++' to end flags */
  153.          if ( *(p+1) == c_KWD_PFX  &&  !*(p+2) ) {
  154.             BSET( cmd_state(cmd), ps_NOFLAGS );
  155.             cmd_list(cmd) = ARGDESCNULL;
  156.             continue;
  157.          }
  158. #endif
  159.  
  160.             /* get past prefix and look for possible argument */
  161. #ifdef POSIX_SOURCE
  162.          ++p;
  163. #endif
  164.          s = strpbrk(++p, s_ARG_SEP);
  165.          if(s) {
  166.             c = *s;
  167.             *s++ = '\0';
  168.          }
  169.  
  170.          is_match = FALSE;
  171.          for ( args = argd ; args  &&  !is_match ; args = cmd_defargs(args) ) {
  172.             for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  173.                if ( arg_type(ad) == argDummy )  continue;
  174.  
  175.                if ( !ARG_isPOSONLY(ad)  &&  match(p, arg_sname(ad)) == 0 ) {
  176.                   is_match = TRUE;
  177.                   break;
  178.                }/*if*/
  179.             }
  180.          }
  181.  
  182.          if ( c )  *(s-1) = c;  /* restore the equal sign */
  183.  
  184.          if ( !is_match ) {
  185. #ifndef POSIX_SOURCE
  186.             usrerr("option %c%s unknown", c_KWD_PFX, p);
  187. #else
  188.             usrerr("option %c%c%s unknown", c_OPT_PFX, c_OPT_PFX, p);
  189. #endif
  190.             parse_error = pe_SYNTAX;
  191.             cmd_list(cmd) = ARGDESCNULL;
  192.             continue;
  193.          }
  194.  
  195.          /* reset the argument flags - if this arg was already given, some
  196.          ** of its flags may be set to indicate how it was given before. 
  197.          ** we need to know how it was given now (but save the old ones
  198.          ** just in case the new one fails).
  199.          */
  200.          flags = arg_flags(ad);
  201.          if ( ARG_isGIVEN(ad) ) {
  202.             BCLEAR( arg_flags(ad), ARGVALSEP | ARGKEYWORD );
  203.             if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  204.          }
  205.  
  206.          BSET( arg_flags(ad), ARGKEYWORD );
  207.  
  208.          if( ARG_isMULTIVAL(ad) ) { /* we matched a list (or a vector) */
  209.             cmd_list(cmd) = ad;
  210.          }
  211.          else {
  212.             cmd_list(cmd) = ARGDESCNULL;
  213.          }
  214.  
  215.             /* if usage - just print usage and exit */
  216.          if ( arg_type(ad) == argUsage ) {
  217.             Usage_Requested = TRUE;
  218.             usage(argd);
  219.             exit(exit_USAGE);
  220.          }
  221.  
  222.             /* ARGNOVALs are special, having no value */
  223.          if ( ! ARG_isVALTAKEN(ad) ) {
  224.             ad_okay = HANDLE(ad, s, cmd_flags(cmd));
  225.             if ( !ad_okay ) {
  226.                arg_flags(ad) = flags;
  227.                parse_error = pe_SYNTAX;
  228.             }
  229.             else {
  230.                BSET( arg_flags(ad), ARGGIVEN );
  231.                ad = ARGDESCNULL;
  232.             }
  233.             continue;
  234.          }/*if ARGNOVAL*/
  235.  
  236.             /* now get the real value */
  237.          if (!s) {
  238.             s = *av++;
  239.             if ( !s  ||  isOPT(s)  ||  isKWD(s) ) {
  240.                if ( ARG_isVALOPTIONAL(ad) ) {
  241.                   BSET( arg_flags(ad), ARGGIVEN );
  242.                }
  243.                else {
  244.                   (VOID) get_kwdname( arg_sname(ad), name );
  245.                   usrerr("option %c%s requires an argument", c_KWD_PFX, name);
  246.                   arg_flags(ad) = flags;
  247.                   parse_error = pe_SYNTAX;
  248.                }
  249.  
  250.                av--;
  251.                continue;
  252.             }/*if arg*/
  253.             BSET( arg_flags(ad), ARGVALSEP );
  254.          }/*if empty*/
  255.  
  256.             /* try to convert the type */
  257.          ad_okay = HANDLE(ad, s, cmd_flags(cmd));
  258.          if ( !ad_okay ) {
  259.             arg_flags(ad) = flags;
  260.             parse_error = pe_SYNTAX;
  261.          }
  262.          else {
  263.             BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  264.          }
  265.  
  266.          continue;
  267.       }/*if keyword*/
  268.  
  269.       else if ( isOPT(p) ) {
  270.          p++;  /* skip over option prefix */
  271.  
  272.             /* check for `--' to end flags */
  273.          if ( *p == c_OPT_PFX  &&  !*(p+1) ) {
  274.             BSET( cmd_state(cmd), ps_NOFLAGS );
  275.             cmd_list(cmd) = ARGDESCNULL;
  276.             continue;
  277.          }
  278.  
  279.          /* We have a flag argument;
  280.          ** remember that in the case of single character keywords,
  281.          ** the conversion function (ad_type) tells us how many characters
  282.          ** were used. We need that information to decide how many 
  283.          ** characters to skip before the next iteration of the while loop.
  284.          */
  285.          while (*p) {
  286.  
  287.                /* find the flag in the list */
  288.             is_match = FALSE;
  289.             for (args = argd; args  &&  !is_match ; args = cmd_defargs(args)) {
  290.                for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  291.                   register char c1 = arg_cname(ad);
  292.                   register char c2 = *p;
  293.  
  294.                   if ( arg_type(ad) == argDummy )   continue;
  295.                   if ( ARG_isPOSONLY(ad) )   continue;
  296.  
  297.                   if ( BTEST(cmd_flags(cmd), pa_ANYCASE) ) {
  298.                      c1 = TOUPPER( c1 );
  299.                      c2 = TOUPPER( c2 );
  300.                   }/*if*/
  301.  
  302.                   if ( c1 == c2 ) {
  303.                      is_match = TRUE;
  304.                      break;
  305.                   }/*if*/
  306.                }
  307.             }
  308.             if ( !is_match ) {
  309.                   usrerr("option %c%c unknown", c_OPT_PFX, *p++);
  310.                   parse_error = pe_SYNTAX;
  311.                   cmd_list(cmd) = ARGDESCNULL;
  312.                   continue;
  313.             }/* if unknown-option */
  314.  
  315.             /* reset the argument flags - if this arg was already given, some
  316.             ** of its flags may be set to indicate how it was given before.
  317.             ** we need to know how it was given now (but save the old ones
  318.             ** just in case the new one fails).
  319.             */
  320.             flags = arg_flags(ad);
  321.             if ( ARG_isGIVEN(ad) ) {
  322.                BCLEAR( arg_flags(ad), ARGVALSEP | ARGKEYWORD );
  323.                if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  324.             }
  325.  
  326.             if( ARG_isMULTIVAL(ad) ) {
  327.                cmd_list(cmd) = ad;  /* we matched a list (or a vector) */
  328.             }
  329.             else {
  330.                cmd_list(cmd) = ARGDESCNULL;
  331.             }
  332.  
  333.                /* move p up to point to the (possible) value */
  334.             p++;
  335.  
  336.             /* if usage - just print usage and exit */
  337.             if (arg_type(ad) == argUsage) {
  338.                Usage_Requested = TRUE;
  339.                usage(argd);
  340.                exit(exit_USAGE);
  341.             }
  342.  
  343.                /* ARGNOVALs are special, having no value */
  344.             if (! ARG_isVALTAKEN(ad)) {
  345.                ad_okay = HANDLE(ad, p, cmd_flags(cmd));
  346.  
  347.                if ( !ad_okay ) {
  348.                   arg_flags(ad) = flags;
  349.                   parse_error = pe_SYNTAX;
  350.                }/*if*/
  351.                else {
  352.                   BSET( arg_flags(ad), ARGGIVEN );
  353.                   ad = ARGDESCNULL;
  354.                   if ( ad_okay < 0 )  p -= ad_okay;
  355.                }/*else*/
  356.  
  357.                continue;
  358.             }/*if*/
  359.  
  360.                /* now get the real value */
  361.             if ( !(*p) ) {
  362.                p = *av++;
  363.                if ( !p  ||  isOPT(p)  ||  isKWD(p) ) {
  364.                   if ( ARG_isVALOPTIONAL(ad) ) {
  365.                      BSET( arg_flags(ad), ARGGIVEN );
  366.                   }
  367.                   else {
  368.                      (VOID) get_argname(arg_sname(ad), name);
  369.                      usrerr( "%s required for %c%c flag",
  370.                              name, c_OPT_PFX, arg_cname(ad) );
  371.                      arg_flags(ad) = flags;
  372.                      parse_error = pe_SYNTAX;
  373.                   }/*else*/
  374.  
  375.                   av--;
  376.                   break;
  377.                }/*if arg*/
  378.                BSET( arg_flags(ad), ARGVALSEP );
  379.             }/*if empty*/
  380.  
  381.                /* try to convert the type */
  382.             ad_okay = HANDLE(ad, p, cmd_flags(cmd));
  383.             if ( !ad_okay ) {
  384.                arg_flags(ad) = flags;
  385.                parse_error = pe_SYNTAX;
  386.                p += strlen(p);
  387.             }/*if*/
  388.             else {
  389.                BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  390.                if ( ad_okay < 0  &&  !ARG_isVALSEPARATE(ad) ) {
  391.                   p -= ad_okay;
  392.                }
  393.                else {
  394.                   p += strlen(p);
  395.                }
  396.             }/*else*/
  397.  
  398.          }/*while*/
  399.       }/*elif option*/
  400.       else {
  401.             /* parsing a list of arguments */
  402.          if ( cmd_list(cmd) ) {  /* we're in the middle of a list */
  403.             ad = cmd_list(cmd);
  404.  
  405.             /* reset the argument flags - if this arg was already given, some
  406.             ** of its flags may be set to indicate how it was given before.
  407.             ** we need to know how it was given now (but save the old ones
  408.             ** just in case the new one fails).
  409.             */
  410.             flags = arg_flags(ad);
  411.             if ( ARG_isGIVEN(ad) ) {
  412.                BCLEAR( arg_flags(ad), ARGVALSEP | ARGKEYWORD );
  413.             }
  414.  
  415.             BSET( arg_flags(ad), ARGVALSEP );
  416.  
  417.             ad_okay = HANDLE(ad, p, cmd_flags(cmd));
  418.             if ( !ad_okay ) {
  419.                arg_flags(ad) = flags;
  420.                parse_error = pe_SYNTAX;
  421.             }
  422.  
  423.             continue;
  424.          }
  425.             /* positional argument */
  426.          is_match = FALSE;
  427.          for (args = argd; args  &&  !is_match ; args = cmd_defargs(args)) {
  428.             for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  429.                if (arg_type(ad) == argDummy)  continue;
  430.  
  431.                if ( ARG_isPOSITIONAL(ad)  &&
  432.                     (!ARG_isGIVEN(ad) ||  ARG_isMULTIVAL(ad)) ) {
  433.                   is_match = TRUE;
  434.                   break;
  435.                }/*if*/
  436.             }
  437.          }
  438.  
  439.          if ( !is_match ) {
  440.             usrerr("too many arguments");
  441.             parse_error = pe_SYNTAX;
  442.             continue;
  443.          }
  444.  
  445.          /* reset the argument flags - if this arg was already given, some
  446.          ** of its flags may be set to indicate how it was given before.
  447.          ** we need to know how it was given now (but save the old ones
  448.          ** just in case the new one fails).
  449.          */
  450.          flags = arg_flags(ad);
  451.          if ( ARG_isGIVEN(ad) ) {
  452.             BCLEAR( arg_flags(ad), ARGVALSEP | ARGKEYWORD );
  453.             if ( !ARG_isMULTIVAL(ad) )  BCLEAR( arg_flags(ad), ARGVALGIVEN );
  454.          }
  455.  
  456.          if ( ARG_isMULTIVAL(ad) ) {  /* we positionally matched a list */
  457.             cmd_list(cmd) = ad;
  458.          }
  459.  
  460.          /* if FLAGS1ST is set then first positional marks end-of-flags */
  461.          if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) ) {
  462.             BSET( cmd_state(cmd), ps_NOFLAGS );
  463.          }
  464.  
  465.          BSET( arg_flags(ad), ARGVALSEP );
  466.  
  467.             /* try to convert */
  468.          ad_okay = HANDLE(ad, p, cmd_flags(cmd));
  469.          if ( !ad_okay ) {
  470.             arg_flags(ad) = flags;
  471.             parse_error = pe_SYNTAX;
  472.          }
  473.          else {
  474.             BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
  475.          }
  476.       }/*else*/
  477.    }/*while*/
  478.  
  479.    return  parse_error;
  480. }
  481.  
  482.  
  483. /***************************************************************************
  484. ** ^FUNCTION: fmtarg - format command-argument syntax
  485. **
  486. ** ^SYNOPSIS:
  487. */
  488. #ifndef __ANSI_C__
  489.    static int fmtarg( ad, buf, usgflags )
  490. /*
  491. ** ^PARAMETERS:
  492. */
  493.    ARGDESC *ad;
  494. /*    -- pointer to the argument to format
  495. */
  496.    char *buf;
  497. /*    -- character buffer to hold the formatted result
  498. */
  499.    argMask_t usgflags;
  500. /*    -- set of bitmasks corresponding to the value of the user's USAGECNTL
  501. **       environment variable
  502. */
  503. #endif  /* !__ANSI_C__ */
  504.  
  505. /* ^DESCRIPTION:
  506. **    Fmtarg will determine the proper command-line syntax for the
  507. **    given argument and write the result to the given buffer.
  508. **
  509. ** ^REQUIREMENTS:
  510. **    buf must be large enough to hold the formatted result (100 characters
  511. **    should do the trick).
  512. **
  513. ** ^SIDE-EFFECTS:
  514. **    buf is overwritten.
  515. **
  516. ** ^RETURN-VALUE:
  517. **    The number of printable characters in the argument-syntax-string
  518. **
  519. ** ^ALGORITHM:
  520. **    Print argument usage based on whether or not the argument is
  521. **    positional, hidden, multi-valued (list or vector), etc ....
  522. **    Optional arguments and values are enclosed in square braces.
  523. **
  524. **    Any syntax biases reflected in usgflags will be used.
  525. ***^^**********************************************************************/
  526. #ifdef __ANSI_C__
  527.    static int fmtarg ( const ARGDESC *ad, char *buf, argMask_t usgflags )
  528. #endif
  529. {
  530.    /* buf must already be large enough */
  531.    char *pos;
  532.    argName_t   name, keyword;
  533.  
  534.    (VOID) get_argname( arg_sname(ad), name );
  535.  
  536.    if (ARG_isPOSITIONAL(ad)) {
  537.       sprintf( buf, "<%s>", name );
  538.    }
  539.    else {
  540.       (VOID) get_kwdname( arg_sname(ad), keyword );
  541.  
  542.       if ( isupper(arg_cname(ad))  &&  toupper(*keyword) == arg_cname(ad) ) {
  543.          *keyword = toupper(*keyword);
  544.       }
  545.  
  546.       if ( !(usgflags & usg_LONGOPTS) ) {
  547.          sprintf( buf, "%c%c", c_OPT_PFX, arg_cname(ad) );
  548.       }
  549.       else if ( !(usgflags & usg_OPTS) ) {
  550. #ifndef POSIX_SOURCE
  551.          sprintf( buf, "%c%s", c_KWD_PFX, keyword );
  552. #else
  553.          sprintf( buf, "%c%c%s", c_OPT_PFX, c_OPT_PFX, keyword );
  554. #endif
  555.       }
  556.       else  {  /* use both */
  557. #ifndef POSIX_SOURCE
  558.          sprintf( buf, "%c%c|%c%s", c_OPT_PFX, arg_cname(ad),
  559.                                     c_KWD_PFX, keyword );
  560. #else
  561.          sprintf( buf, "%c%c|%c%c%s", c_OPT_PFX, arg_cname(ad),
  562.                                       c_OPT_PFX, c_OPT_PFX, keyword );
  563. #endif
  564.       }
  565.  
  566.       pos = buf + strlen(buf);
  567.  
  568.       if ( ARG_isVALTAKEN(ad)  &&  !ARG_isBOOLEAN(ad) &&  !ARG_isPSEUDOARG(ad) )
  569.       {
  570.          *(pos++) = ' ';
  571.  
  572.          if  (ARG_isVALOPTIONAL(ad)) {
  573.             sprintf( pos, "[<%s>]", name);
  574.          }
  575.          else {
  576.             sprintf( pos, "<%s>", name );
  577.          }
  578.       }/*if*/
  579.    }/*else*/
  580.  
  581.    return  strlen(buf);
  582. }
  583.  
  584.  
  585. /***************************************************************************
  586. ** ^FUNCTION: unix_usage - print a usage message
  587. **
  588. ** ^SYNOPSIS:
  589. */
  590. #ifndef __ANSI_C__
  591.    VOID unix_usage( argd, usage_flags )
  592. /*
  593. ** ^PARAMETERS:
  594. */
  595.    ARGDESC *argd;
  596. /*    -- the command-descriptor array
  597. */
  598.    argMask_t usage_flags;
  599. /*    -- flags set by $USAGECNTL
  600. */
  601. #endif  /* !__ANSI_C__ */
  602.  
  603. /* ^DESCRIPTION:
  604. **    Unix_usage will print the Unix command-line usage of the given
  605. **    command on standard diagnostic output (stderr). The content of the
  606. **    usage message is controlled by the bitmasks in usage_flags which
  607. **    correspond to the settings in the user's USAGECNTL variable.
  608. **
  609. ** ^REQUIREMENTS:
  610. **    argd should be a non-null command-line argument-descriptor array
  611. **
  612. ** ^SIDE-EFFECTS:
  613. **    Prints on stderr.
  614. **
  615. ** ^RETURN-VALUE:
  616. **    None.
  617. **
  618. ** ^ALGORITHM:
  619. **    - if no usage is desired then exit
  620. **    - if paging is requested print to the pager instead of stderr
  621. **    - print the command-line syntax
  622. **    - if the description is requested print it
  623. **    - if verbose mode is requested, print the description of each argument
  624. ***^^**********************************************************************/
  625. #ifdef __ANSI_C__
  626.    void unix_usage ( const ARGDESC *argd, argMask_t usage_flags )
  627. #endif
  628. {
  629.    register CONST ARGDESC *ad, *args, *cmd;
  630.    int  max_cols = 80, max_lines  = 24;
  631.    int  ll, margin, options, longest, positionals;
  632.    BOOL first = TRUE;
  633.    FILE *fp;
  634.  
  635.    if ( !argd )  return;
  636.  
  637.       /* initialize command-structure */
  638.    if ( !CMD_isINIT(argd) )  init_args( (ARGDESC *)argd );
  639.    cmd = argd;
  640.  
  641.       /* force verbose-mode if requested */
  642.    if ( Usage_Requested )   BSET( usage_flags, usg_VERBOSE );
  643.  
  644.    if ( BTEST(usage_flags, usg_NONE) )  return;
  645.  
  646.    fp = ( BTEST(usage_flags, usg_PAGED) )
  647.       ? pgopen( stderr, getenv("USAGE_PAGER") )
  648.       : stderr;
  649.  
  650.       /* get screen size */
  651.    get_winsize( fileno(fp), &max_lines, &max_cols );
  652.  
  653.    fprintf(fp, "Usage: %.*s", ProgNameLen, (ProgName) ? ProgName : "");
  654.  
  655.    ll = ProgNameLen + 7;
  656.    margin = ll + 1;
  657.    longest = 0;
  658.  
  659.       /* print Synopsis */
  660.    for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
  661.       for ( args = argd ; args ; args = cmd_defargs(args) ) {
  662.          for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  663.             argName_t  buf;
  664.             int pl;
  665.  
  666.                /* don't display hidden arguments */
  667.             if ( ARG_isHIDDEN(ad) )  continue;
  668.             if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
  669.             if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
  670.  
  671.                /* figure out how wide this parameter is (for printing) */
  672.             pl = fmtarg(ad, buf, usage_flags);
  673.  
  674.             if ( pl > longest)  longest = pl;
  675.  
  676.             if  ( ARG_isMULTIVAL(ad) ) {
  677.                strcat( buf, "..." );
  678.                pl += 3;
  679.             }
  680.             if ( !ARG_isREQUIRED(ad) ) {
  681.                pl += 2;
  682.             }
  683.  
  684.             /* see if this will fit */
  685.             if ( (ll + pl + 1) > (max_cols - first) ) {
  686.                   /* no... start a new line */
  687.                fprintf(fp, "\n%*s", margin, "");
  688.                ll = margin;
  689.             }
  690.             else {
  691.                   /* yes... just throw in a space */
  692.                fputc(' ', fp);
  693.                ++ll;
  694.             }
  695.             ll += pl;
  696.  
  697.                /* show the argument */
  698.             if ( !ARG_isREQUIRED(ad) )  fputc('[', fp);
  699.             fprintf(fp, buf);
  700.             if ( !ARG_isREQUIRED(ad) )  fputc(']', fp);
  701.  
  702.             first = FALSE;  /* not first line anymore */
  703.          }/*for each ad */
  704.       }/* for each argd */
  705.    }/* for each parm-type */
  706.  
  707.    fputc('\n', fp);
  708.  
  709.    if ( BTEST(usage_flags, usg_DESCRIPTION) ) {
  710.       CONST char *description = cmd_description(cmd);
  711.  
  712.       if ( description  &&  *description ) {
  713.          fprintf( fp, "Description:\n" );
  714.          indent_para(fp, max_cols, 8, "", 0, description, 0);
  715.          fputc( '\n', fp );
  716.       }
  717.    }/*if*/
  718.  
  719.    if ( !BTEST(usage_flags, usg_VERBOSE) )  {
  720.       if ( pgactive(fp) )  (VOID) pgclose( fp );
  721.       return;
  722.    }
  723.  
  724.    options = 0;
  725.  
  726.       /* print Argument descriptions */
  727.    for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
  728.       for ( args = argd ; args ; args = cmd_defargs(args) ) {
  729.          for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  730.             argName_t  buf;
  731.             char  *desc;
  732.             int  desclen;
  733.  
  734.                /* don't display hidden arguments */
  735.             if ( ARG_isHIDDEN(ad) )  continue;
  736.             if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
  737.             if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
  738.  
  739.             if ( !options++ )   fprintf(fp, "Options/Arguments:\n");
  740.             fmtarg(ad, buf, usage_flags);
  741.             desc = get_argdesc(arg_description(ad), &desclen);
  742.             indent_para( fp, max_cols, 8, buf, longest+2, desc, desclen );
  743.          }/*for each ad */
  744.       }/* for each argd */
  745.    }/* for each parm-type */
  746.  
  747.    if ( pgactive(fp) )  (VOID) pgclose( fp );
  748. }
  749.  
  750.