moab
ProgOptions.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 #include <sstream>
00003 #include <iomanip>
00004 #include <cstdlib>
00005 #include <list>
00006 #include <limits>
00007 #include <set>
00008 
00009 #include <assert.h>
00010 #include <string.h>
00011 
00012 #include "moab/ProgOptions.hpp"
00013 #ifdef USE_MPI
00014 # include "moab_mpi.h"
00015 #endif
00016 
00017 
00018 enum OptType {
00019   FLAG = 0,
00020   INT, 
00021   REAL, 
00022   STRING, 
00023   INT_VECT
00024 };
00025 
00026 
00027 template <typename T> inline
00028 static OptType get_opt_type();
00029 
00030 
00031 template<> OptType get_opt_type<void             >(){ return FLAG;     }
00032 template<> OptType get_opt_type<int              >(){ return INT;      }
00033 template<> OptType get_opt_type<double           >(){ return REAL;     }
00034 template<> OptType get_opt_type<std::string      >(){ return STRING;   }
00035 template<> OptType get_opt_type<std::vector<int> >(){ return INT_VECT; }
00036 
00037 class ProgOpt{
00038 
00039   std::string shortname, longname;
00040   std::vector< std::string > args;
00041   OptType type;
00042   void* storage;
00043   int flags;
00044   ProgOpt* cancel_opt;
00045 
00046   const char* get_argstring() const { 
00047     switch( type ){
00048     case INT:
00049       return "int";
00050     case INT_VECT:
00051       return "ints";
00052     case REAL:
00053       return "val";
00054     case FLAG:
00055       return "";
00056     default:
00057       return "arg";
00058     }
00059   }
00060 
00061 public:
00062   ProgOpt( const std::string& longname_p, const std::string& shortname_p, int flags_p, OptType t = FLAG ):
00063     shortname( shortname_p ), longname( longname_p ), type(t), 
00064     storage(NULL), flags(flags_p), cancel_opt(NULL)
00065   {}
00066  
00067   friend class ProgOptions;
00068 };
00069 
00070 ProgOptions::ProgOptions( const std::string& helpstring, const std::string& briefhelp ) :
00071     expect_optional_args(false)
00072 {
00073   brief_help = briefhelp;
00074   if (!helpstring.empty())
00075     main_help.push_back( helpstring );
00076  addOpt<void>( "help,h", "Show full help text", help_flag );
00077 }
00078 
00079 ProgOptions::~ProgOptions(){
00080   for( std::vector<help_line>::iterator i = option_help_strings.begin();
00081        i != option_help_strings.end(); ++ i )
00082   {
00083     if( (*i).first ){ delete (*i).first; }
00084   }
00085 
00086   for( std::vector<help_line>::iterator i = arg_help_strings.begin();
00087        i != arg_help_strings.end(); ++ i )
00088   {
00089     delete (*i).first;
00090   }
00091 }
00092 
00093 
00094 void ProgOptions::get_namestrings( const std::string& namestring, 
00095                                    std::string* longname, std::string* shortname )
00096 {
00097   *shortname = "";
00098   *longname = namestring;
00099 
00100   size_t idx = namestring.find_first_of(',');
00101   if( idx != namestring.npos ){
00102     *longname = namestring.substr(0, idx);
00103     *shortname = namestring.substr( idx+1, namestring.npos );
00104   }
00105 
00106   
00107 }
00108 
00109 void ProgOptions::setVersion( const std::string& version_string, bool addFlag ){
00110   progversion = version_string;
00111   if( addFlag ){
00112     addOpt<void>( "version", "Print version number and exit", version_flag );
00113   }
00114 }
00115 
00116 
00117 
00118 template < typename T >
00119 void ProgOptions::addOpt( const std::string& namestring, const std::string& helpstring, 
00120               T* value, int flags ){
00121 
00122   std::string shortname, longname;
00123   get_namestrings( namestring, &longname, &shortname );
00124   
00125   if (flags & int_flag) { // short name is implicit for this flag
00126     if (!shortname.empty()) 
00127       error( "Requested short name with int_flag option" );
00128     if (get_opt_type<T>() != INT)
00129       error( "Requested int_flag for non-integer option" );
00130     if (!number_option_name.empty()) 
00131       error( "Requested int_flag for multiple options" );
00132     number_option_name = longname;
00133   }
00134 
00135   ProgOpt* opt = new ProgOpt( longname, shortname, flags, get_opt_type<T>() );
00136   if( value ) opt->storage = value;
00137 
00138 
00139   if(longname.length())  long_names[longname] = opt;
00140   if(shortname.length()) short_names[shortname] = opt;
00141 
00142   help_line help = std::make_pair( opt, helpstring );
00143   option_help_strings.push_back( help );
00144 
00145   if( flags & add_cancel_opt ){
00146     std::string flag = "no-" + (longname.length() ? longname : shortname );
00147     ProgOpt* cancel_opt = new ProgOpt( flag, "", flags ^ ProgOptions::store_false, FLAG );
00148     if (value) cancel_opt->storage = value;
00149     
00150     cancel_opt->cancel_opt = opt;
00151     long_names[flag] = cancel_opt;
00152     std::string clear_helpstring = "Clear previous " + flag.substr(3,flag.npos) + " flag";
00153     help = std::make_pair( cancel_opt, clear_helpstring );
00154     option_help_strings.push_back( help );
00155   }
00156 }
00157 
00158 
00159 template < typename T >
00160 void ProgOptions::addRequiredArg( const std::string& helpname, 
00161                                   const std::string& helpstring, 
00162                                   T* value,
00163                                   int flags ){
00164   
00165   OptType type = get_opt_type<T>();
00166 
00167   ProgOpt* opt = new ProgOpt( helpname, "", flags,  type );
00168   if( value ) opt->storage = value;
00169   help_line help = std::make_pair( opt, helpstring );
00170   arg_help_strings.push_back( help );
00171   required_args[helpname] = opt;
00172 }
00173 
00174 template < typename T >
00175 void ProgOptions::addOptionalArgs( unsigned max_count, 
00176                                    const std::string& helpname, 
00177                                    const std::string& helpstring,
00178                                    int flags )
00179 {
00180     // If there was a previous one, we need to remove it
00181     // because there can be only one.  If we didn't remove
00182     // the old one then it would be treated as a required arg.
00183   if (expect_optional_args) {
00184     std::map<std::string, ProgOpt*>::iterator iter;
00185     iter = required_args.find( arg_help_strings[optional_args_position].second );
00186     assert(iter != required_args.end());
00187     delete iter->second;
00188     required_args.erase(iter);
00189     arg_help_strings.erase( arg_help_strings.begin() + optional_args_position );
00190   }
00191 
00192   expect_optional_args = true;
00193   optional_args_position = arg_help_strings.size();
00194   max_optional_args = max_count;
00195   addRequiredArg<T>( helpname, helpstring, 0, flags );
00196 }
00197 
00198 
00199 void ProgOptions::addOptionHelpHeading( const std::string& s ){
00200   option_help_strings.push_back( std::make_pair( (ProgOpt*)NULL, s) );
00201 }
00202 
00203 void ProgOptions::printVersion( std::ostream& out ){
00204   out << progversion << std::endl;
00205 }
00206 
00207 void ProgOptions::printHelp( std::ostream& out ){
00208   
00209   /* Print introductory help text */
00210   if (!brief_help.empty())
00211     out << brief_help << std::endl;
00212   for( std::vector<std::string>::iterator i = main_help.begin(); i!= main_help.end(); ++i ){
00213     if( (*i).length() ){
00214       out << std::endl << *i << std::endl;
00215     }
00216   }
00217 
00218   printUsage( out );
00219 
00220   // max number of characters to pad argument/option names with
00221   // options with long names may exceed this, but will appear out of alignment in help text
00222   const int max_padding = 20;
00223 
00224   /* List required arguments, with help text */
00225   if( arg_help_strings.size() > 0 ){
00226     
00227     int max_arg_namelen = 0;
00228     
00229     for( std::vector<help_line>::iterator i = arg_help_strings.begin();
00230          i != arg_help_strings.end(); ++i )
00231       {
00232         max_arg_namelen = std::max( max_arg_namelen, (int)((*i).first->longname.length()) );
00233       }
00234     
00235     max_arg_namelen = std::min( max_arg_namelen+3, max_padding );
00236 
00237     out << "Arguments: " << std::endl;
00238     
00239     for( std::vector<help_line>::iterator i = arg_help_strings.begin();
00240          i != arg_help_strings.end(); ++i )
00241       {
00242         ProgOpt* option = (*i).first;
00243         std::string& info = (*i).second;
00244 
00245         std::stringstream s;
00246         s << "  " << option->longname;
00247         out << std::setw(max_arg_namelen) << std::left << s.str();
00248         out << ": " << info << std::endl;
00249         
00250       }
00251   }
00252     
00253   /* List options, with help text */
00254   out << "Options: " << std::endl;
00255   int max_option_prefix_len = 0;
00256 
00257   for( std::vector<help_line>::iterator i = option_help_strings.begin();
00258        i != option_help_strings.end(); ++ i )
00259   {
00260     ProgOpt* option = (*i).first;
00261     std::string& info = (*i).second;
00262 
00263     if( option ){
00264 
00265       if( max_option_prefix_len == 0 ){
00266         // iterate ahead in the option list to determine whitespace padding
00267         // stop if (*j).first is NULL, which indicates a help header message 
00268         for( std::vector<help_line>::iterator j = i; j!=option_help_strings.end() && (*j).first; ++j ){
00269           int len = get_option_usage_prefix( *((*j).first) ).length();
00270           max_option_prefix_len = std::max (max_option_prefix_len, len);
00271         }
00272       }
00273       max_option_prefix_len = std::min( max_option_prefix_len, max_padding );
00274       std::string option_prefix = get_option_usage_prefix( *option );
00275 
00276       out << std::setw(max_option_prefix_len) << std::left <<  option_prefix; 
00277       out << ": ";
00278     }
00279     else{ 
00280       // no option: this is a help header.  Reset max name length.
00281       max_option_prefix_len = 0;
00282     }
00283     out << info << std::endl;
00284   }
00285 }
00286 
00287 std::string ProgOptions::get_option_usage_prefix( const  ProgOpt& option ){
00288   bool has_shortname = option.shortname.length() > 0;
00289   bool has_longname  = option.longname.length() > 0;  
00290   std::string argstr = option.get_argstring();
00291 
00292   std::stringstream s;
00293   s << "  ";
00294   if( has_shortname ){
00295     
00296     s << "-" << option.shortname;
00297     if( has_longname ){ s << " "; }
00298     
00299   }
00300   else if ( option.flags & int_flag ) {
00301     
00302     s << "-<n>";
00303     if( has_longname ){ s << " "; }
00304     
00305   }
00306   if( has_longname ){
00307     
00308     if( has_shortname ) s << "[";
00309     s << "--" << option.longname; 
00310     if( has_shortname ) s << "]"; 
00311     
00312   }
00313   
00314   if( argstr.length() ) s << " <" << argstr << ">";
00315   return s.str();
00316 }
00317 
00318 void ProgOptions::printUsage( std::ostream& out ){
00319 
00320   out << "Usage: " << progname << " --help | [options] ";
00321 
00322   for (size_t i = 0 ; i < arg_help_strings.size(); ++i)
00323   {
00324     if (!expect_optional_args || i != optional_args_position) 
00325       out << '<' << arg_help_strings[i].first->longname << "> "; 
00326     else if (0 == max_optional_args || max_optional_args > 3)
00327       out << "[<" << arg_help_strings[i].first->longname << "> ...] ";
00328     else if (1 == max_optional_args)
00329       out << "[" << arg_help_strings[i].first->longname << "] ";
00330     else for (unsigned j = 0; j < max_optional_args; ++j)
00331       out << "[" << arg_help_strings[i].first->longname << (j+1) << "] ";
00332   }
00333     
00334   out << std::endl;
00335 
00336 }
00337 
00338 
00339 ProgOpt* ProgOptions::lookup( const std::map<std::string, ProgOpt* >& table, const std::string& arg ){
00340   std::map<std::string, ProgOpt*>::const_iterator it = table.find( arg );
00341   if (it != table.end())
00342     return it->second;
00343   else if (&table == &short_names && 
00344            arg.size() == 1 && 
00345            isdigit(arg[0]) &&
00346            !number_option_name.empty() &&
00347            (it = long_names.find(number_option_name)) != long_names.end())
00348     return it->second;
00349   else
00350     return 0;
00351 }
00352 
00353 ProgOpt* ProgOptions::lookup_option( const std::string& namestring ){
00354   std::string longname, shortname;
00355   get_namestrings( namestring, &longname, &shortname );
00356   
00357   ProgOpt* opt = lookup( long_names, longname );
00358   if( !opt ) opt = lookup( short_names, shortname );
00359   
00360   if( !opt ){
00361     error( "Invalid option: " + namestring );
00362   }
00363   
00364   return opt;
00365 }
00366 
00367 void ProgOptions::error( const std::string& err ){
00368   std::cerr << "Error: " << err << "\n"<< std::endl;;
00369   printUsage( std::cerr );
00370   std::cerr << std::endl;
00371   if (getenv("MOAB_PROG_OPT_ABORT"))
00372     abort();
00373   std::exit( EXIT_FAILURE );
00374 }
00375 
00376 // Copied from convert.cpp
00377 // Parse list of integer ranges
00378 // e.g. 1,2,5-10,12
00379 static
00380 bool parse_int_list( const char* string, std::vector<int>& results )
00381 {
00382   bool okay = true;
00383   char* mystr = strdup( string );
00384   for (const char* ptr = strtok(mystr, ", \t"); ptr; ptr = strtok(0,", \t"))
00385   {
00386     char* endptr;
00387     long val = strtol( ptr, &endptr, 0 );
00388     if (endptr == ptr) {
00389       std::cerr << "Not an integer: \"" << ptr << '"' << std::endl;
00390       okay = false;
00391       break;
00392     }
00393     
00394     long val2 = val;
00395     if (*endptr == '-') {
00396       const char* sptr = endptr+1;
00397       val2 = strtol( sptr, &endptr, 0 );
00398       if (endptr == sptr) {
00399         std::cerr << "Not an integer: \"" << sptr << '"' << std::endl;
00400         okay = false;
00401         break;
00402       }
00403       if (val2 < val) {
00404         std::cerr << "Invalid id range: \"" << ptr << '"' << std::endl;
00405         okay = false;
00406         break;
00407       }
00408     }
00409     
00410     if (*endptr) {
00411       okay = false;
00412       break;
00413     }
00414     
00415     for (; val <= val2; ++val)
00416       results.push_back( (int)val );
00417 
00418   }
00419   
00420   free( mystr );
00421   return okay;    
00422 }
00423 
00424 
00425 // Copied from convert.cpp
00426 // Replace '%' with MPI rank iff compiled with MPI
00427 static
00428 std::string do_rank_subst( const std::string& s )
00429 {
00430 #ifndef USE_MPI
00431   return s;
00432 #else
00433   int rank, size;
00434   if (MPI_SUCCESS != MPI_Comm_rank( MPI_COMM_WORLD, &rank )
00435    || MPI_SUCCESS != MPI_Comm_size( MPI_COMM_WORLD, &size ))
00436     return s;
00437   int width = 1;
00438   while (size > 10) {
00439     size /= 10;
00440     width++;
00441   }
00442 
00443   size_t j = s.find( '%' );
00444   if (j == std::string::npos) 
00445     return s;
00446   
00447   std::ostringstream st;
00448   st << std::setfill('0');
00449   st << s.substr( 0, j );
00450   st << rank;
00451   
00452   size_t i;
00453   while ((i = s.find( '%', j+1)) != std::string::npos) {
00454     st << s.substr( j, i - j );
00455     st << std::setw(width) << rank;
00456     j = i;
00457   }
00458   st << s.substr( j+1 );
00459   return st.str();
00460 #endif
00461 }
00462 
00469 bool ProgOptions::evaluate( const ProgOpt& opt, void* target, const std::string& option, unsigned* arg_idx ){
00470 
00471   unsigned idx = arg_idx ? *arg_idx : opt.args.size()-1;
00472 
00473   switch( opt.type ){
00474   case FLAG:
00475     error("Cannot evaluate a flag");
00476     break;
00477   case INT:
00478     {
00479       int temp;
00480       int* i = target ? reinterpret_cast<int*>(target) : &temp;
00481       if( opt.args.size() < 1 ){
00482     error( "Missing argument to " + option + " option");
00483       }
00484       const char* arg = opt.args.at(idx).c_str();
00485       char* p;
00486       *i = std::strtol( arg, &p, 0 );
00487       if( *p != '\0' ){ error("Bad integer argument '" + opt.args.at(idx) + "' to " + option + " option."); }
00488       return true;
00489     }
00490   case REAL:
00491     {
00492       double temp;
00493       double* i = target ? reinterpret_cast<double*>(target) : &temp;
00494       if( opt.args.size() < 1 ){
00495     error( "Missing argument to " + option + " option");
00496       }
00497       const char* arg = opt.args.at(idx).c_str();
00498       char* p;
00499       *i = std::strtod( arg, &p );
00500       if( *p != '\0' ){ error("Bad real argument '" + opt.args.at(idx) + "' to " + option + " option."); }
00501       return true;
00502     
00503     }
00504   
00505   case STRING:
00506     {
00507       std::string temp;
00508       std::string* i = target ? reinterpret_cast<std::string*>(target) : &temp;
00509       if( opt.args.size() < 1 ){
00510     error( "Missing argument to " + option + " option");
00511       }
00512       if (opt.flags & rank_subst)
00513         *i = do_rank_subst( opt.args.at(idx) );
00514       else
00515         *i = opt.args.at(idx);
00516       return true;
00517     }
00518   
00519   case INT_VECT:
00520     {
00521       std::vector<int> temp;
00522       std::vector<int>* i = target ? reinterpret_cast<std::vector<int>*>(target) : &temp;
00523       if(!parse_int_list( opt.args.at(idx).c_str(), *i ))
00524         error( "Bad integer list '" + opt.args.at(idx) + "' to " + option + " option.");
00525       return true;
00526     }
00527 
00528   }
00529 
00530   return false;
00531 }
00532 
00533 template <typename T>
00534 bool ProgOptions::getOpt( const std::string& namestring, T* t ){
00535  
00536   ProgOpt* opt = lookup_option( namestring );
00537 
00538   if( get_opt_type<T>() != opt->type ){
00539     error( "Option '" + namestring + "' looked up with incompatible type" );
00540   }
00541   
00542   // This call to evaluate is inefficient, because opt was already evaluated when it was parsed.
00543   if( opt->args.size() ){
00544     if (t)
00545       evaluate( *opt, t, "" );
00546     return true;
00547   }
00548   else return false;
00549 
00550 }
00551 
00552 template <typename T>
00553 void ProgOptions::getOptAllArgs( const std::string& namestring, std::vector<T>& values ){
00554   ProgOpt* opt = lookup_option( namestring );
00555 
00556     // special case: if user asks for list of int, but argument
00557     // was INT_VECT, concatenate all lists
00558   if (get_opt_type<T>() == INT && opt->type == INT_VECT) {
00559     for (unsigned i = 0; i < opt->args.size(); ++i)
00560       evaluate( *opt, &values, "", &i );
00561     return;
00562   }
00563   
00564   if( get_opt_type<T>() != opt->type ){
00565     error( "Option '" + namestring + "' looked up with incompatible type" );
00566   }
00567   
00568   values.resize( opt->args.size() );
00569 
00570   // These calls to evaluate are inefficient, because the arguments were evaluated when they were parsed
00571   for( unsigned i = 0; i < opt->args.size(); ++i ){
00572     evaluate( *opt, &(values[i]), "", &i );
00573   }
00574 
00575 }
00576 
00577 int ProgOptions::numOptSet( const std::string& namestring ){
00578   std::string longname, shortname;
00579   get_namestrings( namestring, &longname, &shortname );
00580   
00581   ProgOpt* opt = lookup( long_names, longname );
00582   if( !opt ) opt = lookup( short_names, shortname );
00583 
00584   if( !opt ){
00585     error( "Could not look up option: " + namestring );
00586   }
00587   
00588   return opt->args.size();
00589 
00590 }
00591 
00592 template <typename T>
00593 T ProgOptions::getReqArg( const std::string& namestring ){
00594   
00595   ProgOpt* opt = lookup( required_args, namestring );
00596   
00597   if( !opt ){
00598     error( "Could not look up required arg: " + namestring );
00599   }
00600   
00601   // if parseProgramOptions succeeded, we can assume each required arg has a value,
00602   // so calling evaluate is valid
00603   T value;
00604   evaluate( *opt, &value, "" );
00605   return value; 
00606 
00607 }
00608 
00609 template <typename T>
00610 void ProgOptions::getArgs( const std::string& namestring, 
00611                            std::vector<T>& values )
00612 {
00613   ProgOpt* opt = lookup( required_args, namestring );
00614   
00615   if( !opt ){
00616     error( "Could not look up required arg: " + namestring );
00617   }
00618   
00619   
00620   if( get_opt_type<T>() != opt->type ){
00621     error( "Option '" + namestring + "' looked up with incompatible type" );
00622   }
00623   
00624   values.resize( opt->args.size() );
00625 
00626   // These calls to evaluate are inefficient, because the arguments were evaluated when they were parsed
00627   for( unsigned i = 0; i < opt->args.size(); ++i ){
00628     evaluate( *opt, &(values[i]), "", &i );
00629   }
00630 
00631 }
00632   
00633 
00634 // Process parsed option.
00635 // Returns true if value is still expected
00636 // Should never return true if optional value is passed
00637 // \param arg Used for error messages only
00638 bool ProgOptions::process_option( ProgOpt* opt, std::string arg, const char* value )
00639 {
00640   if( !opt ){
00641     if (arg == "--manpage") {
00642       write_man_page(std::cout);
00643       exit(0);
00644     }
00645     
00646     error ("Unknown option: " + arg );
00647   }
00648 
00649   if( opt->flags & help_flag ){
00650     printHelp( std::cout );
00651     exit( EXIT_SUCCESS );
00652   }
00653 
00654   if( opt->flags & version_flag ){
00655     printVersion( std::cout );
00656     exit( EXIT_SUCCESS );
00657   }
00658   
00659   if (opt->type != FLAG) {
00660     if (!value)
00661       return true;
00662     
00663     opt->args.push_back( value );
00664     evaluate( *opt, opt->storage, arg );
00665   }
00666   else {
00667     if (value) {
00668       error( "Unexpected value for flag: " + arg );
00669     }
00670 
00671     // do flag operations
00672     if( opt->cancel_opt ){ opt->cancel_opt->args.clear(); }
00673     if( opt->storage ){
00674       *static_cast<bool*>(opt->storage) = ( opt->flags & store_false ) ? false : true;            
00675     }
00676     opt->args.push_back(""); 
00677   }
00678   
00679   return false;
00680 }
00681 
00682 
00683 void ProgOptions::parseCommandLine( int argc, char* argv[] ){
00684   const char* name = strrchr(argv[0],'/');
00685   if (name)
00686     this->progname = ++name;
00687   else
00688     this->progname = argv[0];
00689 
00690   std::vector<const char*> args;
00691   std::list<ProgOpt*> expected_vals;
00692   bool no_more_flags = false;                    
00693 
00694     // Loop over all command line arguments
00695   for( int i = 1; i < argc; ++i ) {
00696     std::string arg(argv[i]);
00697     if (arg.empty())
00698       continue;
00699     
00700     if (!expected_vals.empty()) {
00701       ProgOpt* opt = expected_vals.front();
00702       expected_vals.pop_front();
00703       assert(opt->type != FLAG);
00704       opt->args.push_back( arg );
00705       evaluate( *opt, opt->storage, arg );
00706     }
00707     else if (!no_more_flags && arg[0] == '-') {
00708       if (arg.length() > 2 && arg[1] == '-') { // long opt
00709         size_t eq = arg.find_first_of('=');
00710         if (eq != std::string::npos) {
00711           ProgOpt* opt = lookup( long_names, arg.substr( 2, eq-2 ) );
00712           process_option( opt, arg, arg.substr( eq+1 ).c_str() );
00713         }
00714         else {
00715           ProgOpt* opt = lookup( long_names, arg.substr( 2 ) );
00716           if (process_option( opt, arg )) 
00717             expected_vals.push_back( opt );
00718         }
00719       }
00720       else if (arg == "--") { // --
00721         no_more_flags = true;
00722       }
00723       else for (size_t f = 1; f < arg.length(); ++f) { // for each short opt
00724         ProgOpt* opt = lookup( short_names, std::string(1,arg[f]) );
00725         if (opt && (opt->flags & int_flag) ) {
00726           const char val[] = { arg[f], 0 };
00727           process_option( opt, std::string(1,arg[f]), val );
00728         }
00729         else if (process_option( opt, std::string(1,arg[f]) ))
00730           expected_vals.push_back( opt );
00731       }
00732     }
00733     else{ 
00734       /* arguments */
00735       args.push_back(argv[i]);
00736     }
00737   }/* End loop over inputs */
00738 
00739     // Print error if any missing values
00740   if (!expected_vals.empty()) {
00741     error( "Missing value for option: -" + 
00742            expected_vals.front()->shortname + ",--" + 
00743            expected_vals.front()->longname );
00744   }
00745 
00746     // Process non-option arguments
00747   std::vector<help_line>::iterator arg_help_pos = arg_help_strings.begin();
00748   std::vector<const char*>::iterator arg_val_pos = args.begin();
00749   std::vector<help_line>::iterator opt_args_pos = arg_help_strings.end();
00750   size_t min_required_args = required_args.size();
00751   size_t max_required_args = required_args.size();
00752   if (expect_optional_args) {
00753     min_required_args--;
00754     if (max_optional_args)
00755       max_required_args += max_optional_args;
00756     else
00757       max_required_args = std::numeric_limits<int>::max();
00758     opt_args_pos = arg_help_pos + optional_args_position;
00759   }
00760   // check valid number of non-flag arguments
00761   if (args.size() < min_required_args) {
00762     size_t missing_pos = args.size();
00763     if (expect_optional_args && missing_pos >= optional_args_position)
00764       ++missing_pos;
00765     
00766     const std::string& missed_arg = arg_help_strings[missing_pos].first->longname; 
00767     error("Did not find required positional argument: " + missed_arg );
00768   }
00769   else if (args.size() > max_required_args) {
00770     error( "Unexpected argument: " + std::string(args[max_required_args]) );  
00771   }
00772   
00773   // proccess arguments up to the first optional argument
00774   // (or all arguments if no optional args)
00775   while (arg_help_pos != opt_args_pos) {
00776     ProgOpt* opt = arg_help_pos->first;
00777     ++arg_help_pos;
00778     opt->args.push_back( *arg_val_pos );
00779     evaluate( *opt, opt->storage, *arg_val_pos );
00780     ++arg_val_pos;
00781   }
00782   // process any optional args
00783   if (arg_help_pos != arg_help_strings.end()) {
00784     assert( arg_help_pos == opt_args_pos );
00785     size_t num_opt_args = args.size() + 1 - required_args.size();
00786     ProgOpt* opt = arg_help_pos->first;
00787     ++arg_help_pos;
00788     while (num_opt_args--) {
00789       opt->args.push_back( *arg_val_pos );
00790       evaluate( *opt, opt->storage, *arg_val_pos );
00791       ++arg_val_pos;
00792     }
00793   }
00794   // process any remaining args
00795   while (arg_help_pos != arg_help_strings.end()) {
00796     assert(arg_val_pos != args.end());
00797     ProgOpt* opt = arg_help_pos->first;
00798     ++arg_help_pos;
00799     opt->args.push_back( *arg_val_pos );
00800     evaluate( *opt, opt->storage, *arg_val_pos );
00801     ++arg_val_pos;
00802   }
00803   assert(arg_val_pos == args.end());
00804 }
00805 
00806 void ProgOptions::write_man_page( std::ostream& s )
00807 {
00808   // a leading '.' is a control character.  strip it if present.
00809   std::string lprogname;
00810   if (progname.empty() || progname[0] != '.')
00811     lprogname = progname;
00812   else {
00813     lprogname = progname.substr(1);
00814   }
00815 
00816   // Manpage controls:
00817   // .TH title
00818   // .SH section
00819   // .SS subsection
00820   // .P  paragraph
00821   // .HP hanging paragraph
00822   // .B  bold
00823   // .I  italic
00824   // .B  bold
00825   // .I  italic
00826   // .RS begin indent
00827   // .RE end indent
00828   // .RB alternating roman and blold
00829   // .BR alternating bold and roman
00830 
00831   std::vector<help_line>::iterator it;
00832   std::set<ProgOpt*> skip_list;
00833 
00834   // start man page
00835   s << std::endl << ".TH " << lprogname << " 1" << std::endl;
00836 
00837   // write NAME section
00838   s << std::endl << ".SH NAME" << std::endl
00839     << ".P " << std::endl
00840     << lprogname << " \\- ";
00841   if (brief_help.empty() && !main_help.empty())
00842     s << main_help.front();
00843   else
00844     s << brief_help;
00845   s << std::endl << std::endl;
00846 
00847   // write SYNOPSIS section
00848   s << std::endl << ".SH SYNOPSIS" << std::endl
00849     << ".HP" << std::endl 
00850     << ".B \"" << lprogname << '"' << std::endl;
00851   for (it = option_help_strings.begin(); it != option_help_strings.end(); ++it) {
00852     if (!it->first || 
00853         skip_list.find( it->first ) != skip_list.end() ||
00854         it->first->longname == "help")
00855       continue;
00856     
00857     if (it->first->type == FLAG) {
00858       char c = '[';
00859       s << ".RB";
00860       if (!it->first->shortname.empty()) {
00861         s << ' ' << c << " \"-" << it->first->shortname << '"';
00862         c = '|';
00863       }
00864       if (!it->first->longname.empty()) {
00865         s << ' ' << c << " \"--" << it->first->longname << '"';
00866       }
00867       if (it->first->cancel_opt) {
00868         skip_list.insert( it->first->cancel_opt );
00869         if (!it->first->cancel_opt->shortname.empty())
00870           s << " | \"-" << it->first->cancel_opt->shortname << '"';
00871         if (!it->first->cancel_opt->longname.empty()) 
00872           s << " | \"--" << it->first->cancel_opt->longname << '"';
00873       }
00874       s << " ]" << std::endl;
00875     }
00876     else if (it->first->flags & int_flag) {
00877       s << ".RB [ - <n>| \"--" << it->first->longname
00878         << "\" \"=" << it->first->get_argstring() << "]\"" << std::endl;
00879     }
00880     else {
00881       s << ".RB [ ";
00882       if (!it->first->shortname.empty()) 
00883         s << "\"-" << it->first->shortname << "\" \"\\ " << it->first->get_argstring();
00884       if (!it->first->shortname.empty() && !it->first->longname.empty())
00885         s << "|\" ";
00886       if (!it->first->longname.empty())
00887         s << "\"--" << it->first->longname
00888           << "\" \"=" << it->first->get_argstring();
00889       s << "]\"" << std::endl;
00890     }
00891   }
00892   for (it = arg_help_strings.begin(); it != arg_help_strings.end(); ++it) {
00893     if (!it->first)
00894       continue;
00895     
00896     if (!expect_optional_args || (it - arg_help_strings.begin()) != optional_args_position)
00897       s << it->first->longname << ' ';
00898     else if (1 == max_optional_args)
00899       s << '[' << it->first->longname << "] ";
00900     else 
00901       s << '[' << it->first->longname << " ...] ";
00902   }
00903   s << std::endl;
00904   s << ".HP" << std::endl 
00905     << ".B \"" << lprogname << " -h|--help\"" << std::endl;
00906   
00907     // write DESCRIPTION section
00908   s << std::endl << ".SH DESCRIPTION" << std::endl;
00909   if (main_help.empty())
00910     s << brief_help << std::endl;
00911   for (size_t i = 0; i < main_help.size(); ++i) {
00912     const std::string::size_type n = main_help[i].size();
00913     std::string::size_type j = 0, k;
00914     s << std::endl << ".P" << std::endl;
00915     while (j != n) {
00916       if (main_help[i][j] == '\n') {
00917         s << std::endl << ".P" << std::endl;
00918         ++j;
00919         continue;
00920       }
00921       k = main_help[i].find( "\n", j );
00922       if (k == std::string::npos)
00923         k = n;
00924       if (main_help[i][j] == '.')
00925         s << '\\';
00926       s << main_help[i].substr( j, k - j );
00927       j = k;
00928     }
00929   }
00930   
00931     // write OPTIONS section
00932   s << std::endl << ".SH OPTIONS" << std::endl;
00933   for (it = arg_help_strings.begin(); it != arg_help_strings.end(); ++it) {
00934     if (it->first) 
00935       s << ".IP \"" << it->first->longname << '"' << std::endl << it->second << std::endl;
00936     else
00937       s << ".SS " << it->first->longname << std::endl;
00938   }
00939   for (it = option_help_strings.begin(); it != option_help_strings.end(); ++it) {
00940     if (!it->first) {
00941       s << ".SS " << it->second << std::endl;
00942       continue;
00943     }
00944     
00945     s << ".IP \"";
00946     if (it->first->longname.empty())
00947       s << "-" << it->first->shortname;
00948     else if (it->first->shortname.empty())
00949       s << "--" << it->first->longname;
00950     else
00951       s << "-" << it->first->shortname << ", --" << it->first->longname;
00952     s << '"' << std::endl << it->second << std::endl;
00953   }
00954   s << std::endl;
00955 }
00956 
00957 
00958 /* Ensure g++ instantiates the template types we expect to use */
00959 
00960 #define DECLARE_OPTION_TYPE(T)                                 \
00961   template void ProgOptions::addOpt<T>( const std::string&, const std::string&, T*, int ); \
00962   template bool ProgOptions::getOpt<T>( const std::string&, T* ); 
00963 
00964 #define DECLARE_VALUED_OPTION_TYPE(T)                          \
00965   DECLARE_OPTION_TYPE(T)                                       \
00966   template void ProgOptions::getOptAllArgs<T> (const std::string&, std::vector<T>& ); \
00967   template void ProgOptions::addRequiredArg<T>( const std::string&, const std::string&, T*, int ); \
00968   template void ProgOptions::addOptionalArgs<T>( unsigned, const std::string&, const std::string&, int ); \
00969   template T ProgOptions::getReqArg<T>( const std::string& ); \
00970   template void ProgOptions::getArgs<T>( const std::string&, std::vector<T>& );
00971  
00972 DECLARE_OPTION_TYPE(void)
00973 DECLARE_VALUED_OPTION_TYPE(int)
00974 DECLARE_VALUED_OPTION_TYPE(double)
00975 DECLARE_VALUED_OPTION_TYPE(std::string)
00976 DECLARE_VALUED_OPTION_TYPE(std::vector<int>)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines