MOAB: Mesh Oriented datABase  (version 5.4.1)
ProgOptions.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 #include <sstream>
00003 #include <iomanip>
00004 #include <cstdlib>
00005 #include <list>
00006 #include <limits>
00007 #include <set>
00008 #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 > )
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines