Loading [MathJax]/extensions/tex2jax.js
Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ProgOptions.cpp
Go to the documentation of this file.
1 #include <iostream> 2 #include <sstream> 3 #include <iomanip> 4 #include <cstdlib> 5 #include <list> 6 #include <limits> 7 #include <set> 8 #include <algorithm> 9  10 #include <cassert> 11 #include <cstring> 12  13 #include "moab/MOABConfig.h" 14 #include "moab/ProgOptions.hpp" 15 #ifdef MOAB_HAVE_MPI 16 #include "moab_mpi.h" 17 #endif 18  19 enum OptType 20 { 21  FLAG = 0, 22  INT, 23  REAL, 24  STRING, 25  INT_VECT 26 }; 27  28 template < typename T > 29 inline static OptType get_opt_type(); 30  31 template <> 32 OptType get_opt_type< void >() 33 { 34  return FLAG; 35 } 36 template <> 37 OptType get_opt_type< int >() 38 { 39  return INT; 40 } 41 template <> 42 OptType get_opt_type< double >() 43 { 44  return REAL; 45 } 46 template <> 47 OptType get_opt_type< std::string >() 48 { 49  return STRING; 50 } 51 template <> 52 OptType get_opt_type< std::vector< int > >() 53 { 54  return INT_VECT; 55 } 56  57 class ProgOpt 58 { 59  60  std::string shortname, longname; 61  std::vector< std::string > args; 62  OptType type; 63  void* storage; 64  int flags; 65  ProgOpt* cancel_opt; 66  67  const char* get_argstring() const 68  { 69  switch( type ) 70  { 71  case INT: 72  return "int"; 73  case INT_VECT: 74  return "ints"; 75  case REAL: 76  return "val"; 77  case FLAG: 78  return ""; 79  default: 80  return "arg"; 81  } 82  } 83  84  public: 85  ProgOpt( const std::string& longname_p, const std::string& shortname_p, int flags_p, OptType t = FLAG ) 86  : shortname( shortname_p ), longname( longname_p ), type( t ), storage( NULL ), flags( flags_p ), 87  cancel_opt( NULL ) 88  { 89  } 90  91  friend class ProgOptions; 92 }; 93  94 ProgOptions::ProgOptions( const std::string& helpstring, const std::string& briefhelp ) 95  : expect_optional_args( false ), optional_args_position( 0 ), max_optional_args( 0 ) 96 { 97  brief_help = briefhelp; 98  if( !helpstring.empty() ) main_help.push_back( helpstring ); 99  addOpt< void >( "help,h", "Show full help text", help_flag ); 100 } 101  102 ProgOptions::~ProgOptions() 103 { 104  for( std::vector< help_line >::iterator i = option_help_strings.begin(); i != option_help_strings.end(); ++i ) 105  { 106  if( ( *i ).first ) 107  { 108  delete( *i ).first; 109  } 110  } 111  112  for( std::vector< help_line >::iterator i = arg_help_strings.begin(); i != arg_help_strings.end(); ++i ) 113  { 114  delete( *i ).first; 115  } 116 } 117  118 void ProgOptions::get_namestrings( const std::string& namestring, std::string* longname, std::string* shortname ) 119 { 120  *shortname = ""; 121  *longname = namestring; 122  123  size_t idx = namestring.find_first_of( ',' ); 124  if( idx != namestring.npos ) 125  { 126  *longname = namestring.substr( 0, idx ); 127  *shortname = namestring.substr( idx + 1, namestring.npos ); 128  } 129 } 130  131 void ProgOptions::setVersion( const std::string& version_string, bool addFlag ) 132 { 133  progversion = version_string; 134  if( addFlag ) 135  { 136  addOpt< void >( "version", "Print version number and exit", version_flag ); 137  } 138 } 139  140 template < typename T > 141 void ProgOptions::addOpt( const std::string& namestring, const std::string& helpstring, T* value, int flags ) 142 { 143  144  std::string shortname, longname; 145  get_namestrings( namestring, &longname, &shortname ); 146  147  if( flags & int_flag ) 148  { // short name is implicit for this flag 149  if( !shortname.empty() ) error( "Requested short name with int_flag option" ); 150  if( get_opt_type< T >() != INT ) error( "Requested int_flag for non-integer option" ); 151  if( !number_option_name.empty() ) error( "Requested int_flag for multiple options" ); 152  number_option_name = longname; 153  } 154  155  ProgOpt* opt = new ProgOpt( longname, shortname, flags, get_opt_type< T >() ); 156  if( value ) opt->storage = value; 157  158  if( longname.length() ) long_names[longname] = opt; 159  if( shortname.length() ) short_names[shortname] = opt; 160  161  help_line help = std::make_pair( opt, helpstring ); 162  option_help_strings.push_back( help ); 163  164  if( flags & add_cancel_opt ) 165  { 166  std::string flag = "no-" + ( longname.length() ? longname : shortname ); 167  ProgOpt* cancel_opt = new ProgOpt( flag, "", flags ^ ProgOptions::store_false, FLAG ); 168  if( value ) cancel_opt->storage = value; 169  170  cancel_opt->cancel_opt = opt; 171  long_names[flag] = cancel_opt; 172  std::string clear_helpstring = "Clear previous " + flag.substr( 3, flag.npos ) + " flag"; 173  help = std::make_pair( cancel_opt, clear_helpstring ); 174  option_help_strings.push_back( help ); 175  } 176 } 177  178 template < typename T > 179 void ProgOptions::addRequiredArg( const std::string& helpname, const std::string& helpstring, T* value, int flags ) 180 { 181  182  OptType type = get_opt_type< T >(); 183  184  ProgOpt* opt = new ProgOpt( helpname, "", flags, type ); 185  if( value ) opt->storage = value; 186  help_line help = std::make_pair( opt, helpstring ); 187  arg_help_strings.push_back( help ); 188  required_args[helpname] = opt; 189 } 190  191 template < typename T > 192 void ProgOptions::addOptionalArgs( unsigned max_count, 193  const std::string& helpname, 194  const std::string& helpstring, 195  int flags ) 196 { 197  // If there was a previous one, we need to remove it 198  // because there can be only one. If we didn't remove 199  // the old one then it would be treated as a required arg. 200  if( expect_optional_args ) 201  { 202  std::map< std::string, ProgOpt* >::iterator iter; 203  iter = required_args.find( arg_help_strings[optional_args_position].second ); 204  assert( iter != required_args.end() ); 205  delete iter->second; 206  required_args.erase( iter ); 207  arg_help_strings.erase( arg_help_strings.begin() + optional_args_position ); 208  } 209  210  expect_optional_args = true; 211  optional_args_position = arg_help_strings.size(); 212  max_optional_args = max_count; 213  addRequiredArg< T >( helpname, helpstring, 0, flags ); 214 } 215  216 void ProgOptions::addOptionHelpHeading( const std::string& s ) 217 { 218  option_help_strings.push_back( std::make_pair( (ProgOpt*)NULL, s ) ); 219 } 220  221 void ProgOptions::printVersion( std::ostream& out ) 222 { 223  out << progversion << std::endl; 224 } 225  226 void ProgOptions::printHelp( std::ostream& out ) 227 { 228  229  /* Print introductory help text */ 230  if( !brief_help.empty() ) out << brief_help << std::endl; 231  for( std::vector< std::string >::iterator i = main_help.begin(); i != main_help.end(); ++i ) 232  { 233  if( ( *i ).length() ) 234  { 235  out << std::endl << *i << std::endl; 236  } 237  } 238  239  printUsage( out ); 240  241  // max number of characters to pad argument/option names with 242  // options with long names may exceed this, but will appear out of alignment in help text 243  const int max_padding = 20; 244  245  /* List required arguments, with help text */ 246  if( arg_help_strings.size() > 0 ) 247  { 248  249  int max_arg_namelen = 0; 250  251  for( std::vector< help_line >::iterator i = arg_help_strings.begin(); i != arg_help_strings.end(); ++i ) 252  { 253  max_arg_namelen = std::max( max_arg_namelen, (int)( ( *i ).first->longname.length() ) ); 254  } 255  256  max_arg_namelen = std::min( max_arg_namelen + 3, max_padding ); 257  258  out << "Arguments: " << std::endl; 259  260  for( std::vector< help_line >::iterator i = arg_help_strings.begin(); i != arg_help_strings.end(); ++i ) 261  { 262  ProgOpt* option = ( *i ).first; 263  std::string& info = ( *i ).second; 264  265  std::stringstream s; 266  s << " " << option->longname; 267  out << std::setw( max_arg_namelen ) << std::left << s.str(); 268  out << ": " << info << std::endl; 269  } 270  } 271  272  /* List options, with help text */ 273  out << "Options: " << std::endl; 274  int max_option_prefix_len = 0; 275  276  for( std::vector< help_line >::iterator i = option_help_strings.begin(); i != option_help_strings.end(); ++i ) 277  { 278  ProgOpt* option = ( *i ).first; 279  std::string& info = ( *i ).second; 280  281  if( option ) 282  { 283  284  if( max_option_prefix_len == 0 ) 285  { 286  // iterate ahead in the option list to determine whitespace padding 287  // stop if (*j).first is NULL, which indicates a help header message 288  for( std::vector< help_line >::iterator j = i; j != option_help_strings.end() && ( *j ).first; ++j ) 289  { 290  int len = get_option_usage_prefix( *( ( *j ).first ) ).length(); 291  max_option_prefix_len = std::max( max_option_prefix_len, len ); 292  } 293  } 294  max_option_prefix_len = std::min( max_option_prefix_len, max_padding ); 295  std::string option_prefix = get_option_usage_prefix( *option ); 296  297  out << std::setw( max_option_prefix_len ) << std::left << option_prefix; 298  out << ": "; 299  } 300  else 301  { 302  // no option: this is a help header. Reset max name length. 303  max_option_prefix_len = 0; 304  } 305  out << info << std::endl; 306  } 307 } 308  309 std::string ProgOptions::get_option_usage_prefix( const ProgOpt& option ) 310 { 311  bool has_shortname = option.shortname.length() > 0; 312  bool has_longname = option.longname.length() > 0; 313  std::string argstr = option.get_argstring(); 314  315  std::stringstream s; 316  s << " "; 317  if( has_shortname ) 318  { 319  320  s << "-" << option.shortname; 321  if( has_longname ) 322  { 323  s << " "; 324  } 325  } 326  else if( option.flags & int_flag ) 327  { 328  329  s << "-<n>"; 330  if( has_longname ) 331  { 332  s << " "; 333  } 334  } 335  if( has_longname ) 336  { 337  338  if( has_shortname ) s << "["; 339  s << "--" << option.longname; 340  if( has_shortname ) s << "]"; 341  } 342  343  if( argstr.length() ) s << " <" << argstr << ">"; 344  return s.str(); 345 } 346  347 void ProgOptions::printUsage( std::ostream& out ) 348 { 349  350  out << "Usage: " << progname << " --help | [options] "; 351  352  for( size_t i = 0; i < arg_help_strings.size(); ++i ) 353  { 354  if( !expect_optional_args || i != optional_args_position ) 355  out << '<' << arg_help_strings[i].first->longname << "> "; 356  else if( 0 == max_optional_args || max_optional_args > 3 ) 357  out << "[<" << arg_help_strings[i].first->longname << "> ...] "; 358  else if( 1 == max_optional_args ) 359  out << "[" << arg_help_strings[i].first->longname << "] "; 360  else 361  for( unsigned j = 0; j < max_optional_args; ++j ) 362  out << "[" << arg_help_strings[i].first->longname << ( j + 1 ) << "] "; 363  } 364  365  out << std::endl; 366 } 367  368 ProgOpt* ProgOptions::lookup( const std::map< std::string, ProgOpt* >& table, const std::string& arg ) 369 { 370  std::map< std::string, ProgOpt* >::const_iterator it = table.find( arg ); 371  if( it != table.end() ) 372  return it->second; 373  else if( &table == &short_names && arg.size() == 1 && isdigit( arg[0] ) && !number_option_name.empty() && 374  ( it = long_names.find( number_option_name ) ) != long_names.end() ) 375  return it->second; 376  else 377  return 0; 378 } 379  380 ProgOpt* ProgOptions::lookup_option( const std::string& namestring ) 381 { 382  std::string longname, shortname; 383  get_namestrings( namestring, &longname, &shortname ); 384  385  ProgOpt* opt = lookup( long_names, longname ); 386  if( !opt ) opt = lookup( short_names, shortname ); 387  388  if( !opt ) 389  { 390  error( "Invalid option: " + namestring ); 391  } 392  393  return opt; 394 } 395  396 void ProgOptions::error( const std::string& err ) 397 { 398  std::cerr << "Error: " << err << "\n" << std::endl; 399  ; 400  printUsage( std::cerr ); 401  std::cerr << std::endl; 402  if( getenv( "MOAB_PROG_OPT_ABORT" ) ) abort(); 403  std::exit( EXIT_FAILURE ); 404 } 405  406 // Copied from convert.cpp 407 // Parse list of integer ranges 408 // e.g. 1,2,5-10,12 409 static bool parse_int_list( const char* string, std::vector< int >& results ) 410 { 411  bool okay = true; 412  char* mystr = strdup( string ); 413  for( const char* ptr = strtok( mystr, ", \t" ); ptr; ptr = strtok( 0, ", \t" ) ) 414  { 415  char* endptr; 416  long val = strtol( ptr, &endptr, 0 ); 417  if( endptr == ptr ) 418  { 419  std::cerr << "Not an integer: \"" << ptr << '"' << std::endl; 420  okay = false; 421  break; 422  } 423  424  long val2 = val; 425  if( *endptr == '-' ) 426  { 427  const char* sptr = endptr + 1; 428  val2 = strtol( sptr, &endptr, 0 ); 429  if( endptr == sptr ) 430  { 431  std::cerr << "Not an integer: \"" << sptr << '"' << std::endl; 432  okay = false; 433  break; 434  } 435  if( val2 < val ) 436  { 437  std::cerr << "Invalid id range: \"" << ptr << '"' << std::endl; 438  okay = false; 439  break; 440  } 441  } 442  443  if( *endptr ) 444  { 445  okay = false; 446  break; 447  } 448  449  for( ; val <= val2; ++val ) 450  results.push_back( (int)val ); 451  } 452  453  free( mystr ); 454  return okay; 455 } 456  457 // Copied from convert.cpp 458 // Replace '%' with MPI rank iff compiled with MPI 459 static std::string do_rank_subst( const std::string& s ) 460 { 461 #ifndef MOAB_HAVE_MPI 462  return s; 463 #else 464  int rank, size; 465  if( MPI_SUCCESS != MPI_Comm_rank( MPI_COMM_WORLD, &rank ) || MPI_SUCCESS != MPI_Comm_size( MPI_COMM_WORLD, &size ) ) 466  return s; 467  int width = 1; 468  while( size > 10 ) 469  { 470  size /= 10; 471  width++; 472  } 473  474  size_t j = s.find( '%' ); 475  if( j == std::string::npos ) return s; 476  477  std::ostringstream st; 478  st << std::setfill( '0' ); 479  st << s.substr( 0, j ); 480  st << rank; 481  482  size_t i; 483  while( ( i = s.find( '%', j + 1 ) ) != std::string::npos ) 484  { 485  st << s.substr( j, i - j ); 486  st << std::setw( width ) << rank; 487  j = i; 488  } 489  st << s.substr( j + 1 ); 490  return st.str(); 491 #endif 492 } 493  494 /** 495  * Check the input to a given option for correctness, converting it to its expected type (e.g. int) 496  * and storing the result to target, if target is non-NULL. 497  * @param option Used only in error messages to state which option could not be successfully 498  * converted 499  * @param arg_idx If non-NULL, evaluate the (*arg_idx)'th item in opt's args list 500  */ 501 bool ProgOptions::evaluate( const ProgOpt& opt, void* target, const std::string& option, unsigned* arg_idx ) 502 { 503  504  unsigned idx = arg_idx ? *arg_idx : opt.args.size() - 1; 505  506  switch( opt.type ) 507  { 508  case FLAG: 509  error( "Cannot evaluate a flag" ); 510  break; 511  case INT: { 512  int temp; 513  int* i = target ? reinterpret_cast< int* >( target ) : &temp; 514  if( opt.args.size() < 1 ) 515  { 516  error( "Missing argument to " + option + " option" ); 517  } 518  const char* arg = opt.args.at( idx ).c_str(); 519  char* p; 520  *i = std::strtol( arg, &p, 0 ); 521  if( *p != '\0' ) 522  { 523  error( "Bad integer argument '" + opt.args.at( idx ) + "' to " + option + " option." ); 524  } 525  return true; 526  } 527  case REAL: { 528  double temp; 529  double* i = target ? reinterpret_cast< double* >( target ) : &temp; 530  if( opt.args.size() < 1 ) 531  { 532  error( "Missing argument to " + option + " option" ); 533  } 534  const char* arg = opt.args.at( idx ).c_str(); 535  char* p; 536  *i = std::strtod( arg, &p ); 537  if( *p != '\0' ) 538  { 539  error( "Bad real argument '" + opt.args.at( idx ) + "' to " + option + " option." ); 540  } 541  return true; 542  } 543  544  case STRING: { 545  std::string temp; 546  std::string* i = target ? reinterpret_cast< std::string* >( target ) : &temp; 547  if( opt.args.size() < 1 ) 548  { 549  error( "Missing argument to " + option + " option" ); 550  } 551  if( opt.flags & rank_subst ) 552  *i = do_rank_subst( opt.args.at( idx ) ); 553  else 554  *i = opt.args.at( idx ); 555  return true; 556  } 557  558  case INT_VECT: { 559  std::vector< int > temp; 560  std::vector< int >* i = target ? reinterpret_cast< std::vector< int >* >( target ) : &temp; 561  if( !parse_int_list( opt.args.at( idx ).c_str(), *i ) ) 562  error( "Bad integer list '" + opt.args.at( idx ) + "' to " + option + " option." ); 563  return true; 564  } 565  } 566  567  return false; 568 } 569  570 template < typename T > 571 bool ProgOptions::getOpt( const std::string& namestring, T* t ) 572 { 573  574  ProgOpt* opt = lookup_option( namestring ); 575  576  if( get_opt_type< T >() != opt->type ) 577  { 578  error( "Option '" + namestring + "' looked up with incompatible type" ); 579  } 580  581  // This call to evaluate is inefficient, because opt was already evaluated when it was parsed. 582  if( opt->args.size() ) 583  { 584  if( t ) evaluate( *opt, t, "" ); 585  return true; 586  } 587  else 588  return false; 589 } 590  591 template < typename T > 592 void ProgOptions::getOptAllArgs( const std::string& namestring, std::vector< T >& values ) 593 { 594  ProgOpt* opt = lookup_option( namestring ); 595  596  // special case: if user asks for list of int, but argument 597  // was INT_VECT, concatenate all lists 598  if( get_opt_type< T >() == INT && opt->type == INT_VECT ) 599  { 600  for( unsigned i = 0; i < opt->args.size(); ++i ) 601  evaluate( *opt, &values, "", &i ); 602  return; 603  } 604  605  if( get_opt_type< T >() != opt->type ) 606  { 607  error( "Option '" + namestring + "' looked up with incompatible type" ); 608  } 609  610  values.resize( opt->args.size() ); 611  612  // These calls to evaluate are inefficient, because the arguments were evaluated when they were 613  // parsed 614  for( unsigned i = 0; i < opt->args.size(); ++i ) 615  { 616  evaluate( *opt, &( values[i] ), "", &i ); 617  } 618 } 619  620 int ProgOptions::numOptSet( const std::string& namestring ) 621 { 622  std::string longname, shortname; 623  get_namestrings( namestring, &longname, &shortname ); 624  625  ProgOpt* opt = lookup( long_names, longname ); 626  if( !opt ) opt = lookup( short_names, shortname ); 627  628  if( !opt ) 629  { 630  error( "Could not look up option: " + namestring ); 631  } 632  633  return opt->args.size(); 634 } 635  636 template < typename T > 637 T ProgOptions::getReqArg( const std::string& namestring ) 638 { 639  640  ProgOpt* opt = lookup( required_args, namestring ); 641  642  if( !opt ) 643  { 644  error( "Could not look up required arg: " + namestring ); 645  } 646  647  // if parseProgramOptions succeeded, we can assume each required arg has a value, 648  // so calling evaluate is valid 649  T value; 650  evaluate( *opt, &value, "" ); 651  return value; 652 } 653  654 template < typename T > 655 void ProgOptions::getArgs( const std::string& namestring, std::vector< T >& values ) 656 { 657  ProgOpt* opt = lookup( required_args, namestring ); 658  659  if( !opt ) 660  { 661  error( "Could not look up required arg: " + namestring ); 662  } 663  664  if( get_opt_type< T >() != opt->type ) 665  { 666  error( "Option '" + namestring + "' looked up with incompatible type" ); 667  } 668  669  values.resize( opt->args.size() ); 670  671  // These calls to evaluate are inefficient, because the arguments were evaluated when they were 672  // parsed 673  for( unsigned i = 0; i < opt->args.size(); ++i ) 674  { 675  evaluate( *opt, &( values[i] ), "", &i ); 676  } 677 } 678  679 // Process parsed option. 680 // Returns true if value is still expected 681 // Should never return true if optional value is passed 682 // \param arg Used for error messages only 683 bool ProgOptions::process_option( ProgOpt* opt, std::string arg, const char* value ) 684 { 685  if( !opt ) 686  { 687  if( arg == "--manpage" ) 688  { 689  write_man_page( std::cout ); 690  exit( 0 ); 691  } 692  693  error( "Unknown option: " + arg ); 694  } 695  696  if( opt->flags & help_flag ) 697  { 698  printHelp( std::cout ); 699  exit( EXIT_SUCCESS ); 700  } 701  702  if( opt->flags & version_flag ) 703  { 704  printVersion( std::cout ); 705  exit( EXIT_SUCCESS ); 706  } 707  708  if( opt->type != FLAG ) 709  { 710  if( !value ) return true; 711  712  opt->args.push_back( value ); 713  evaluate( *opt, opt->storage, arg ); 714  } 715  else 716  { 717  if( value ) 718  { 719  error( "Unexpected value for flag: " + arg ); 720  } 721  722  // do flag operations 723  if( opt->cancel_opt ) 724  { 725  opt->cancel_opt->args.clear(); 726  } 727  if( opt->storage ) 728  { 729  *static_cast< bool* >( opt->storage ) = ( opt->flags & store_false ) ? false : true; 730  } 731  opt->args.push_back( "" ); 732  } 733  734  return false; 735 } 736  737 void ProgOptions::parseCommandLine( int argc, char* argv[] ) 738 { 739  const char* name = strrchr( argv[0], '/' ); 740  if( name ) 741  this->progname = ++name; 742  else 743  this->progname = argv[0]; 744  745  std::vector< const char* > args; 746  std::list< ProgOpt* > expected_vals; 747  bool no_more_flags = false; 748  749  // Loop over all command line arguments 750  for( int i = 1; i < argc; ++i ) 751  { 752  std::string arg( argv[i] ); 753  if( arg.empty() ) continue; 754  755  if( !expected_vals.empty() ) 756  { 757  ProgOpt* opt = expected_vals.front(); 758  expected_vals.pop_front(); 759  assert( opt->type != FLAG ); 760  opt->args.push_back( arg ); 761  evaluate( *opt, opt->storage, arg ); 762  } 763  else if( !no_more_flags && arg[0] == '-' ) 764  { 765  if( arg.length() > 2 && arg[1] == '-' ) 766  { // long opt 767  size_t eq = arg.find_first_of( '=' ); 768  if( eq != std::string::npos ) 769  { 770  ProgOpt* opt = lookup( long_names, arg.substr( 2, eq - 2 ) ); 771  process_option( opt, arg, arg.substr( eq + 1 ).c_str() ); 772  } 773  else 774  { 775  ProgOpt* opt = lookup( long_names, arg.substr( 2 ) ); 776  if( process_option( opt, arg ) ) expected_vals.push_back( opt ); 777  } 778  } 779  else if( arg == "--" ) 780  { // -- 781  no_more_flags = true; 782  } 783  else 784  for( size_t f = 1; f < arg.length(); ++f ) 785  { // for each short opt 786  ProgOpt* opt = lookup( short_names, std::string( 1, arg[f] ) ); 787  if( opt && ( opt->flags & int_flag ) ) 788  { 789  const char val[] = { arg[f], 0 }; 790  process_option( opt, std::string( 1, arg[f] ), val ); 791  } 792  else if( process_option( opt, std::string( 1, arg[f] ) ) ) 793  expected_vals.push_back( opt ); 794  } 795  } 796  else 797  { 798  /* arguments */ 799  args.push_back( argv[i] ); 800  } 801  } /* End loop over inputs */ 802  803  // Print error if any missing values 804  if( !expected_vals.empty() ) 805  { 806  error( "Missing value for option: -" + expected_vals.front()->shortname + ",--" + 807  expected_vals.front()->longname ); 808  } 809  810  // Process non-option arguments 811  std::vector< help_line >::iterator arg_help_pos = arg_help_strings.begin(); 812  std::vector< const char* >::iterator arg_val_pos = args.begin(); 813  std::vector< help_line >::iterator opt_args_pos = arg_help_strings.end(); 814  size_t min_required_args = required_args.size(); 815  size_t max_required_args = required_args.size(); 816  if( expect_optional_args ) 817  { 818  min_required_args--; 819  if( max_optional_args ) 820  max_required_args += max_optional_args; 821  else 822  max_required_args = std::numeric_limits< int >::max(); 823  opt_args_pos = arg_help_pos + optional_args_position; 824  } 825  // check valid number of non-flag arguments 826  if( args.size() < min_required_args ) 827  { 828  size_t missing_pos = args.size(); 829  if( expect_optional_args && missing_pos >= optional_args_position ) ++missing_pos; 830  831  const std::string& missed_arg = arg_help_strings[missing_pos].first->longname; 832  error( "Did not find required positional argument: " + missed_arg ); 833  } 834  else if( args.size() > max_required_args ) 835  { 836  error( "Unexpected argument: " + std::string( args[max_required_args] ) ); 837  } 838  839  // proccess arguments up to the first optional argument 840  // (or all arguments if no optional args) 841  while( arg_help_pos != opt_args_pos ) 842  { 843  ProgOpt* opt = arg_help_pos->first; 844  ++arg_help_pos; 845  opt->args.push_back( *arg_val_pos ); 846  evaluate( *opt, opt->storage, *arg_val_pos ); 847  ++arg_val_pos; 848  } 849  // process any optional args 850  if( arg_help_pos != arg_help_strings.end() ) 851  { 852  assert( arg_help_pos == opt_args_pos ); 853  size_t num_opt_args = args.size() + 1 - required_args.size(); 854  ProgOpt* opt = arg_help_pos->first; 855  ++arg_help_pos; 856  while( num_opt_args-- ) 857  { 858  opt->args.push_back( *arg_val_pos ); 859  evaluate( *opt, opt->storage, *arg_val_pos ); 860  ++arg_val_pos; 861  } 862  } 863  // process any remaining args 864  while( arg_help_pos != arg_help_strings.end() ) 865  { 866  assert( arg_val_pos != args.end() ); 867  ProgOpt* opt = arg_help_pos->first; 868  ++arg_help_pos; 869  opt->args.push_back( *arg_val_pos ); 870  evaluate( *opt, opt->storage, *arg_val_pos ); 871  ++arg_val_pos; 872  } 873  assert( arg_val_pos == args.end() ); 874 } 875  876 void ProgOptions::write_man_page( std::ostream& s ) 877 { 878  // a leading '.' is a control character. strip it if present. 879  std::string lprogname; 880  if( progname.empty() || progname[0] != '.' ) 881  lprogname = progname; 882  else 883  { 884  lprogname = progname.substr( 1 ); 885  } 886  887  // Manpage controls: 888  // .TH title 889  // .SH section 890  // .SS subsection 891  // .P paragraph 892  // .HP hanging paragraph 893  // .B bold 894  // .I italic 895  // .B bold 896  // .I italic 897  // .RS begin indent 898  // .RE end indent 899  // .RB alternating roman and blold 900  // .BR alternating bold and roman 901  902  std::vector< help_line >::iterator it; 903  std::set< ProgOpt* > skip_list; 904  905  // start man page 906  s << std::endl << ".TH " << lprogname << " 1" << std::endl; 907  908  // write NAME section 909  s << std::endl << ".SH NAME" << std::endl << ".P " << std::endl << lprogname << " \\- "; 910  if( brief_help.empty() && !main_help.empty() ) 911  s << main_help.front(); 912  else 913  s << brief_help; 914  s << std::endl << std::endl; 915  916  // write SYNOPSIS section 917  s << std::endl << ".SH SYNOPSIS" << std::endl << ".HP" << std::endl << ".B \"" << lprogname << '"' << std::endl; 918  for( it = option_help_strings.begin(); it != option_help_strings.end(); ++it ) 919  { 920  if( !it->first || skip_list.find( it->first ) != skip_list.end() || it->first->longname == "help" ) continue; 921  922  if( it->first->type == FLAG ) 923  { 924  char c = '['; 925  s << ".RB"; 926  if( !it->first->shortname.empty() ) 927  { 928  s << ' ' << c << " \"-" << it->first->shortname << '"'; 929  c = '|'; 930  } 931  if( !it->first->longname.empty() ) 932  { 933  s << ' ' << c << " \"--" << it->first->longname << '"'; 934  } 935  if( it->first->cancel_opt ) 936  { 937  skip_list.insert( it->first->cancel_opt ); 938  if( !it->first->cancel_opt->shortname.empty() ) 939  s << " | \"-" << it->first->cancel_opt->shortname << '"'; 940  if( !it->first->cancel_opt->longname.empty() ) s << " | \"--" << it->first->cancel_opt->longname << '"'; 941  } 942  s << " ]" << std::endl; 943  } 944  else if( it->first->flags & int_flag ) 945  { 946  s << ".RB [ - <n>| \"--" << it->first->longname << "\" \"=" << it->first->get_argstring() << "]\"" 947  << std::endl; 948  } 949  else 950  { 951  s << ".RB [ "; 952  if( !it->first->shortname.empty() ) 953  s << "\"-" << it->first->shortname << "\" \"\\ " << it->first->get_argstring(); 954  if( !it->first->shortname.empty() && !it->first->longname.empty() ) s << "|\" "; 955  if( !it->first->longname.empty() ) 956  s << "\"--" << it->first->longname << "\" \"=" << it->first->get_argstring(); 957  s << "]\"" << std::endl; 958  } 959  } 960  for( it = arg_help_strings.begin(); it != arg_help_strings.end(); ++it ) 961  { 962  if( !it->first ) continue; 963  964  if( !expect_optional_args || (unsigned)( it - arg_help_strings.begin() ) != optional_args_position ) 965  s << it->first->longname << ' '; 966  else if( 1 == max_optional_args ) 967  s << '[' << it->first->longname << "] "; 968  else 969  s << '[' << it->first->longname << " ...] "; 970  } 971  s << std::endl; 972  s << ".HP" << std::endl << ".B \"" << lprogname << " -h|--help\"" << std::endl; 973  974  // write DESCRIPTION section 975  s << std::endl << ".SH DESCRIPTION" << std::endl; 976  if( main_help.empty() ) s << brief_help << std::endl; 977  for( size_t i = 0; i < main_help.size(); ++i ) 978  { 979  const std::string::size_type n = main_help[i].size(); 980  std::string::size_type j = 0, k; 981  s << std::endl << ".P" << std::endl; 982  while( j != n ) 983  { 984  if( main_help[i][j] == '\n' ) 985  { 986  s << std::endl << ".P" << std::endl; 987  ++j; 988  continue; 989  } 990  k = main_help[i].find( "\n", j ); 991  if( k == std::string::npos ) k = n; 992  if( main_help[i][j] == '.' ) s << '\\'; 993  s << main_help[i].substr( j, k - j ); 994  j = k; 995  } 996  } 997  998  // write OPTIONS section 999  s << std::endl << ".SH OPTIONS" << std::endl; 1000  for( it = arg_help_strings.begin(); it != arg_help_strings.end(); ++it ) 1001  { 1002  if( it->first ) 1003  s << ".IP \"" << it->first->longname << '"' << std::endl << it->second << std::endl; 1004  else 1005  s << ".SS " << it->first->longname << std::endl; 1006  } 1007  for( it = option_help_strings.begin(); it != option_help_strings.end(); ++it ) 1008  { 1009  if( !it->first ) 1010  { 1011  s << ".SS " << it->second << std::endl; 1012  continue; 1013  } 1014  1015  s << ".IP \""; 1016  if( it->first->longname.empty() ) 1017  s << "-" << it->first->shortname; 1018  else if( it->first->shortname.empty() ) 1019  s << "--" << it->first->longname; 1020  else 1021  s << "-" << it->first->shortname << ", --" << it->first->longname; 1022  s << '"' << std::endl << it->second << std::endl; 1023  } 1024  s << std::endl; 1025 } 1026  1027 /* Ensure g++ instantiates the template types we expect to use */ 1028  1029 #define DECLARE_OPTION_TYPE( T ) \ 1030  template void ProgOptions::addOpt< T >( const std::string&, const std::string&, T*, int ); \ 1031  template bool ProgOptions::getOpt< T >( const std::string&, T* ); 1032  1033 #define DECLARE_VALUED_OPTION_TYPE( T ) \ 1034  DECLARE_OPTION_TYPE( T ) \ 1035  template void ProgOptions::getOptAllArgs< T >( const std::string&, std::vector< T >& ); \ 1036  template void ProgOptions::addRequiredArg< T >( const std::string&, const std::string&, T*, int ); \ 1037  template void ProgOptions::addOptionalArgs< T >( unsigned, const std::string&, const std::string&, int ); \ 1038  template T ProgOptions::getReqArg< T >( const std::string& ); \ 1039  template void ProgOptions::getArgs< T >( const std::string&, std::vector< T >& ); 1040  1041 DECLARE_OPTION_TYPE( void ) 1042 DECLARE_VALUED_OPTION_TYPE( int ) 1043 DECLARE_VALUED_OPTION_TYPE( double ) 1044 DECLARE_VALUED_OPTION_TYPE( std::string ) 1045 DECLARE_VALUED_OPTION_TYPE( std::vector< int > )