home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_C / CL187A.ZIP / CMDLN.CPP < prev    next >
C/C++ Source or Header  |  1994-03-15  |  9KB  |  335 lines

  1. /*
  2.     cmdln.cpp -- command line option parser
  3.  
  4.     (C) Copyright 1994  John Webster Small
  5.     All rights reserved
  6.  
  7.     Notes:
  8.  
  9.     1. Call the constructor CmdLn() or the parse() 
  10.     member function with argc, argv, and an options 
  11.     string.  The options string is a string of 
  12.     option characters that may appear on your 
  13.     application's command line after the switch 
  14.     character.  The valid switch characters are 
  15.     defined in the CmdLn::switches string below.  
  16.     If an option takes a string argument then a 
  17.     colon must immediately follow that option 
  18.     character in the options string to let 
  19.     getOption() know to look for the string 
  20.     argument.  For example, the options string "a:" 
  21.     signifies that the "a" option has an optional 
  22.     string argument.  If an option takes a numeric 
  23.     argument then a pound sign, i.e. #, must 
  24.     immediately follow that option character in the 
  25.     options string to let getOption() know to look 
  26.     for the argument and convert it to a numeric 
  27.     value.  For example, the options string 
  28.     "Aa:b#B" signifies that the "A" option has no 
  29.     arguments other than an optional "+" or "-", 
  30.     the "a" option has an optional string argument, 
  31.     the "b" option has an option numeric argument 
  32.     that may be proceeded by an optional ":", and 
  33.     the "B" has no arguments just like the "A" 
  34.     option.  The syntax for the options string is 
  35.     as follows:
  36.  
  37.         options string ::= {optch[:|#]}+
  38.  
  39.     You read the syntax as: An options string is 
  40.     one or more (the "+" indicates this) option 
  41.     characters any of which may be followed by a 
  42.     colon or a pound sign to indicate that the 
  43.     option has an optional string or numeric 
  44.     argument, respectively.
  45.  
  46.     2. Call getOption() repeatedly to parse command 
  47.     line arguments.  GetOption() returns the 
  48.     current option character being processed along 
  49.     with any argument via the variable CmdLn:: 
  50.     optArg.  If the argument is numeric as 
  51.     indicated by the pound sign in the options 
  52.     string, then the variable CmdLn::optNum 
  53.     additionally holds this value.  Given the 
  54.     options string "a:b#cd" and the command line 
  55.     "-ahello -b:10 -cc+d-", the first call to 
  56.     getOption() would return 'a' with CmdLn::optArg 
  57.     == "hello" and the second would return 'b' with 
  58.     CmdLn::optArg == ":10" and CmdLn:optNum == 10. 
  59.     Note the ":" in the command line parameter is 
  60.     optional!  The third and fourth calls would 
  61.     return "c" with CmdLn::optOff == 0 while the 
  62.     fifth call would return "d" with CmdLn::optOff 
  63.     == 1.  As you can see the "+/-" indicators are 
  64.     optional and when missing "+" is assumed.  
  65.     Another important point is that options can be 
  66.     clustered such as '-cd'.
  67.  
  68.     GetOption() can recognize clusters of command 
  69.     line options.  A cluster is the switch 
  70.     character followed immediately by any number of 
  71.     option characters, no white space.  If an 
  72.     option takes an argument, i.e. string or 
  73.     numeric, then the option's character must be 
  74.     the last option in the cluster with the 
  75.     argument immediately following, i.e. no white 
  76.     space.  The syntax for an option cluster is as 
  77.     follows:
  78.  
  79.         option cluster
  80.  
  81.         ::= swCh{optCh[\+|\-]}+
  82.  
  83.         ::= swCh{optCh[\+|\-]}*
  84.             {optch[strArg|[:]numArg]}
  85.  
  86.     Wow! That reads: a command line option cluster 
  87.     starts with the switch character, e.g. '-', 
  88.     with either one or more option characters (+ 
  89.     means one or more) or in the second case any 
  90.     number of option characters (* means zero or 
  91.     more), only the last of which is allowed to 
  92.     take an argument.
  93.  
  94.     If getOption() encounters an unknown option it 
  95.     returns '?' and the unknown option in CmdLn::
  96.     optNot.  The rest of the cluster is not parsed 
  97.     in this case.
  98.  
  99.     The Unix convention is for options to come 
  100.     first on the command line followed by other 
  101.     parameters.  With CmdLn they are allowed in any 
  102.     order.  Each time getOption() encounters a 
  103.     parameter it returns '%'.
  104.  
  105.     The last value returned from getOption() can 
  106.     also be found in CmdLn:: optCh.  The type of 
  107.     option can be found in CmdLn::optType 
  108.     enumerated as NA, FLAG, STR, NUM, UNKNOWN, and 
  109.     PARAM.  When getOption() has finished 
  110.     processing all arguments it returns 0.  
  111.     
  112.     3.  For example, if the options string contains 
  113.     "C:af:zN#" then 'C' and 'f' take string 
  114.     arguments and N a numeric argument.  A valid 
  115.     command line for the demo below would be:
  116.  
  117.     cmdln C:af:zN# -a:fnew outfile /z-Ccmdfile -N:20
  118.  
  119.     with repeated calls to getOption() returning:
  120.  
  121.         'a' with optOff  == 0
  122.              optType == FLAG
  123.          ?  with optNot  == ':'
  124.              optType == UNKNOWN
  125.         'f' with optArg  == "new"
  126.              optType == STR
  127.         '%' with param   == "outfile"
  128.              optType == NA
  129.         'z' with optOff  != 0
  130.              optType == FLAG
  131.         'C' with optArg  == "cmdfile"
  132.              optType == STR
  133.         'N' with optArg  == ":20"
  134.              optNum  == 20
  135.              optType == NUM
  136.          0
  137.     
  138.     4.  To test CmdLn, define CMDLN_DEMO below and
  139.     then compile and run cmdln.cpp.  Be sure you
  140.     understand the demo code in main() before using
  141.     cmdln.cpp.
  142.  
  143. */
  144.  
  145. //#define CMDLN_DEMO
  146.  
  147. #include <ctype.h>    /*  isalpha()  */
  148. #include <string.h>    /*  strchr(), strdup()  */
  149. #include <stdlib.h>    /*  atoi()    */
  150. #include <iomanip.h>
  151. #include "cmdln.h"
  152.  
  153.  
  154. void argument::reset()
  155. {
  156.     optType = NA;
  157.     optCh = optNot = '\0';
  158.     optArg = param = (char *) 0;
  159.     optOff = optNum = 0;
  160. }
  161.  
  162. argument& argument::operator=(const argument& a)
  163. {
  164.     optType = a.optType;
  165.     optCh = a.optCh;
  166.     optNot = a.optNot;
  167.     optArg = a.optArg;
  168.     param = a.param;
  169.     optOff = a.optOff;
  170.     optNum = a.optNum;
  171.     return *this;
  172. }
  173.  
  174. ostream& operator<<(ostream& os, argument& a)
  175. {
  176.     switch (a.optType)  {
  177.  
  178.     case argument::FLAG:
  179.         os << a.optCh
  180.         << (a.optOff? "  -" : "  +");
  181.         break;
  182.     case argument::STR:
  183.         os << a.optCh;
  184.         if (a.optArg)
  185.             os << "  " << a.optArg;
  186.         break;
  187.     case argument::NUM:
  188.         os << a.optCh;
  189.         if (a.optArg) if (a.optNum)
  190.             os << "  " << a.optNum;
  191.         else
  192.             os << "  " << a.optArg;
  193.         break;
  194.     case argument::UNKNOWN:
  195.         os << "?  " << a.optNot;
  196.         break;
  197.     case argument::PARAM:
  198.         os << "%  " << a.param;
  199.         break;
  200.     }
  201.     return os;
  202. }
  203.  
  204.  
  205. char CmdLn::switches[] = "/-";
  206. /*
  207.     Switches is initialized here for DOS switch
  208.     characters.  Change as necessary for your
  209.     host OS shell.
  210. */
  211.  
  212. void CmdLn::parse(int argc, char *argv[],
  213.     char *options)
  214. {
  215.     this->argv = argv;
  216.     this->options = (options? options : "");
  217.     optCluster = (char *) 0;
  218.     this->argc = argc;
  219.     argvEnd = 0;
  220.     argi = 1;
  221.     reset();
  222. }
  223.  
  224. int CmdLn::getOption(void)
  225. {
  226.  
  227.     char *lookup;
  228.  
  229.     if (argvEnd)
  230.         return 0;
  231.     reset();
  232.     if (!optCluster || (*optCluster == '\0'))  {
  233.         if (argi >= argc)  {
  234.             /* no more parameters */
  235.             argvEnd = 1;
  236.             return 0;
  237.         }
  238.         /* next possible option cluster */
  239.         if ((optCluster = argv[argi++])
  240.             == (char *)0)  {
  241.             argvEnd = 1;
  242.             return 1;
  243.         }
  244.         if (!strchr(switches,*optCluster))  {
  245.             /* param */
  246.             param = optCluster;
  247.             optCluster = (char *) 0;
  248.             optType = PARAM;
  249.             return (optCh = '%');
  250.         }
  251.         optCluster++;/* next possible option */
  252.        }
  253.     if (!isalpha(*optCluster))  {
  254.         /*  unknown option  */
  255.         optNot = *optCluster;
  256.         optCluster = (char *) 0;
  257.         optType = UNKNOWN;
  258.         return (optCh = '?');
  259.     }
  260.     if ((lookup = strchr(options,*optCluster++))
  261.         == (char *)0)  {
  262.         /*  unknown option  */
  263.         optNot = *--optCluster;
  264.         optCluster = (char *) 0;
  265.         optType = UNKNOWN;
  266.         return (optCh = '?');
  267.     }
  268.     /* validate option character */
  269.     switch (lookup[1])  {
  270.     case ':' :
  271.         if (*optCluster != '\0')
  272.             optArg = optCluster;
  273.         optCluster = (char *) 0;
  274.         optType = STR;
  275.         break;
  276.     case '#' :
  277.         if (*optCluster != '\0')  {
  278.             optArg = optCluster;
  279.             optNum = atoi((*optArg == ':')?
  280.                 &optArg[1]:optArg);
  281.         }
  282.         optCluster = (char *) 0;
  283.         optType = NUM;
  284.         break;
  285.     /*
  286.         non argument switches are allowed to
  287.         have optional +/- flags
  288.     */
  289.     default :
  290.         switch (*optCluster)  {
  291.         case '-' :
  292.             optOff = 1;
  293.         case '+' :
  294.             optCluster++;
  295.             break;
  296.         }
  297.         optType = FLAG;
  298.         break;
  299.     }
  300.     return (optCh = *lookup);
  301.     /* return option */
  302. }
  303.  
  304.  
  305.  
  306. #ifdef CMDLN_DEMO
  307.  
  308. main(int argc, char *argv[])
  309. {
  310.     cout << endl;
  311.     if (argc < 3)  {
  312.         cerr << "Usage: cmdln optionsStr "
  313.             "options+" << endl << endl;
  314.         cerr << "e.g. cmdln C:af:zN# -afnew "
  315.             "outfile /z-Ccmdfile -N:20"
  316.             << endl;
  317.         return 1;
  318.     }
  319.  
  320.     // skip over options string
  321.     CmdLn args(argc-1,&argv[1],argv[1]);
  322.  
  323.     // display command line
  324.     for (unsigned i = 1; i < argc; i++)
  325.         cout << argv[i] << "  ";
  326.     cout << endl << endl;
  327.  
  328.     while (args.getOption())
  329.         cout << args << endl;
  330.  
  331.     return 0;
  332. }
  333.  
  334. #endif    // CMDLN_DEMO
  335.