Computer Science II Lab 3
       Parsing the Command Line and Building Complex Programs

 Introduction
 ============
 This lab will guide you through building programs which have
 multiple files, as well as automating the build process.  This is a
 review of some of the CS I material, but it also pushes your uses of
 classes just a little farther.

 We will also be adding a couple of functions to ansi.h, which will
 allow us to hide and show the cursor.  I had originally left these
 out, and now I want to correct that!

 At the end of this lab, you should have a clear picture of class
 scoping, how command line arguments are processed, and how to write
 simple makefiles.  The command line parser presented in this lab
 will be used in future labs, so be sure you get it right!

 First, we do your addendum for ansi.h.


 Guided Lab Part 1 - A slight addition to ansi.h
 ===============================================
 In lab 1, we wrote a nifty little file which allows us to color
 things on the terminal and make cool animation like effects.  I had
 omitted one sequence because, at the time, I thought it wasn't as
 universal as it appears to actually be.  These functions are the ones
 which will allow us to turn the cursor on and off.  That would have
 made the game of life so much better!  Well, live and learn.  We
 won't be using these in this actual lab, but they will be used in
 our in-class examples, so you may as well add them.

 So, simply add the following lines to ansi.h:

 --begin end of ansi.h--
 inline std::ostream& cursorOff(std::ostream &os) {
   return os << "\033[?25l";
 }


 inline std::ostream& cursorOn(std::ostream &os) {
   return os << "\033[?25h";
 }
 --end end of ansi.h--

 Now you can turn the cursor off with:

   cout << cursorOff;

 And you can turn the cursor on with:

   cout << cursorOn;


 Now that we're done with that, let's dive into the main part of the
 lab!



 Parsing the Command Line (Guided)
 =================================
 As we know from class, the UNIX command line is passed into a
 program via the argc and argv arguments to the main function.  argc
 contains the number of arguments passed into the progamm and argv is
 a ragged array of strings containing the values of the command line
 arguments.  argv[0] is the name of the program as invoked at the
 command line and the rest are the space delimited words which follow
 the name of the program.

 Now, there is a general format users have come to expected.
 Basically there are two categories of arguments.  There are unnamed
 arguments, which are just values, and then there are named arguments
 or "switches" that they can turn on.  Literal values are specified
 by themselves, and switches begin with a -.

 In addition to these rules, there is a second rule.  Namely, a
 single - denotes a single character switch, and -- denotes an entire
 word being used.  Take for instance, the mysql command line client.
 I could log into a database as follows:

   mysql -ubob -pfoo database_name

 Or

   mysql --user bob -pfoo database_name

 Note how -u and --user are equivalent.  This is definitely the norm!
 The long words are easier to remember, while the short ones are
 shorter for typing.  So most switches come in both forms.  Also
 because -u is a single character "-u bob" and "-ubob" are
 equivalent.  Thus they both set the internal variable user to
 "bob".

 So now, how can we parse all that?  Well, we could establish some
 rules.  The rules go like this:

   1.) If a string starts with "--" then this is a full word
       argument.  Check the next string, if it does not start with
       "-" then it is the value for the switch.  Thus the switch gets
       a flag and a value, or it's a flag with a blank value.

   2.) If a string starts with "-" then this is a single character
       argument.  If the string contains more than one character
       after the "-", that is the value.  Otherwise the next string
       is the value if it does not begin with "-".  The result is as
       above in number 1.

   3.) Otherwise, the string is a value without a switch/flag.
       Return a blank flag and the value.

 Now these rules aren't terribly complex, but they can be subtle in
 their implementation.  So it would be best to write this once and
 then reuse the parser.  We will do this using a class.  The class
 will keep track of our position in the argument list and let us
 cycle through the strings and it will convert them into the two part
 arguments as it goes. We will implement this in the files
 cmdParser.h and cmdParser.cpp.  These files follow.  Go ahead and
 type them in now.  Pay close attention to how the class does its
 job.  Look at when the variables are available, and ask questions if
 you don't understand how it does its job!

 --begin cmdParser.h--
    1  /*
    2   * File: cmdParser.h
    3   * Purpose: The header for a class used for parsing command line arguments.
    4   *          There are three kinds of arguments.  The first are single letter
    5   *          flags and they begin with -, and then there are multiple letter
    6   *          flags which begin with -- and then there are non-flagged values
    7   *          which do not begin with -.
    8   *          thus -a1  and -a 1 are both "a=1"
    9   *          --add 1 is add=1
   10   *          foo is a null flag, with value "foo"
   11   * Author: Robert Lowe
   12   */
   13  #ifndef PARSER_H
   14  #define PARSER_H
   15  #include <string>
   16
   17  //the command line argument returned by the parser
   18  typedef struct argument {
   19    std::string flag;         //the argument itself (named, if there is one)
   20    std::string value;        //the value of the argument (if there is one)
   21  } Argument;
   22
   23
   24  class CmdParser {
   25  public:
   26    // Constructor
   27    CmdParser(int argc, char **argv);
   28
   29    //pull out the next command line argument
   30    Argument nextArgument();
   31
   32    //returns true if there is a next argument, false otherwise
   33    bool hasNext();
   34
   35  private:
   36    int argc;      //argument count
   37    char **argv;   //argument values as passed into constructor
   38    int index;     //current index of the
   39  };
   40
   41  #endif
 --end cmdParser.h--


 --begin cmdParser.cpp--
    1  #include "cmdParser.h"
    2  #include <string>
    3
    4  using namespace std;
    5
    6  // Constructor
    7  CmdParser::CmdParser(int argc, char **argv) {
    8    //set up the argc and argv
    9    this->argc = argc;
   10    this->argv = argv;
   11
   12    //setup the index.  We skip the program name
   13    index = 1;
   14  }
   15
   16
   17  //pull out the next commmand line argument
   18  Argument
   19  CmdParser::nextArgument() {
   20    Argument result;  //the result
   21
   22    //fail safely if someone tries to read too much
   23    if(index >= argc)
   24      return result;
   25
   26    //if block to handle arguments
   27    if(argv[index][0] != '-') {
   28      //handle null flags
   29      result.value = argv[index];
   30    } else {
   31      //this is a flag!
   32      if(argv[index][1] == '-') {
   33        //handle the multiple character flag
   34        result.flag = argv[index]+2;
   35      } else {
   36        //handle single character flag
   37        result.flag = argv[index][1];
   38
   39        //there is a chance the value part is in the same string
   40        if(argv[index][2]!='\0') {
   41          result.value = argv[index] + 2;
   42        }
   43      }
   44    }
   45
   46    //advance the index
   47    index++;
   48
   49    //ok, so now if we don't yet have a value, look at the next argument.
   50    //if it isn't a flag, it's a value, so pull it in!
   51    if(result.value.empty() && index < argc && argv[index][0]!='-') {
   52      result.value = argv[index];
   53      index++;  //go to the next index
   54    }
   55
   56    return result;
   57  }
   58
   59
   60  //returns true if there is a next argument, false otherwise
   61  bool
   62  CmdParser::hasNext() {
   63    //basically, we are just comparing index to the argc
   64    return index < argc;
   65  }
 --end cmdParser.cpp--

 Now that we have that, let's get a test running.  Here's one that
 just dumps out the parsed values to the screen:


 --begin testCmdParser.cpp--
    1  #include <iostream>
    2  #include <string>
    3  #include "cmdParser.h"
    4
    5
    6  using namespace std;
    7
    8  int main(int argc, char **argv) {
    9    CmdParser args(argc, argv);    //parser for command line arguments
   10    Argument argument;
   11
   12    //process all arguments
   13    while(args.hasNext()) {
   14      //get the argument and show it
   15      argument = args.nextArgument();
   16
   17      //print the flag if there is one
   18      if(!argument.flag.empty()) {
   19        cout << argument.flag << ": ";
   20      }
   21
   22      cout << argument.value << endl;
   23    }
   24
   25    return 0;
   26  }
 --end testCmdParser.cpp--

 Now, let's test it all!  Compile with:
   g++ testCmdParser.cpp cmdParser.cpp -o testCmdParser

 Run your generated program, try giving it some command line
 arguments and see how it works.  Study the test file and make sure
 you understand how to use the class.



 Now, let's use this to write a usable program.  We will write the
 most super deluxe hello world program known to mankind!  This
 program lets you change the words, the color, displays a help
 message, and it parses command lines!  Here's the program:


 --begin hello.cpp--
    1  /*
    2   * The most elaborate hello world known to man!
    3   */
    4
    5  #include <iostream>
    6  #include "cmdParser.h"
    7  #include "ansi.h"
    8
    9  using namespace std;
   10
   11  int
   12  main(int argc, char** argv) {
   13    string w1 = "Hello";
   14    string w2 = "World";
   15    CmdParser args(argc, argv);
   16    Argument argument;
   17
   18    //process the arguments
   19    while(args.hasNext()) {
   20      //get the next argument
   21      argument = args.nextArgument();
   22
   23      //handle the arguments
   24      if(argument.flag == "1" || argument.flag == "word1") {
   25        w1 = argument.value;
   26      } else if(argument.flag == "2" || argument.flag == "word2") {
   27        w2 = argument.value;
   28      } else if(argument.flag == "r" || argument.flag == "red") {
   29        cout << red;
   30      } else if(argument.flag == "g" || argument.flag == "green") {
   31        cout << green;
   32      } else if(argument.flag == "b" || argument.flag == "blue") {
   33        cout << blue;
   34      } else if(argument.flag == "h" || argument.flag == "help") {
   35        cout << argv[0] << endl
   36             << " -1, --word1  first word" << endl
   37             << " -2, --word2  second word" << endl
   38             << " -r, --red    Display result in red" << endl
   39             << " -b, --blue   Display result in blue" << endl
   40             << " -g, --green  Dispay result in green" << endl
   41             << " -h, --help   display this message" << endl;
   42      }
   43    }
   44
   45    //print the message & return terminal to normal
   46    cout << w1 << " " << w2 << endl << normal;
   47
   48    return 0;
   49  }
   50
 --end hello.cpp--

 To compile this one, we'll create a makefile.  Recall that makefiles
 are used to automate the build process.  Also, the indentations on
 the line are accomplished with literal tabs.  This makefile will
 build both the test program and the hello program.  Here it is:


 --begin Makefile--
      1        all: testCmdParser hello
    2
    3  testCmdParser: testCmdParser.o cmdParser.o
    4          g++ -o testCmdParser testCmdParser.o cmdParser.o
    5
    6  hello: hello.o cmdParser.o
    7          g++ -o hello cmdParser.o hello.o
    8
    9  cmdParser.o: cmdParser.cpp cmdParser.h
   10          g++ -c cmdParser.cpp
   11
   12  testCmdParser.o: testCmdParser.cpp
   13          g++ -c testCmdParser.cpp
   14
   15  hello.o: hello.cpp
   16          g++ -c hello.cpp
   17
   18  clean:
   19          rm -f *.o testCmdParser hello
   20
 --end Makefile--

 Now to verify that it works, run the following commands:

   gmake clean
   gmake

 Correct any compile errors, and try again.  From here on in, gmake
 is your build command!  (or make if you are on a system where you
 have access to traditional UNIX make).


 Study the hello.cpp file.  Make sure you understand how it works.
 Play around with running the program.  Also study the makefile.  The
 challenge is coming!

 Study the structure of this program carefully.  All labs after this
 one will require multiple header and cpp files, make sure you
 understand what each are doing!


 A Commmand Line Calculator (Challenge)
 ======================================
 Ok, now it's time for you to fly on your own!  You will use our
 little command line library to build a simple calculator.  Our
 calculator will have the following functions:

   -a, --add     addition
   -s, --sub     subtraction
   -m, --mul     multiplication
   -d, --div     division

 It will take in arguments from left to right and compute the
 result.  You do not need to worry about order of operations.  The
 basic idea is that I can type in something like this:

   ./calc 40 -a 2 --sub 6 -m 5

 And I will get 180.  ((40+2) - 6) * 5).

 Also, you should add a -h option which prints a helpful message
 about how to use your program!

 HINT:  You will need to convert strings to numbers from the
 arguments.  The wise student would google for "atof" and especially
 "using atof with c++ strings".


 Extra Credit Opportunity
 ========================
 Add the following:

   --sin      Sine (radians)
   --cos      Cosine (radians)
   --tan      Tangent (radians)
   --sqrt     Square root
   -e, --pow  Exponent   2 -e 4 is 16.

 These may seem easy at first, except note that you have to process
 things a little differently. For example.  Consider the following:
   ./calc 50 -a --sin 1.2
 Note that I have to evaluate --sin 1.2 before I can add!  If you try
 this, I wish you luck!  (It is doable using only what we have
 covered so far.)


 Good luck, and as always, Enjoy!