00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "Teuchos_CommandLineProcessor.hpp"
00033 #include "Teuchos_GlobalMPISession.hpp"
00034 #include "Teuchos_VerboseObject.hpp"
00035 #include "Teuchos_TestForException.hpp"
00036
00037 namespace {
00038
00039 inline int my_max( int a, int b ) { return a > b ? a : b; }
00040
00041 std::string remove_quotes( const std::string& str )
00042 {
00043 if(str[0] != '\"')
00044 return str;
00045 return str.substr(1,str.size()-2);
00046 }
00047
00048 std::string add_quotes( const std::string& str )
00049 {
00050 if(str[0] == '\"')
00051 return str;
00052 return "\"" + str + "\"";
00053 }
00054
00055 }
00056
00057 namespace Teuchos {
00058
00059 const bool CommandLineProcessor::output_all_front_matter_default_(false);
00060 const bool CommandLineProcessor::output_show_line_prefix_default_(false);
00061 const bool CommandLineProcessor::output_show_tab_count_default_(false);
00062 const bool CommandLineProcessor::output_show_proc_rank_default_(false);
00063 const int CommandLineProcessor::output_to_root_rank_only_default_(0);
00064
00065 CommandLineProcessor::CommandLineProcessor(
00066 bool throwExceptions_in
00067 ,bool recogniseAllOptions_in
00068 ,bool addOutputSetupOptions_in
00069 )
00070 :throwExceptions_(throwExceptions_in)
00071 ,recogniseAllOptions_(recogniseAllOptions_in)
00072 ,addOutputSetupOptions_(addOutputSetupOptions_in)
00073 ,output_all_front_matter_(output_all_front_matter_default_)
00074 ,output_show_line_prefix_(output_show_line_prefix_default_)
00075 ,output_show_tab_count_(output_show_tab_count_default_)
00076 ,output_show_proc_rank_(output_show_proc_rank_default_)
00077 ,output_to_root_rank_only_(output_to_root_rank_only_default_)
00078 ,added_extra_output_setup_options_(false)
00079 ,in_add_extra_output_setup_options_(false)
00080 {}
00081
00082
00083
00084 void CommandLineProcessor::setDocString( const char doc_string[] )
00085 {
00086 doc_string_ = doc_string;
00087 }
00088
00089 void CommandLineProcessor::setOption(
00090 const char option_true[]
00091 ,const char option_false[]
00092 ,bool *option_val
00093 ,const char documentation[]
00094 )
00095 {
00096 add_extra_output_setup_options();
00097 TEST_FOR_EXCEPT(!(option_val!=NULL));
00098 options_list_[std::string(option_true)]
00099 = opt_val_val_t(OPT_BOOL_TRUE,any(option_val),false);
00100 options_list_[std::string(option_false)]
00101 = opt_val_val_t(OPT_BOOL_FALSE,any(option_val),false);
00102 options_documentation_list_.push_back(
00103 opt_doc_t(OPT_BOOL_TRUE,option_true,option_false,std::string(documentation?documentation:""),any(option_val)) );
00104 }
00105
00106 void CommandLineProcessor::setOption(
00107 const char option_name[]
00108 ,int *option_val
00109 ,const char documentation[]
00110 ,const bool required
00111 )
00112 {
00113 add_extra_output_setup_options();
00114 TEST_FOR_EXCEPT(!(option_val!=NULL));
00115 options_list_[std::string(option_name)]
00116 = opt_val_val_t(OPT_INT,any(option_val),required);
00117 options_documentation_list_.push_back(
00118 opt_doc_t(OPT_INT,option_name,"",std::string(documentation?documentation:""),any(option_val)) );
00119 }
00120
00121 void CommandLineProcessor::setOption(
00122 const char option_name[]
00123 ,double *option_val
00124 ,const char documentation[]
00125 ,const bool required
00126 )
00127 {
00128 add_extra_output_setup_options();
00129 TEST_FOR_EXCEPT(!(option_val!=NULL));
00130 options_list_[std::string(option_name)]
00131 = opt_val_val_t(OPT_DOUBLE,any(option_val),required);
00132 options_documentation_list_.push_back(
00133 opt_doc_t(OPT_DOUBLE,option_name,"",std::string(documentation?documentation:""),any(option_val)) );
00134 }
00135
00136 void CommandLineProcessor::setOption(
00137 const char option_name[]
00138 ,std::string *option_val
00139 ,const char documentation[]
00140 ,const bool required
00141 )
00142 {
00143 add_extra_output_setup_options();
00144 TEST_FOR_EXCEPT(!(option_val!=NULL));
00145 options_list_[std::string(option_name)]
00146 = opt_val_val_t(OPT_STRING,any(option_val),required);
00147 options_documentation_list_.push_back(
00148 opt_doc_t(OPT_STRING,option_name,"",std::string(documentation?documentation:""),any(option_val)) );
00149 }
00150
00151
00152
00153 CommandLineProcessor::EParseCommandLineReturn
00154 CommandLineProcessor::parse(
00155 int argc
00156 ,char* argv[]
00157 ,std::ostream *errout
00158 ) const
00159 {
00160 add_extra_output_setup_options();
00161 std::string opt_name;
00162 std::string opt_val_str;
00163 const std::string echo_cl_opt = "echo-command-line";
00164 const std::string help_opt = "help";
00165 const std::string pause_opt = "pause-for-debugging";
00166 int procRank = GlobalMPISession::getRank();
00167 for( int i = 1; i < argc; ++i ) {
00168 bool gov_return = get_opt_val( argv[i], &opt_name, &opt_val_str );
00169 if( !gov_return ) {
00170 if(recogniseAllOptions()) {
00171 if(procRank == 0)
00172 print_bad_opt(i,argv,errout);
00173 return PARSE_UNRECOGNIZED_OPTION;
00174 }
00175 else {
00176 continue;
00177 }
00178 }
00179 if( opt_name == echo_cl_opt ) {
00180 if(errout && procRank == 0) {
00181 *errout << "\nEchoing the command-line:\n\n";
00182 for( int j = 0; j < argc; ++j )
00183 *errout << argv[j] << " ";
00184 *errout << "\n\n";
00185 }
00186 continue;
00187 }
00188 if( opt_name == help_opt ) {
00189 if(errout) printHelpMessage( argv[0], *errout );
00190 return PARSE_HELP_PRINTED;
00191 }
00192 if( opt_name == pause_opt ) {
00193 if(procRank == 0) {
00194 std::cerr << "\nType 0 and press enter to continue : ";
00195 int dummy_int = 0;
00196 std::cin >> dummy_int;
00197 }
00198 #ifdef HAVE_MPI
00199 MPI_Barrier(MPI_COMM_WORLD);
00200 #endif
00201 continue;
00202 }
00203
00204 options_list_t::iterator itr = options_list_.find(opt_name);
00205 if( itr == options_list_.end() ) {
00206 if(procRank == 0)
00207 print_bad_opt(i,argv,errout);
00208 if( recogniseAllOptions() )
00209 return PARSE_UNRECOGNIZED_OPTION;
00210 else
00211 continue;
00212 }
00213
00214
00215 opt_val_val_t &opt_val_val = (*itr).second;
00216 opt_val_val.was_read = true;
00217 switch( opt_val_val.opt_type ) {
00218 case OPT_BOOL_TRUE:
00219 *(any_cast<bool*>(opt_val_val.opt_val)) = true;
00220 break;
00221 case OPT_BOOL_FALSE:
00222 *(any_cast<bool*>(opt_val_val.opt_val)) = false;
00223 break;
00224 case OPT_INT:
00225 *(any_cast<int*>(opt_val_val.opt_val)) = std::atoi(opt_val_str.c_str());
00226 break;
00227 case OPT_DOUBLE:
00228 *(any_cast<double*>(opt_val_val.opt_val)) = std::atof(opt_val_str.c_str());
00229 break;
00230 case OPT_STRING:
00231 *(any_cast<std::string*>(opt_val_val.opt_val)) = remove_quotes(opt_val_str);
00232 break;
00233 case OPT_ENUM_INT:
00234 if( !set_enum_value( i, argv, opt_name, any_cast<int>(opt_val_val.opt_val), remove_quotes(opt_val_str), errout ) )
00235 return PARSE_UNRECOGNIZED_OPTION;
00236 break;
00237 default:
00238 TEST_FOR_EXCEPT(true);
00239 }
00240 }
00241
00242 for(
00243 options_list_t::const_iterator itr = options_list_.begin();
00244 itr != options_list_.end();
00245 ++itr
00246 )
00247 {
00248 const std::string &opt_val_name = (*itr).first;
00249 const opt_val_val_t &opt_val_val = (*itr).second;
00250 if( opt_val_val.required && !opt_val_val.was_read ) {
00251 TEST_FOR_EXCEPTION(
00252 true, std::logic_error
00253 ,"Error, the option --"<<opt_val_name<<" was required but was not set!"
00254 );
00255 }
00256 }
00257
00258 RCP<FancyOStream>
00259 defaultOut = VerboseObjectBase::getDefaultOStream();
00260 if( defaultOut.get() && addOutputSetupOptions_ ) {
00261 if( output_all_front_matter_ != output_all_front_matter_default_ )
00262 defaultOut->setShowAllFrontMatter(output_all_front_matter_);
00263 if( output_show_line_prefix_ != output_show_line_prefix_default_ )
00264 defaultOut->setShowLinePrefix(output_show_line_prefix_);
00265 if( output_show_tab_count_ != output_show_tab_count_default_ )
00266 defaultOut->setShowTabCount(output_show_tab_count_);
00267 if( output_show_proc_rank_ != output_show_proc_rank_default_ )
00268 defaultOut->setShowProcRank(output_show_proc_rank_);
00269 if( output_to_root_rank_only_ != output_to_root_rank_only_default_ )
00270 defaultOut->setOutputToRootOnly(output_to_root_rank_only_);
00271 }
00272 return PARSE_SUCCESSFUL;
00273 }
00274
00275 void CommandLineProcessor::printHelpMessage( const char program_name[], std::ostream &out ) const
00276 {
00277 add_extra_output_setup_options();
00278 int procRank = GlobalMPISession::getRank();
00279 if (procRank == 0) {
00280 using std::setw;
00281 using std::endl;
00282
00283 const int opt_type_w = 8;
00284 const char spc_chars[] = " ";
00285
00286
00287 int opt_name_w = 19;
00288 options_documentation_list_t::const_iterator itr;
00289 for( itr = options_documentation_list_.begin(); itr != options_documentation_list_.end(); ++itr ) {
00290 opt_name_w = my_max(opt_name_w,itr->opt_name.length());
00291 if( itr->opt_type )
00292 opt_name_w = my_max(opt_name_w,itr->opt_name_false.length());
00293 }
00294 opt_name_w += 2;
00295
00296
00297 out
00298 << "Usage: " << program_name << " [options]\n"
00299 << spc_chars << "options:\n"
00300 << spc_chars
00301 << "--"
00302 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00303 << std::left << setw(opt_name_w) << "help"
00304 << std::left << setw(opt_type_w) << " "
00305 #else
00306 << std::setiosflags(std::ios::left) << setw(opt_name_w) << "help"
00307 << std::setiosflags(std::ios::left) << setw(opt_type_w) << " "
00308 #endif
00309 << "Prints this help message"
00310 << std::endl
00311 << spc_chars
00312 << "--"
00313 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00314 << std::left << setw(opt_name_w) << "pause-for-debugging"
00315 << std::left << setw(opt_type_w) << " "
00316 #else
00317 << std::setiosflags(std::ios::left) << setw(opt_name_w) << "pause-for-debugging"
00318 << std::setiosflags(std::ios::left) << setw(opt_type_w) << " "
00319 #endif
00320 << "Pauses for user input to allow attaching a debugger"
00321 << std::endl
00322 << spc_chars
00323 << "--"
00324 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00325 << std::left << setw(opt_name_w) << "echo-command-line"
00326 << std::left << setw(opt_type_w) << " "
00327 #else
00328 << std::setiosflags(std::ios::left) << setw(opt_name_w) << "echo-command-line"
00329 << std::setiosflags(std::ios::left) << setw(opt_type_w) << " "
00330 #endif
00331 << "Echo the command-line but continue as normal"
00332 << std::endl;
00333 for(
00334 itr = options_documentation_list_.begin();
00335 itr != options_documentation_list_.end();
00336 ++itr )
00337 {
00338
00339 out
00340 << spc_chars
00341 << "--"
00342 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00343 << std::left << setw(opt_name_w) << itr->opt_name
00344 << std::left << setw(opt_type_w) << opt_type_str(itr->opt_type)
00345 #else
00346 << std::setiosflags(std::ios::left) << setw(opt_name_w) << itr->opt_name
00347 << std::setiosflags(std::ios::left) << setw(opt_type_w) << opt_type_str(itr->opt_type)
00348 #endif
00349 << ( itr->documentation.length() ? itr->documentation.c_str() : "No documentation" )
00350 << std::endl;
00351
00352 if( itr->opt_type == OPT_ENUM_INT ) {
00353 out
00354 << spc_chars
00355 << " "
00356 << setw(opt_name_w) << ""
00357 << setw(opt_type_w) << "";
00358 print_enum_opt_names( any_cast<int>(itr->default_val), out );
00359 out
00360 << std::endl;
00361 }
00362
00363 if( itr->opt_type == OPT_BOOL_TRUE ) {
00364 out
00365 << spc_chars
00366 << "--"
00367 << setw(opt_name_w) << itr->opt_name_false;
00368 }
00369 else {
00370 out
00371 << spc_chars
00372 << " "
00373 << setw(opt_name_w) << " ";
00374 }
00375 out
00376 << setw(opt_type_w) << " "
00377 << "(default: ";
00378 switch( itr->opt_type ) {
00379 case OPT_BOOL_TRUE:
00380 out << "--" << ( (*(any_cast<bool*>(itr->default_val))) ? itr->opt_name : itr->opt_name_false );
00381 break;
00382 case OPT_INT:
00383 case OPT_DOUBLE:
00384 case OPT_STRING:
00385 case OPT_ENUM_INT:
00386 out << "--" << itr->opt_name;
00387 break;
00388 default:
00389 TEST_FOR_EXCEPT(true);
00390 }
00391 switch( itr->opt_type ) {
00392 case OPT_BOOL_TRUE:
00393 break;
00394 case OPT_INT:
00395 out << "=" << (*(any_cast<int*>(itr->default_val)));
00396 break;
00397 case OPT_DOUBLE:
00398 out << "=" << (*(any_cast<double*>(itr->default_val)));
00399 break;
00400 case OPT_STRING:
00401 out << "=" << add_quotes(*(any_cast<std::string*>(itr->default_val)));
00402 break;
00403 case OPT_ENUM_INT:
00404 out << "=" << add_quotes(enum_opt_default_val_name(itr->opt_name,any_cast<int>(itr->default_val),&out));
00405 break;
00406 default:
00407 TEST_FOR_EXCEPT(true);
00408 }
00409 out << ")\n";
00410 }
00411 if(doc_string_.length()) {
00412 out << "\nDETAILED DOCUMENTATION:\n\n" << doc_string_ << std::endl << std::endl;
00413 }
00414 if(throwExceptions_)
00415 TEST_FOR_EXCEPTION( true, HelpPrinted, "Help message was printed" );
00416 }
00417 }
00418
00419
00420
00421 void CommandLineProcessor::add_extra_output_setup_options() const
00422 {
00423 if(
00424 in_add_extra_output_setup_options_
00425 ||
00426 added_extra_output_setup_options_
00427 ||
00428 !addOutputSetupOptions_
00429 )
00430 {
00431 return;
00432 }
00433
00434 CommandLineProcessor
00435 *clp = const_cast<CommandLineProcessor*>(this);
00436 clp->in_add_extra_output_setup_options_ = true;
00437 clp->setOption(
00438 "output-all-front-matter","output-no-front-matter",&clp->output_all_front_matter_
00439 ,"Set if all front matter is printed to the default FancyOStream or not"
00440 );
00441 clp->setOption(
00442 "output-show-line-prefix","output-no-show-line-prefix",&clp->output_show_line_prefix_
00443 ,"Set if the line prefix matter is printed to the default FancyOStream or not"
00444 );
00445 clp->setOption(
00446 "output-show-tab-count","output-no-show-tab-count",&clp->output_show_tab_count_
00447 ,"Set if the tab count is printed to the default FancyOStream or not"
00448 );
00449 clp->setOption(
00450 "output-show-proc-rank","output-no-show-proc-rank",&clp->output_show_proc_rank_
00451 ,"Set if the processor rank is printed to the default FancyOStream or not"
00452 );
00453 clp->setOption(
00454 "output-to-root-rank-only",&clp->output_to_root_rank_only_
00455 ,"Set which processor (the root) gets the output. If < 0, then all processors get output."
00456 );
00457 clp->added_extra_output_setup_options_ = true;
00458 clp->in_add_extra_output_setup_options_ = false;
00459 }
00460
00461 void CommandLineProcessor::setEnumOption(
00462 const char enum_option_name[]
00463 ,int *enum_option_val
00464 ,const int num_enum_opt_values
00465 ,const int enum_opt_values[]
00466 ,const char* enum_opt_names[]
00467 ,const char documentation[]
00468 ,const bool required
00469 )
00470 {
00471 add_extra_output_setup_options();
00472
00473 TEST_FOR_EXCEPT(enum_option_val==NULL);
00474 TEST_FOR_EXCEPT(num_enum_opt_values<=0);
00475 TEST_FOR_EXCEPT(enum_opt_values==NULL);
00476 TEST_FOR_EXCEPT(enum_opt_names==NULL);
00477
00478 enum_opt_data_list_.push_back(
00479 enum_opt_data_t(enum_option_val,num_enum_opt_values,enum_opt_values,enum_opt_names)
00480 );
00481 const int opt_id = enum_opt_data_list_.size()-1;
00482 options_list_[std::string(enum_option_name)]
00483 = opt_val_val_t(OPT_ENUM_INT,any(opt_id),required);
00484 options_documentation_list_.push_back(
00485 opt_doc_t(OPT_ENUM_INT,enum_option_name,"",std::string(documentation?documentation:""),any(opt_id))
00486 );
00487 }
00488
00489 bool CommandLineProcessor::set_enum_value(
00490 int argv_i
00491 ,char* argv[]
00492 ,const std::string &enum_opt_name
00493 ,const int enum_id
00494 ,const std::string &enum_str_val
00495 ,std::ostream *errout
00496 ) const
00497 {
00498 const enum_opt_data_t
00499 &enum_opt_data = enum_opt_data_list_.at(enum_id);
00500 std::vector<std::string>::const_iterator
00501 itr_begin = enum_opt_data.enum_opt_names.begin(),
00502 itr_end = enum_opt_data.enum_opt_names.end(),
00503 itr = std::find( itr_begin, itr_end, enum_str_val );
00504 if( itr == itr_end ) {
00505 const int j = argv_i;
00506 #define CLP_ERR_MSG \
00507 "Error, the value \"" << enum_str_val << "\" for the " \
00508 << j<<(j==1?"st":(j==2?"nd":(j==3?"rd":"th"))) << " option --" \
00509 << enum_opt_name << " was not recognized (use --help)!"
00510 if(errout)
00511 *errout << std::endl << argv[0] << " : " << CLP_ERR_MSG << std::endl;
00512 if( throwExceptions() ) {
00513 TEST_FOR_EXCEPTION( true, std::invalid_argument, CLP_ERR_MSG );
00514 }
00515 else {
00516 return false;
00517 }
00518 #undef CLP_ERR_MSG
00519 }
00520 const int enum_opt_val_index = itr - itr_begin;
00521 *enum_opt_data.enum_option_val = enum_opt_data.enum_opt_values.at(enum_opt_val_index);
00522 return true;
00523 }
00524
00525 void CommandLineProcessor::print_enum_opt_names(
00526 const int enum_id
00527 ,std::ostream &out
00528 ) const
00529 {
00530 const enum_opt_data_t
00531 &enum_opt_data = enum_opt_data_list_.at(enum_id);
00532 typedef std::vector<std::string>::const_iterator itr_t;
00533 out << "Valid options:";
00534 for( itr_t itr = enum_opt_data.enum_opt_names.begin(); itr != enum_opt_data.enum_opt_names.end() ; ++itr ) {
00535 if( itr != enum_opt_data.enum_opt_names.begin() ) out << ",";
00536 out << " " << add_quotes(*itr);
00537 }
00538 }
00539
00540 std::string
00541 CommandLineProcessor::enum_opt_default_val_name(
00542 const std::string &enum_name
00543 ,const int enum_id
00544 ,std::ostream *errout
00545 ) const
00546 {
00547 const enum_opt_data_t
00548 &enum_opt_data = enum_opt_data_list_.at(enum_id);
00549 return enum_opt_data.enum_opt_names.at(
00550 find_enum_opt_index(
00551 enum_name,*enum_opt_data.enum_option_val,enum_opt_data,errout
00552 )
00553 );
00554 }
00555
00556 int CommandLineProcessor::find_enum_opt_index(
00557 const std::string &enum_opt_name
00558 ,const int opt_value
00559 ,const enum_opt_data_t &enum_data
00560 ,std::ostream *errout
00561 ) const
00562 {
00563 std::vector<int>::const_iterator
00564 itr_begin = enum_data.enum_opt_values.begin(),
00565 itr_end = enum_data.enum_opt_values.end(),
00566 itr = std::find( itr_begin, itr_end, opt_value );
00567 if( itr == itr_end ) {
00568 #define CLP_ERR_MSG \
00569 ( recogniseAllOptions() ? "Error" : "Warning" ) \
00570 << ", option --" << enum_opt_name << " was given an invalid " \
00571 "initial option value of " << opt_value << "!"
00572 if(errout)
00573 *errout << CLP_ERR_MSG << std::endl;
00574 if( throwExceptions() )
00575 TEST_FOR_EXCEPTION( true, std::invalid_argument, CLP_ERR_MSG );
00576 #undef CLP_ERR_MSG
00577 }
00578 return itr - itr_begin;
00579 }
00580
00581 bool CommandLineProcessor::get_opt_val(
00582 const char str[]
00583 ,std::string *opt_name
00584 ,std::string *opt_val_str
00585 ) const
00586 {
00587 const int len = std::strlen(str);
00588 if( len < 3 )
00589 return false;
00590 if( str[0] != '-' || str[1] != '-' )
00591 return false;
00592
00593 int equ_i;
00594 for( equ_i = 2; equ_i < len && str[equ_i] != '='; ++equ_i );
00595
00596 opt_name->assign( str + 2, equ_i-2 );
00597
00598 if( equ_i == len ) {
00599 *opt_val_str = "";
00600 }
00601 else {
00602 opt_val_str->assign( str + equ_i + 1, len - equ_i - 1 );
00603 }
00604 return true;
00605 }
00606
00607 void CommandLineProcessor::print_bad_opt(
00608 int argv_i
00609 ,char* argv[]
00610 ,std::ostream *errout
00611 ) const
00612 {
00613 const int j = argv_i;
00614 #define CLP_ERR_MSG \
00615 ( recogniseAllOptions() ? "Error" : "Warning" ) \
00616 << ", the " << j<<(j==1?"st":(j==2?"nd":(j==3?"rd":"th"))) \
00617 << " option \'" << argv[argv_i] << "\' was not recognized (use --help)!"
00618 if(errout)
00619 *errout << std::endl << argv[0] << " : " << CLP_ERR_MSG << std::endl;
00620 if( recogniseAllOptions() && throwExceptions() )
00621 TEST_FOR_EXCEPTION( true, UnrecognizedOption, CLP_ERR_MSG );
00622 #undef CLP_ERR_MSG
00623 }
00624
00625 }
00626
00627