Command line option

Handling command line options from int main(int argc, char *argv[])
program_name [-d] [-h] [-v] [-o output_file] [-l limit_value]
             file_name [ file_name [file_name [ ... ]]]

// valid
prog chap1.doc
prog -l 1024 -o chap1-2.out chap1.doc chap2.doc
prog -d chap3.doc
prog -l 512 -d chap4.doc
  1. Extract each option in turn from argv for (int ix=1; ix<argc; ix++) { // begin at 1 char *pchar = argv[ix]; // ... }
  2. Determine the type of option. If it begins with a hyphen, then it may be one of {h, d, v, l, o}. Otherwise, it may either the actual limit value associated with -l, an output file name associated with -o or the name of a file for the program to process.
    switch (pchar[0]) {
      case '-': {
        switch (pchar[1])
        {
          case 'd':
            // handle debug
            break;
          case 'v':
            // handle version request
            break;
          case 'h':
            // handle help
            break;
          case 'o':
            // prepare to handle output_file
            break;
          case 'l':
            // prepare to handle limit_value
            break;
          default:
            // unrecognized option:
            // report it and exit
        }
      }
      default: {
        if (ofile_on) {
          // handle output_file      
          // turn off ofile_on
        }
        else if (limit_on) {
          // handle limit_value
          // turn off limit_on
        }
        else {
          // handle file_name
        }
      }
    }
    
  3. Fill in the handling
    bool debug_on = false;
    bool ofile_on = false; // if true, next argument is output file
    bool limit_on = false; // if true, next argument is limit value
    string ofile;
    vector<string, allocator> file_names;
    
    switch (pchar[0]) {
      case '-': {
        switch (pchar[1])
        {
          case 'd':
            debug_on = true;
            break;
          case 'v':
            // displays version number and then exits
            cout << program_name << "::" << program_version << endl;
            return 0;
          case 'h':
            usage(); // no break necessary: usage() exits
          case 'o':
            ofile_on = true;
            break;
          case 'l':
            limit_on = true;
            break;
          default:
            cerr << program_name << " : error : "
                 << "unrecognized option: - " << pchar << "\n\n";
            usage(-1);
        }
      }
      default: {
        if (ofile_on) {
          ofile = pchar;
          ofile_on = false;
        }
        else if (limit_on) {
          limit = atoi(pchar);
          if (limit < 0) {
            cerr << program_name << "::"
                 << program_version << " : error: "
                 << "negative value for limit.\n";
            usage(-2);
          }
          limit_on = false;
        }
        else
          file_names.push_back(string(pchar));
        break;
      }
    }
    
  4. Full implementation
    const char *const program_name = "comline";
    const char *const program_version = "version 0.01 (08/07/97)";
    
    inline void usage(int exit_value = 0)
    {
      // prints out a formatted usage message and exits using exit_value
      cerr << "usage:\n"
           << program_name << " "
           << "[-d] [-h] [-v] \n\t"
           << "[-o output_file] [-l limit] \n\t"
           << "file_name\n\t[file_name [file_name [ ... ]]]\n\n"
           << "where [] indicates optional option:\n\n\t"
           << "-h: help.\n\t\t"
           << "generates this message and exits\n\n\t"
           << "-v: version.\n\t\t"
           << "prints version information and exits\n\n\t"
           << "-d: debug.\n\t\tturns debugging on\n\n\t"
           << "-l limit\n\t\t"
           << "limit must be a non-negative integer\n\n\t"
           << "-o ofile\n\t\t"
           << "file within which to write out results\n\t\t"
           << "by default, results written to standard output\n\n"
           << "file_name\n\t\t"
           << "the name of the actual file to process\n\t\t"
           << "at least one file_name is required --\n\t\t"
           << "any number may be specified\n\n"
           << "examples:\n\t\t"
           << "$command chapter7.doc\n\t\t"
           << "$command -d -l 1024 -o test_7_8 "
           << "chapter7.doc chapter8.doc\n\n";
      exit(exit_value);
    }
    
    // pass values by global variables
    int parse_options(int arg_count, char **arg_vector);
    int main(int argc, char *argv[])
    {
      int option_status;
      option_status = parse_options(argc, argv);
    
      if (file_names.empty()) {
        cerr << program_name << " : error : "
             << "no file specified for processing.\n\n";
        usage(-3);
      }
      if (limit != -1)
        cout << "User-specifed limit: " << limit << endl;
      if (! ofile.empty())
        cout << "User-specified output file: " << ofile << endl;
      cout << (file_names.size() == 1 ? "File " : "Files ")
           << "to be processed are the following:\n";
      for (int inx=0; inx<file_names.size(); inx++)
        cout << "\t" << file_names[inx] << endl;
      }
      // ...
    }
    
Encapsulate the processing within a class
class CommandOpt {
public:
  CommandOpt() : _limit(-1), _debug_on(false) {}
  int parse_options(int argc, char *argv[]);
  string out_file() { return _out_file; }
  bool debug_on() { return _debug_on; }
  int files() { return _file_names.size(); }
  // to access _file_names
  string& operator[](int ix);

private:
  inline int usage(int exit_value = 0);
  
  bool _debug_on;
  int _limit;
  string _out_file;
  vector<string, allocator> _file_names;
  static const char *const program_name;
  static const char *const program_version;
};

int main(int argc, char *argv[])
{
  CommandOpt com_opt;
  int option_status;
  option_status = com_opt.parse_options(argc,argv);
  // ...
}
Index