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