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 {
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
198
199
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
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
242
243 const int max_padding = 20;
244
245
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
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
287
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
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
407
408
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
458
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
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
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
597
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
613
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
648
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
672
673 for( unsigned i = 0; i < opt->args.size(); ++i )
674 {
675 evaluate( *opt, &( values[i] ), "", &i );
676 }
677 }
678
679
680
681
682
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
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
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 {
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 {
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
799 args.push_back( argv[i] );
800 }
801 }
802
803
804 if( !expected_vals.empty() )
805 {
806 error( "Missing value for option: -" + expected_vals.front()->shortname + ",--" +
807 expected_vals.front()->longname );
808 }
809
810
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
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
840
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
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
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
879 std::string lprogname;
880 if( progname.empty() || progname[0] != '.' )
881 lprogname = progname;
882 else
883 {
884 lprogname = progname.substr( 1 );
885 }
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902 std::vector< help_line >::iterator it;
903 std::set< ProgOpt* > skip_list;
904
905
906 s << std::endl << ".TH " << lprogname << " 1" << std::endl;
907
908
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
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
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
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
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 > )