moab
|
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>)