home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / cmdline.lha / cmdline / src / lib / unix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-13  |  26.5 KB  |  822 lines

  1. //------------------------------------------------------------------------
  2. // ^FILE: unix.c - implement the unix-specific portions of CmdLine
  3. //
  4. // ^DESCRIPTION:
  5. //     This file implements the public and private CmdLine library functions
  6. //  that are specific to the native command-line syntax of Unix.
  7. //
  8. //  The following functions are implemented:
  9. //
  10. //     CmdLine::parse_option() -- parse an option
  11. //     CmdLine::parse_keyword() -- parse a keyword
  12. //     CmdLine::parse_value() -- parse a value
  13. //     CmdLine::parse_arg() -- parse a single argument
  14. //     CmdLine::arg_error() -- format an argument for error messages
  15. //     CmdLine::fmt_arg()   -- format an argument for usage messages
  16. //
  17. // ^HISTORY:
  18. //    01/09/92    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  19. //
  20. //    03/01/93    Brad Appleton    <brad@ssd.csd.harris.com>
  21. //    - Added ALLOW_PLUS to list of CmdLine configuration flags
  22. //-^^---------------------------------------------------------------------
  23.  
  24. #include <iostream.h>
  25. #include <strstream.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29. #include "exits.h"
  30. #include "cmdline.h"
  31. #include "states.h"
  32.  
  33. //
  34. // Some Helper function for getting and recognizing prefixes
  35. //
  36.  
  37.   // Function to tell us if an argument looks like an option
  38. inline static int
  39. isOPTION(const char * s)  {
  40.    return  ((*(s) == '-') && ((*((s)+1) != '-')) && ((*((s)+1) != '\0'))) ;
  41. }
  42.  
  43.    // Function to return the option-prefix
  44. inline static const char *
  45. OptionPrefix(void) { return  "-" ; }
  46.  
  47.  
  48.    // Function to tell us if an argument looks like a long-option.
  49.    //
  50.    // NOTE: allowing "+" does not preclude the use of "--"
  51.    //
  52. inline static int
  53. isKEYWORD(const char *s, int allow_plus) {
  54.    return  (((*(s) == '-') && (*((s)+1) == '-')  && (*((s)+2) != '\0')) ||
  55.             (allow_plus && (*(s) == '+') && ((*((s)+1) != '\0')))) ;
  56. }
  57.  
  58.    // Function to return the long-option prefix
  59. inline static const char *
  60. KeywordPrefix(int allow_plus) { return  (allow_plus) ? "+" : "--" ; }
  61.  
  62.    // Need to know when an argument means "end-of-options"
  63. inline static int
  64. isENDOPTIONS(const char *s) {
  65.    return  ((*(s) == '-') && (*((s)+1) == '-')  && (*((s)+2) == '\0')) ;
  66. }
  67.  
  68.    // Function to return the "end-of-options" string
  69. inline static const char *
  70. EndOptions(void) { return "--" ; }
  71.  
  72.  
  73. //-------
  74. // ^FUNCTION: CmdLine::parse_option - parse a Unix option
  75. //
  76. // ^SYNOPSIS:
  77. //    unsigned CmdLine::parse_option(arg);
  78. //
  79. // ^PARAMETERS:
  80. //    const char * arg;
  81. //    -- the command-line argument containing the prospective option
  82. //
  83. // ^DESCRIPTION:
  84. //    This routine will attempt to "handle" all options specified in
  85. //    the string "arg". For each option found, its compile-function
  86. //    is called and the corresponding state of both the command
  87. //    and of the matched option(s) is (are) updated.
  88. //
  89. // ^REQUIREMENTS:
  90. //    "arg" should point past any leading option prefix (such as "-").
  91. //
  92. // ^SIDE-EFFECTS:
  93. //    "cmd" is modified accordingly as each option is parsed (as are its
  94. //    constituent arguments). If there are syntax errors then error messages
  95. //    are printed if QUIET is NOT set.
  96. //
  97. // ^RETURN-VALUE:
  98. //    NO_ERROR is returned if no errors were encountered. Otherwise the
  99. //    return value is a combination of bitmasks of type CmdLine::CmdStatus
  100. //    (defined in <cmdline.h>) indicating all the problems that occurred.
  101. //
  102. // ^ALGORITHM:
  103. //    see if we left an option dangling without parsing its value.
  104. //    for each option bundled in "arg"
  105. //       try to match the option
  106. //       if no match issue a message (unless QUIET is set)
  107. //       else
  108. //          if the option takes NO argument than handle that
  109. //          else if the option takes an argument
  110. //             if the rest or arg is not empty then
  111. //                call the option's compile-function using the rest of
  112. //                                                    arg as the value.
  113. //                skip past whatever portion of value was used
  114. //             else
  115. //                update the state of the command to show that we are expecting
  116. //                       to see the value for this option as the next argument.
  117. //             endif
  118. //          endif
  119. //          update the state of the argument.
  120. //       endif
  121. //    endfor
  122. //-^^----
  123. unsigned
  124. CmdLine::parse_option(const char * arg)
  125. {
  126.    const  char * save_arg = arg;
  127.    unsigned  save_flags = 0, rc = 0 ;
  128.    CmdArg * cmdarg = NULL;
  129.    int  bad_val;
  130.  
  131.    // see if we left an argument dangling without a value
  132.    ck_need_val() ;
  133.  
  134.    do {  // loop over bundled options
  135.       cmdarg = opt_match(*arg);
  136.       if (cmdarg == NULL) {
  137.          // If we were in the middle of a guess - sorry no cigar, otherwise
  138.          // guess that maybe this is a keyword and not an keyword.
  139.          //
  140.          if (cmd_state & cmd_GUESSING) {
  141.             if (arg == save_arg)  return  BAD_OPTION;
  142.          } else {
  143.             if (! (cmd_flags & NO_GUESSING)) {
  144.                cmd_state |= cmd_GUESSING;
  145.                rc = parse_keyword(arg);
  146.                cmd_state &= ~cmd_GUESSING;
  147.                if (rc != BAD_KEYWORD)  return  rc;
  148.             }
  149.          }
  150.          if (! (cmd_flags & QUIET)) {
  151.             error() << "unknown option \"" << OptionPrefix() << char(*arg)
  152.                     << "\"." << endl ;
  153.          }
  154.          rc |= BAD_OPTION ;
  155.          ++arg ;  // skip bad option
  156.          continue ;
  157.       }
  158.       ++arg ;  // skip past option character
  159.       save_flags = cmdarg->flags() ;
  160.       cmdarg->clear();
  161.       cmdarg->set(CmdArg::OPTION) ;
  162.       if ((! *arg) && (cmdarg->syntax() & CmdArg::isVALTAKEN)) {
  163.          // End of string -- value must be in next arg
  164.          // Save this cmdarg-pointer for later and set the parse_state to
  165.          // indicate that we are expecting a value.
  166.          //
  167.  
  168.          if (cmdarg->syntax() & CmdArg::isVALSTICKY) {
  169.             // If this argument is sticky we already missed our chance
  170.             // at seeing a value.
  171.             //
  172.             if (cmdarg->syntax() & CmdArg::isVALREQ) {
  173.                if (! (cmd_flags & QUIET)) {
  174.                   error() << "value required in same argument for "
  175.                           << OptionPrefix() << char(cmdarg->char_name())
  176.                           << " option." << endl;
  177.                }
  178.                rc |= (VAL_MISSING | VAL_NOTSTICKY) ;
  179.                cmdarg->flags(save_flags);
  180.             } else {
  181.                // The value is optional - set the GIVEN flag and call
  182.                // handle_arg with NULL (and check the result).
  183.                //
  184.                const char * null_str = NULL;
  185.                cmdarg->set(CmdArg::GIVEN) ;
  186.                cmd_parse_state = cmd_START_STATE ;
  187.                bad_val = handle_arg(cmdarg, null_str);
  188.                if (bad_val) {
  189.                   if (! (cmd_flags & QUIET)) {
  190.                      arg_error("bad value for", cmdarg) << "." << endl ;
  191.                   }
  192.                   rc |= BAD_VALUE ;
  193.                   cmdarg->flags(save_flags);
  194.                }
  195.             }
  196.          } else {
  197.             // Wait for the value to show up next time around
  198.             cmdarg->set(CmdArg::GIVEN) ;
  199.             cmd_matched_arg = cmdarg ;
  200.             cmd_parse_state = cmd_WANT_VAL ;
  201.             if (cmdarg->syntax() & CmdArg::isVALREQ) {
  202.                cmd_parse_state += cmd_TOK_REQUIRED ;
  203.             }
  204.          }
  205.          return  rc ;
  206.       }
  207.  
  208.       // If this option is an isVALSEP and "arg" is not-empty then we
  209.       // have an error.
  210.       //
  211.       if ((cmdarg->syntax() & CmdArg::isVALTAKEN) &&
  212.           (cmdarg->syntax() & CmdArg::isVALSEP)) {
  213.          if (! (cmd_flags & QUIET)) {
  214.             error() << "value required in separate argument for "
  215.                     << OptionPrefix() << char(cmdarg->char_name())
  216.                     << " option." << endl;
  217.          }
  218.          rc |= (VAL_MISSING | VAL_NOTSEP) ;
  219.          cmdarg->flags(save_flags);
  220.          return  rc;
  221.       } else {
  222.          // handle the option
  223.          const char * save_arg = arg;
  224.          bad_val = handle_arg(cmdarg, arg);
  225.          if (bad_val) {
  226.             if (! (cmd_flags & QUIET)) {
  227.                arg_error("bad value for", cmdarg) << "." << endl ;
  228.             }
  229.             rc |= BAD_VALUE ;
  230.             cmdarg->flags(save_flags);
  231.          }
  232.          cmdarg->set(CmdArg::GIVEN);
  233.          if (arg != save_arg)  cmdarg->set(CmdArg::VALGIVEN);
  234.       }
  235.    } while (arg && *arg) ;
  236.  
  237.    return  rc ;
  238. }
  239.  
  240.  
  241. //-------
  242. // ^FUNCTION: CmdLine::parse_keyword - parse a Unix keyword
  243. //
  244. // ^SYNOPSIS:
  245. //    unsigned CmdLine::parse_keyword(arg);
  246. //
  247. // ^PARAMETERS:
  248. //    const char * arg;
  249. //    -- the command-line argument containing the prospective keyword
  250. //
  251. // ^DESCRIPTION:
  252. //    This routine will attempt to "handle" the keyword specified in
  253. //    the string "arg". For any keyword found, its compile-function
  254. //    is called and the corresponding state of both the command
  255. //    and of the matched keyword(s) is (are) updated.
  256. //
  257. // ^REQUIREMENTS:
  258. //    "arg" should point past any leading keyword prefix (such as "--").
  259. //
  260. // ^SIDE-EFFECTS:
  261. //    "cmd" is modified accordingly as the keyword is parsed (as are its
  262. //    constituent arguments). If there are syntax errors then error messages
  263. //    are printed if QUIET is NOT set.
  264. //
  265. // ^RETURN-VALUE:
  266. //    NO_ERROR is returned if no errors were encountered. Otherwise the
  267. //    return value is a combination of bitmasks of type CmdLine::CmdStatus
  268. //    (defined in <cmdline.h>) indicating all the problems that occurred.
  269. //
  270. // ^ALGORITHM:
  271. //    see if we left an option dangling without parsing its value.
  272. //    look for a possible value for this keyword denoted by ':' or '='
  273. //    try to match "arg" as a keyword
  274. //    if no match issue a message (unless QUIET is set)
  275. //    else
  276. //       if the keyword takes NO argument than handle that
  277. //       else if the keyword takes an argument
  278. //          if a value was found "arg"
  279. //             call the keyword's compile-function with the value found.
  280. //          else
  281. //             update the state of the command to show that we are expecting
  282. //                    to see the value for this option as the next argument.
  283. //          endif
  284. //       endif
  285. //       update the state of the argument.
  286. //    endif
  287. //-^^----
  288. unsigned
  289. CmdLine::parse_keyword(const char * arg)
  290. {
  291.    unsigned  save_flags = 0, rc = 0 ;
  292.    CmdArg * cmdarg = NULL ;
  293.    int  ambiguous = 0, len = -1, bad_val;
  294.    const char * val = NULL ;
  295.  
  296.    int  plus = (cmd_flags & ALLOW_PLUS) ;  // Can we use "+"?
  297.  
  298.    // see if we left an argument dangling without a value
  299.    ck_need_val() ;
  300.  
  301.    // If there is a value with this argument, get it now!
  302.    val = ::strpbrk(arg, ":=") ;
  303.    if (val) {
  304.       len = val - arg ;
  305.       ++val ;
  306.    }
  307.  
  308.    cmdarg = kwd_match(arg, len, ambiguous);
  309.    if (cmdarg == NULL) {
  310.       // If we were in the middle of a guess - sorry no cigar, otherwise
  311.       // guess that maybe this is an option and not a keyword.
  312.       //
  313.       if (cmd_state & cmd_GUESSING) {
  314.          return  BAD_KEYWORD;
  315.       } else if ((! ambiguous) || (len == 1)) {
  316.          if (! (cmd_flags & NO_GUESSING)) {
  317.             cmd_state |= cmd_GUESSING;
  318.             rc = parse_option(arg);
  319.             cmd_state &= ~cmd_GUESSING;
  320.             if (rc != BAD_OPTION)  return  rc;
  321.          }
  322.       }
  323.       if (! (cmd_flags & QUIET)) {
  324.          error() << ((ambiguous) ? "ambiguous" : "unknown") << " option "
  325.                  << "\"" << ((cmd_flags & KWDS_ONLY) ? OptionPrefix()
  326.                                                      : KeywordPrefix(plus))
  327.                  << arg << "\"." << endl ;
  328.       }
  329.       rc |= ((ambiguous) ? KWD_AMBIGUOUS : BAD_KEYWORD) ;
  330.       return  rc ;
  331.    }
  332.  
  333.    save_flags = cmdarg->flags() ;
  334.    cmdarg->clear();
  335.    cmdarg->set(CmdArg::KEYWORD) ;
  336.    if ((cmdarg->syntax() & CmdArg::isVALTAKEN) && (val == NULL)) {
  337.       // Value must be in the next argument.
  338.       // Save this cmdarg for later and indicate that we are
  339.       // expecting a value.
  340.       //
  341.       if (cmdarg->syntax() & CmdArg::isVALSTICKY) {
  342.          // If this argument is sticky we already missed our chance
  343.          // at seeing a value.
  344.          //
  345.          if (cmdarg->syntax() & CmdArg::isVALREQ) {
  346.             if (! (cmd_flags & QUIET)) {
  347.                error() << "value required in same argument for "
  348.                        << ((cmd_flags & KWDS_ONLY) ? OptionPrefix()
  349.                                                    : KeywordPrefix(plus))
  350.                        << cmdarg->keyword_name() << " option." << endl;
  351.             }
  352.             rc |= (VAL_MISSING | VAL_NOTSTICKY) ;
  353.             cmdarg->flags(save_flags);
  354.          } else {
  355.             // The value is optional - set the GIVEN flag and call
  356.             // handle_arg with NULL (and check the result).
  357.             //
  358.             const char * null_str = NULL;
  359.             cmdarg->set(CmdArg::GIVEN) ;
  360.             cmd_parse_state = cmd_START_STATE ;
  361.             bad_val = handle_arg(cmdarg, null_str);
  362.             if (bad_val) {
  363.                if (! (cmd_flags & QUIET)) {
  364.                   arg_error("bad value for", cmdarg) << "." << endl ;
  365.                }
  366.                rc |= BAD_VALUE ;
  367.                cmdarg->flags(save_flags);
  368.             }
  369.          }
  370.       } else {
  371.          // Wait for the value to show up next time around
  372.          cmdarg->set(CmdArg::GIVEN) ;
  373.          cmd_matched_arg = cmdarg ;
  374.          cmd_parse_state = cmd_WANT_VAL ;
  375.          if (cmdarg->syntax() & CmdArg::isVALREQ) {
  376.             cmd_parse_state += cmd_TOK_REQUIRED ;
  377.          }
  378.       }
  379.       return  rc ;
  380.    }
  381.  
  382.    // If this option is an isVALSEP and "val" is not-NULL then we
  383.    // have an error.
  384.    //
  385.    if (val  &&  (cmdarg->syntax() & CmdArg::isVALTAKEN) &&
  386.        (cmdarg->syntax() & CmdArg::isVALSEP)) {
  387.       if (! (cmd_flags & QUIET)) {
  388.          error() << "value required in separate argument for "
  389.                  << ((cmd_flags & KWDS_ONLY) ? OptionPrefix()
  390.                                              : KeywordPrefix(plus))
  391.                  << cmdarg->keyword_name() << " option." << endl;
  392.       }
  393.       rc |= (VAL_MISSING | VAL_NOTSEP) ;
  394.       cmdarg->flags(save_flags);
  395.       return  rc;
  396.    }
  397.    // handle the keyword
  398.    bad_val = handle_arg(cmdarg, val);
  399.    if (bad_val) {
  400.       if (! (cmd_flags & QUIET)) {
  401.          arg_error("bad value for", cmdarg) << "." << endl ;
  402.       }
  403.       rc |= BAD_VALUE ;
  404.       cmdarg->flags(save_flags);
  405.    }
  406.  
  407.    return  rc ;
  408. }
  409.  
  410.  
  411. //-------
  412. // ^FUNCTION: CmdLine::parse_value - parse a Unix value
  413. //
  414. // ^SYNOPSIS:
  415. //    unsigned CmdLine::parse_value(arg);
  416. //
  417. // ^PARAMETERS:
  418. //    const char * arg;
  419. //    -- the command-line argument containing the prospective value
  420. //
  421. // ^DESCRIPTION:
  422. //    This routine will attempt to "handle" the value specified in
  423. //    the string "arg". The compile-function of the corresponding
  424. //    argument-value is called and the corresponding state of both
  425. //    the command and of the matched option(s) is (are) updated.
  426. //    If the value corresponds to a multi-valued argument, then that
  427. //    is handled here.
  428. //
  429. // ^REQUIREMENTS:
  430. //
  431. // ^SIDE-EFFECTS:
  432. //    "cmd" is modified accordingly for the value that is parsed (as are its
  433. //    constituent arguments). If there are syntax errors then error messages
  434. //    are printed if QUIET is NOT set.
  435. //
  436. // ^RETURN-VALUE:
  437. //    NO_ERROR is returned if no errors were encountered. Otherwise the
  438. //    return value is a combination of bitmasks of type CmdLine::CmdStatus
  439. //    (defined in <cmdline.h>) indicating all the problems that occurred.
  440. //
  441. // ^ALGORITHM:
  442. //    If the command-state says we are waiting for the value of an option
  443. //       find the option that we matched last
  444. //    else
  445. //       match the next positional parameter in "cmd"
  446. //       if there isnt one issue a "too many args" message
  447. //               (unless cmd_QUIETs is set) and return.
  448. //    endif
  449. //    handle the given value and update the argument and command states.
  450. //-^^----
  451. unsigned
  452. CmdLine::parse_value(const char * arg)
  453. {
  454.    unsigned  save_flags = 0, rc = 0 ;
  455.    int  bad_val;
  456.    CmdArg * cmdarg = NULL;
  457.  
  458.    if (cmd_parse_state & cmd_WANT_VAL) {
  459.       if (cmd_matched_arg == NULL) {
  460.          cerr << "*** Internal error in class CmdLine.\n"
  461.               << "\tparse-state is inconsistent with last-matched-arg."
  462.               << endl ;
  463.          ::exit(e_INTERNAL);
  464.       }
  465.       // get back the cmdarg that we saved for later
  466.       // - here is the value it was expecting
  467.       //
  468.       cmdarg = cmd_matched_arg ;
  469.       save_flags = cmdarg->flags() ;
  470.    } else {
  471.       // argument is positional - find out which one it is
  472.       cmdarg = pos_match() ;
  473.       if (cmdarg == NULL) {
  474.          if (! (cmd_flags & QUIET)) {
  475.             error() << "too many arguments given." << endl ;
  476.          }
  477.          rc |= TOO_MANY_ARGS ;
  478.          return  rc ;
  479.       }
  480.       save_flags = cmdarg->flags() ;
  481.       cmdarg->clear();
  482.       cmdarg->set(CmdArg::POSITIONAL) ;
  483.       if (cmd_flags & OPTS_FIRST) {
  484.          cmd_state |= cmd_END_OF_OPTIONS ;
  485.       }
  486.    }
  487.  
  488.    // handle this value
  489.    cmdarg->set(CmdArg::VALSEP) ;
  490.    bad_val = handle_arg(cmdarg, arg);
  491.    if (bad_val) {
  492.       if (! (cmd_flags & QUIET)) {
  493.          arg_error("bad value for", cmdarg) << "." << endl ;
  494.       }
  495.       rc |= BAD_VALUE ;
  496.       cmdarg->flags(save_flags);
  497.       if (! (cmdarg->syntax() & CmdArg::isLIST)) {
  498.          cmd_parse_state = cmd_START_STATE;
  499.       }
  500.    }
  501.  
  502.    // If the value was okay and we were requiring a value, then
  503.    // a value is no longer required.
  504.    //
  505.    if ((! bad_val) && (cmdarg->syntax() & CmdArg::isLIST)) {
  506.       cmd_parse_state &= ~cmd_TOK_REQUIRED ;
  507.    }
  508.  
  509.    return  rc ;
  510. }
  511.  
  512.  
  513. //-------
  514. // ^FUNCTION: CmdLine::parse_arg - parse an argv[] element unix-style
  515. //
  516. // ^SYNOPSIS:
  517. //    unsigned CmdLine::parse_arg(arg)
  518. //
  519. // ^PARAMETERS:
  520. //    const char * arg;
  521. //    -- an argument string (argv[] element) from the command-line
  522. //
  523. // ^DESCRIPTION:
  524. //    This routine will determine whether "arg" is an option, a long-option,
  525. //    or a value and call the appropriate parse_xxxx function defined above.
  526. //
  527. // ^REQUIREMENTS:
  528. //
  529. // ^SIDE-EFFECTS:
  530. //    "cmd" is modified accordingly for the string that is parsed (as are its
  531. //    constituent arguments). If there are syntax errors then error messages
  532. //    are printed if QUIET is NOT set.
  533. //
  534. // ^RETURN-VALUE:
  535. //    NO_ERROR is returned if no errors were encountered. Otherwise the
  536. //    return value is a combination of bitmasks of type CmdLine::CmdStatus
  537. //    (defined in <cmdline.h>) indicating all the problems that occurred.
  538. //
  539. // ^ALGORITHM:
  540. //    if we are expecting a required value
  541. //       call parse_value()
  542. //    else if "arg" is an option
  543. //       skip past the option prefix
  544. //       call parse_option()
  545. //    else if "arg" is a keyword
  546. //       skip past the kewyord prefix
  547. //       call parse_keyword()
  548. //    else if "arg" is "--" (meaning end of options)
  549. //       see that we didnt leave an option dangling without a value
  550. //       indicate end-of-options in the command-state
  551. //    else
  552. //       call parse_value()
  553. //    endif
  554. //-^^----
  555. unsigned
  556. CmdLine::parse_arg(const char * arg)
  557. {
  558.    if (arg == NULL)  return  cmd_status ;
  559.  
  560.    int  plus = (cmd_flags & ALLOW_PLUS) ;  // Can we use "+"?
  561.  
  562.    if (cmd_parse_state & cmd_TOK_REQUIRED) {
  563.       // If a required value is expected, then this argument MUST be
  564.       // the value (even if it looks like an option
  565.       //
  566.       cmd_status |= parse_value(arg) ;
  567.    } else if (isOPTION(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) {
  568.       ++arg ;  // skip '-' option character
  569.       if (cmd_flags & KWDS_ONLY) {
  570.          cmd_state  |= cmd_KEYWORDS_USED ;
  571.          cmd_status |=  parse_keyword(arg) ;
  572.       } else {
  573.          cmd_state  |= cmd_OPTIONS_USED ;
  574.          cmd_status |=  parse_option(arg) ;
  575.       }
  576.    } else if ((! (cmd_flags & OPTS_ONLY))
  577.               && isKEYWORD(arg, plus) && (! (cmd_state & cmd_END_OF_OPTIONS))) {
  578.       cmd_state |= cmd_KEYWORDS_USED ;
  579.       if (*arg == '+') {
  580.          ++arg ;  // skip over '+' keyword prefix
  581.       } else {
  582.          arg += 2 ;  // skip over '--' keyword prefix
  583.       }
  584.       cmd_status |= parse_keyword(arg) ;
  585.    } else if (isENDOPTIONS(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) {
  586.       cmd_state |= cmd_END_OF_OPTIONS ;
  587.       // see if we left an argument dangling without a value
  588.       ck_need_val() ;
  589.    } else {
  590.       cmd_status |= parse_value(arg) ;
  591.    }
  592.  
  593.    return  cmd_status ;
  594. }
  595.  
  596. //-------
  597. // ^FUNCTION: CmdLine::arg_error - format an argument for error messages
  598. //
  599. // ^SYNOPSIS:
  600. //    ostream & arg_error(error_str, cmdarg);
  601. //
  602. // ^PARAMETERS:
  603. //    const char * error_str;
  604. //    -- the problem with the argument
  605. //
  606. //    const CmdArg * cmdarg;
  607. //    -- the argument to be formatted
  608. //
  609. // ^DESCRIPTION:
  610. //    This function will write to "os" the argument corresponding to
  611. //    "cmdarg" as we would like it to appear in error messages that pertain
  612. //    to this argument.
  613. //
  614. // ^REQUIREMENTS:
  615. //    None.
  616. //
  617. // ^SIDE-EFFECTS:
  618. //    writes to "os"
  619. //
  620. // ^RETURN-VALUE:
  621. //    A reference to os.
  622. //
  623. // ^ALGORITHM:
  624. //    Pretty straightforward, just print to os the way we
  625. //    want the argument to appear in usage messages.
  626. //-^^----
  627. ostream &
  628. CmdLine::arg_error(const char * error_str, const CmdArg * cmdarg) const
  629. {
  630.    int  plus = (cmd_flags & ALLOW_PLUS) ;  // Can we use "+"?
  631.  
  632.    ostream & os = error() << error_str << char(' ') ;
  633.  
  634.    if (cmdarg->flags() & CmdArg::GIVEN) {
  635.        if (cmdarg->flags() & CmdArg::KEYWORD) {
  636.           os << ((cmd_flags & KWDS_ONLY) ? OptionPrefix()
  637.                                          : KeywordPrefix(plus))
  638.              << cmdarg->keyword_name() << " option" ;
  639.        } else if (cmdarg->flags() & CmdArg::OPTION) {
  640.           os << OptionPrefix() << (char)cmdarg->char_name() << " option" ;
  641.        } else {
  642.           os << cmdarg->value_name() << " argument" ;
  643.        }
  644.    } else {
  645.        if (cmdarg->syntax() & CmdArg::isPOS) {
  646.           os << cmdarg->value_name() << " argument" ;
  647.        } else {
  648.           if (cmd_flags & KWDS_ONLY) {
  649.              os << OptionPrefix() << cmdarg->keyword_name() << " option" ;
  650.           } else {
  651.              os << OptionPrefix() << (char)cmdarg->char_name() << " option" ;
  652.           }
  653.        }
  654.    }
  655.    return  os;
  656. }
  657.  
  658.  
  659. //-------
  660. // ^FUNCTION: CmdLine::fmt_arg - format an argument for usage messages
  661. //
  662. // ^SYNOPSIS:
  663. //    unsigned CmdLine::fmt_arg(cmdarg, buf, bufsize, syntax, level);
  664. //
  665. // ^PARAMETERS:
  666. //    const CmdArg * cmdarg;
  667. //    -- the argument to be formatted
  668. //
  669. //    char * buf;
  670. //    -- where to print the formatted result
  671. //
  672. //    unsigned bufsize;
  673. //    -- number of bytes allocated for buf.
  674. //
  675. //    CmdLine::CmdLineSyntax syntax;
  676. //    -- the syntax to use (option, long-option, or both).
  677. //
  678. //    CmdLine::CmdUsageLevel;
  679. //    -- the usage-level corresponding to this portion of the
  680. //       usage message.
  681. //
  682. // ^DESCRIPTION:
  683. //    This function will write into "buf" the argument corresponding to
  684. //    "cmdarg" as we would like it to appear in usage messages.
  685. //
  686. // ^REQUIREMENTS:
  687. //    "buf" must be large enough to hold the result.
  688. //
  689. // ^SIDE-EFFECTS:
  690. //    writes to "buf"
  691. //
  692. // ^RETURN-VALUE:
  693. //    the length of the formatted result.
  694. //
  695. // ^ALGORITHM:
  696. //    Its kind of tedious so follow along.
  697. //-^^----
  698. unsigned
  699. CmdLine::fmt_arg(const CmdArg           * cmdarg,
  700.                  char                   * buf,
  701.                  unsigned                 bufsize,
  702.                  CmdLine::CmdLineSyntax   syntax,
  703.                  CmdLine::CmdUsageLevel   level) const
  704. {
  705.    ostrstream  oss(buf, bufsize);
  706.    *buf = '\0';
  707.  
  708.    int  plus = (cmd_flags & ALLOW_PLUS) ;  // Can we use "+"?
  709.    char optchar = cmdarg->char_name();
  710.    const char * keyword = cmdarg->keyword_name();
  711.  
  712.    // Need to adjust the syntax if optchar or keyword is empty
  713.    if ((! (cmdarg->syntax() & CmdArg::isPOS)) &&
  714.        ((! optchar) || (keyword == NULL))) {
  715.       if (keyword == NULL) {
  716.          if ((cmd_flags & KWDS_ONLY) && (cmd_flags & NO_GUESSING)) {
  717.             return  0;
  718.          } else {
  719.             syntax = cmd_OPTS_ONLY;
  720.          }
  721.       }
  722.       if (! optchar) {
  723.          if ((cmd_flags & OPTS_ONLY) && (cmd_flags & NO_GUESSING)) {
  724.             return  0;
  725.          } else {
  726.             syntax = cmd_KWDS_ONLY;
  727.          }
  728.       }
  729.    }
  730.  
  731.    // If the argument is optional - print the leading '['
  732.    if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) {
  733.       oss << char('[') ;
  734.    }
  735.  
  736.    // If we have a sticky-argument and usage is cmd_BOTH then it gets
  737.    // really hairy so we just treat this as a special case right here
  738.    // and now.
  739.    //
  740.    if ((syntax == cmd_BOTH) &&
  741.        (! (cmdarg->syntax() & CmdArg::isPOS)) &&
  742.        (cmdarg->syntax() & CmdArg::isVALTAKEN) &&
  743.        (cmdarg->syntax() & CmdArg::isVALSTICKY))
  744.    {
  745.       if (cmdarg->syntax() & CmdArg::isVALOPT) {
  746.          oss << OptionPrefix() << char(optchar) << char('[')
  747.              << cmdarg->value_name() << "]|" << KeywordPrefix(plus)
  748.              << keyword << "[=" << cmdarg->value_name() << char(']') ;
  749.       } else {
  750.          oss << OptionPrefix() << optchar << cmdarg->value_name()
  751.              << char('|') << KeywordPrefix(plus) << keyword << char('=')
  752.              << cmdarg->value_name() ;
  753.       }
  754.       if ((level == VERBOSE_USAGE) && (cmdarg->syntax() & CmdArg::isLIST)) {
  755.          oss << " ..." ;
  756.       }
  757.       if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) {
  758.          oss << char(']') ;
  759.       }
  760.       oss << ends ;
  761.       return  (oss.pcount() - 1);
  762.    }
  763.  
  764.    if (! (cmdarg->syntax() & CmdArg::isPOS)) {
  765.       switch(syntax) {
  766.          case cmd_OPTS_ONLY :
  767.             oss << OptionPrefix() << char(optchar) ;
  768.             break ;
  769.  
  770.          case cmd_KWDS_ONLY :
  771.             oss << ((cmd_flags & KWDS_ONLY) ? OptionPrefix()
  772.                                             : KeywordPrefix(plus)) << keyword ;
  773.             break ;
  774.  
  775.          case cmd_BOTH :
  776.             oss << OptionPrefix() << char(optchar) << char('|')
  777.                 << KeywordPrefix(plus) << keyword ;
  778.             break ;
  779.  
  780.          default :
  781.             cerr << "*** Internal error in class CmdLine.\n"
  782.                  << "\tunknown CmdLineSyntax value (" << int(syntax) << ")."
  783.                  << endl ;
  784.             ::exit(e_INTERNAL);
  785.       } //switch
  786.       if (cmdarg->syntax() & CmdArg::isVALTAKEN) {
  787.          if (! (cmdarg->syntax() & CmdArg::isVALSTICKY)) {
  788.             oss << char(' ') ;
  789.          }
  790.       }
  791.    }
  792.  
  793.    // If the argument takes a value then print the value
  794.    if (cmdarg->syntax() & CmdArg::isVALTAKEN) {
  795.       if ((! (cmdarg->syntax() & CmdArg::isPOS)) &&
  796.           (cmdarg->syntax() & CmdArg::isVALOPT))
  797.       {
  798.          oss << char('[') ;
  799.       }
  800.       if (cmdarg->syntax() & CmdArg::isVALSTICKY) {
  801.          if (syntax == cmd_KWDS_ONLY)  oss << char('=') ;
  802.       }
  803.       oss << cmdarg->value_name() ;
  804.       if ((level == VERBOSE_USAGE) && (cmdarg->syntax() & CmdArg::isLIST)) {
  805.          oss << " ..." ;
  806.       }
  807.       if ((! (cmdarg->syntax() & CmdArg::isPOS)) &&
  808.           (cmdarg->syntax() & CmdArg::isVALOPT))
  809.       {
  810.          oss << char(']') ;
  811.       }
  812.    }
  813.  
  814.    if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) {
  815.       oss << char(']') ;
  816.    }
  817.    oss << ends ;
  818.  
  819.    return  (oss.pcount() - 1) ;
  820. }
  821.  
  822.