Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
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,
25  INT_VECT
26 };
27 
28 template < typename T >
29 inline static OptType get_opt_type();
30 
31 template <>
33 {
34  return FLAG;
35 }
36 template <>
38 {
39  return INT;
40 }
41 template <>
43 {
44  return REAL;
45 }
46 template <>
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;
63  void* storage;
64  int flags;
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 )
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 
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.
201  {
202  std::map< std::string, ProgOpt* >::iterator iter;
204  assert( iter != required_args.end() );
205  delete iter->second;
206  required_args.erase( iter );
208  }
209 
210  expect_optional_args = true;
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  {
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();
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 )
1044 DECLARE_VALUED_OPTION_TYPE( std::string )
1045 DECLARE_VALUED_OPTION_TYPE( std::vector< int > )