![]() |
Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 #include
00002 #include
00003 #include
00004 #include
00005 #include
00006 #include
00007 #include
00008 #include
00009
00010 #include
00011 #include
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 << "-";
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 [ - | \"--" << 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 > )