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