00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "programBase.h"
00016 #include "wordWrapStream.h"
00017
00018 #include "pnmFileTypeRegistry.h"
00019 #include "indent.h"
00020 #include "dSearchPath.h"
00021 #include "coordinateSystem.h"
00022 #include "dconfig.h"
00023 #include "config_dconfig.h"
00024 #include "string_utils.h"
00025 #include "vector_string.h"
00026 #include "configVariableInt.h"
00027 #include "configVariableBool.h"
00028 #include "panda_getopt_long.h"
00029 #include "preprocess_argv.h"
00030
00031 #include <stdlib.h>
00032 #include <algorithm>
00033 #include <ctype.h>
00034
00035
00036
00037
00038
00039 #ifdef IOCTL_TERMINAL_WIDTH
00040 #include <termios.h>
00041 #ifndef TIOCGWINSZ
00042 #include <sys/ioctl.h>
00043 #elif __APPLE__
00044 #include <sys/ioctl.h>
00045 #endif // TIOCGWINSZ
00046 #endif // IOCTL_TERMINAL_WIDTH
00047
00048 bool ProgramBase::SortOptionsByIndex::
00049 operator () (const Option *a, const Option *b) const {
00050 if (a->_index_group != b->_index_group) {
00051 return a->_index_group < b->_index_group;
00052 }
00053 return a->_sequence < b->_sequence;
00054 }
00055
00056
00057
00058
00059
00060 static void flush_nout() {
00061 nout << flush;
00062 }
00063
00064 static ConfigVariableInt default_terminal_width
00065 ("default-terminal-width", 72,
00066 PRC_DESC("Specify the column at which to wrap output lines "
00067 "from pandatool-based programs, if it cannot be determined "
00068 "automatically."));
00069
00070 static ConfigVariableBool use_terminal_width
00071 ("use-terminal-width", true,
00072 PRC_DESC("True to try to determine the terminal width automatically from "
00073 "the operating system, if supported; false to use the width "
00074 "specified by default-terminal-width even if the operating system "
00075 "appears to report a valid width."));
00076
00077
00078
00079
00080
00081
00082 ProgramBase::
00083 ProgramBase() {
00084
00085 Notify::ptr()->set_ostream_ptr(new WordWrapStream(this), true);
00086
00087
00088 atexit(&flush_nout);
00089
00090 _path_replace = new PathReplace;
00091
00092
00093
00094
00095 _path_replace->_path_store = PS_absolute;
00096 _got_path_store = false;
00097 _got_path_directory = false;
00098
00099 _next_sequence = 0;
00100 _sorted_options = false;
00101 _last_newline = false;
00102 _got_terminal_width = false;
00103 _got_option_indent = false;
00104
00105 add_option("h", "", 100,
00106 "Display this help page.",
00107 &ProgramBase::handle_help_option, NULL, (void *)this);
00108
00109
00110 nout << "\r";
00111 }
00112
00113
00114
00115
00116
00117
00118 ProgramBase::
00119 ~ProgramBase() {
00120
00121
00122 Notify::ptr()->set_ostream_ptr(NULL, false);
00123 }
00124
00125
00126
00127
00128
00129
00130 void ProgramBase::
00131 show_description() {
00132 nout << _description << "\n";
00133 }
00134
00135
00136
00137
00138
00139
00140 void ProgramBase::
00141 show_usage() {
00142 nout << "\rUsage:\n";
00143 Runlines::const_iterator ri;
00144 string prog = " " +_program_name.get_basename_wo_extension();
00145
00146 for (ri = _runlines.begin(); ri != _runlines.end(); ++ri) {
00147 show_text(prog, prog.length() + 1, *ri);
00148 }
00149 nout << "\r";
00150 }
00151
00152
00153
00154
00155
00156
00157 void ProgramBase::
00158 show_options() {
00159 sort_options();
00160 if (!_got_option_indent) {
00161 get_terminal_width();
00162 _option_indent = min(15, (int)(_terminal_width * 0.25));
00163 _got_option_indent = true;
00164 }
00165
00166 nout << "Options:\n";
00167 OptionsByIndex::const_iterator oi;
00168 for (oi = _options_by_index.begin(); oi != _options_by_index.end(); ++oi) {
00169 const Option &opt = *(*oi);
00170 string prefix = " -" + opt._option + " " + opt._parm_name;
00171 show_text(prefix, _option_indent, opt._description + "\r");
00172 }
00173 }
00174
00175
00176
00177
00178
00179
00180
00181 void ProgramBase::
00182 show_text(const string &prefix, int indent_width, string text) {
00183 get_terminal_width();
00184
00185
00186
00187
00188 format_text(cerr, _last_newline,
00189 prefix, indent_width, text, _terminal_width);
00190 }
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201 void ProgramBase::
00202 parse_command_line(int argc, char **argv) {
00203 preprocess_argv(argc, argv);
00204
00205
00206
00207
00208 extern int optind;
00209 optind = 0;
00210
00211
00212 _program_name = Filename::from_os_specific(argv[0]);
00213 int i;
00214 for (i = 1; i < argc; i++) {
00215 _program_args.push_back(argv[i]);
00216 }
00217
00218
00219
00220 pvector<struct option> long_options;
00221 string short_options;
00222
00223
00224
00225
00226 typedef pmap<int, const Option *> Options;
00227 Options options;
00228
00229 OptionsByName::const_iterator oi;
00230 int next_index = 256;
00231
00232
00233
00234
00235 short_options = "-";
00236
00237 for (oi = _options_by_name.begin(); oi != _options_by_name.end(); ++oi) {
00238 const Option &opt = (*oi).second;
00239
00240 int index;
00241 if (opt._option.length() == 1) {
00242
00243
00244 index = (int)opt._option[0];
00245
00246 short_options += opt._option;
00247 if (!opt._parm_name.empty()) {
00248
00249 short_options += ':';
00250 }
00251 } else {
00252
00253
00254 index = ++next_index;
00255 }
00256
00257
00258 struct option gopt;
00259 gopt.name = (char *)opt._option.c_str();
00260 gopt.has_arg = (opt._parm_name.empty()) ?
00261 no_argument : required_argument;
00262 gopt.flag = (int *)NULL;
00263
00264
00265
00266 gopt.val = index;
00267
00268 long_options.push_back(gopt);
00269
00270 options[index] = &opt;
00271 }
00272
00273
00274
00275 struct option gopt;
00276 memset(&gopt, 0, sizeof(gopt));
00277 long_options.push_back(gopt);
00278
00279
00280
00281
00282 Args remaining_args;
00283
00284
00285 extern char *optarg;
00286 const struct option *long_opts = &long_options[0];
00287
00288 int flag =
00289 getopt_long_only(argc, argv, short_options.c_str(), long_opts, NULL);
00290 while (flag != EOF) {
00291 string arg;
00292 if (optarg != NULL) {
00293 arg = optarg;
00294 }
00295
00296 switch (flag) {
00297 case '?':
00298
00299 show_usage();
00300 exit(1);
00301
00302 case '\x1':
00303
00304
00305 remaining_args.push_back(arg);
00306 break;
00307
00308 default:
00309 {
00310
00311 Options::const_iterator ii;
00312 ii = options.find(flag);
00313 if (ii == options.end()) {
00314 nout << "Internal error! Invalid option index returned.\n";
00315 abort();
00316 }
00317
00318 const Option &opt = *(*ii).second;
00319 bool okflag = true;
00320 if (opt._option_function != (OptionDispatchFunction)NULL) {
00321 okflag = (*opt._option_function)(opt._option, arg, opt._option_data);
00322 }
00323 if (opt._option_method != (OptionDispatchMethod)NULL) {
00324 okflag = (*opt._option_method)(this, opt._option, arg, opt._option_data);
00325 }
00326 if (opt._bool_var != (bool *)NULL) {
00327 (*opt._bool_var) = true;
00328 }
00329
00330 if (!okflag) {
00331 show_usage();
00332 exit(1);
00333 }
00334 }
00335 }
00336
00337 flag =
00338 getopt_long_only(argc, argv, short_options.c_str(), long_opts, NULL);
00339 }
00340
00341 if (!handle_args(remaining_args)) {
00342 show_usage();
00343 exit(1);
00344 }
00345
00346 if (!post_command_line()) {
00347 show_usage();
00348 exit(1);
00349 }
00350 }
00351
00352
00353
00354
00355
00356
00357
00358
00359 string ProgramBase::
00360 get_exec_command() const {
00361 string command;
00362
00363 command = _program_name.get_basename_wo_extension();
00364 Args::const_iterator ai;
00365 for (ai = _program_args.begin(); ai != _program_args.end(); ++ai) {
00366 const string &arg = (*ai);
00367
00368
00369 bool legal = true;
00370 string::const_iterator si;
00371 for (si = arg.begin(); legal && si != arg.end(); ++si) {
00372 switch (*si) {
00373 case ' ':
00374 case '\n':
00375 case '\t':
00376 case '*':
00377 case '?':
00378 case '\\':
00379 case '(':
00380 case ')':
00381 case '|':
00382 case '&':
00383 case '<':
00384 case '>':
00385 case '"':
00386 case ';':
00387 case '$':
00388 legal = false;
00389 }
00390 }
00391
00392 if (legal) {
00393 command += " " + arg;
00394 } else {
00395 command += " '" + arg + "'";
00396 }
00397 }
00398
00399 return command;
00400 }
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411 bool ProgramBase::
00412 handle_args(ProgramBase::Args &args) {
00413 if (!args.empty()) {
00414 nout << "Unexpected arguments on command line:\n";
00415 Args::const_iterator ai;
00416 for (ai = args.begin(); ai != args.end(); ++ai) {
00417 nout << (*ai) << " ";
00418 }
00419 nout << "\r";
00420 return false;
00421 }
00422
00423 return true;
00424 }
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436 bool ProgramBase::
00437 post_command_line() {
00438 return true;
00439 }
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450 void ProgramBase::
00451 set_program_description(const string &description) {
00452 _description = description;
00453 }
00454
00455
00456
00457
00458
00459
00460
00461 void ProgramBase::
00462 clear_runlines() {
00463 _runlines.clear();
00464 }
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479 void ProgramBase::
00480 add_runline(const string &runline) {
00481 _runlines.push_back(runline);
00482 }
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493 void ProgramBase::
00494 clear_options() {
00495 _options_by_name.clear();
00496 }
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527 void ProgramBase::
00528 add_option(const string &option, const string &parm_name,
00529 int index_group, const string &description,
00530 OptionDispatchFunction option_function,
00531 bool *bool_var, void *option_data) {
00532 Option opt;
00533 opt._option = option;
00534 opt._parm_name = parm_name;
00535 opt._index_group = index_group;
00536 opt._sequence = ++_next_sequence;
00537 opt._description = description;
00538 opt._option_function = option_function;
00539 opt._option_method = (OptionDispatchMethod)NULL;
00540 opt._bool_var = bool_var;
00541 opt._option_data = option_data;
00542
00543 _options_by_name[option] = opt;
00544 _sorted_options = false;
00545
00546 if (bool_var != (bool *)NULL) {
00547 (*bool_var) = false;
00548 }
00549 }
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568 void ProgramBase::
00569 add_option(const string &option, const string &parm_name,
00570 int index_group, const string &description,
00571 OptionDispatchMethod option_method,
00572 bool *bool_var, void *option_data) {
00573 Option opt;
00574 opt._option = option;
00575 opt._parm_name = parm_name;
00576 opt._index_group = index_group;
00577 opt._sequence = ++_next_sequence;
00578 opt._description = description;
00579 opt._option_function = (OptionDispatchFunction)NULL;
00580 opt._option_method = option_method;
00581 opt._bool_var = bool_var;
00582 opt._option_data = option_data;
00583
00584 _options_by_name[option] = opt;
00585 _sorted_options = false;
00586
00587 if (bool_var != (bool *)NULL) {
00588 (*bool_var) = false;
00589 }
00590 }
00591
00592
00593
00594
00595
00596
00597
00598
00599 bool ProgramBase::
00600 redescribe_option(const string &option, const string &description) {
00601 OptionsByName::iterator oi = _options_by_name.find(option);
00602 if (oi == _options_by_name.end()) {
00603 return false;
00604 }
00605 (*oi).second._description = description;
00606 return true;
00607 }
00608
00609
00610
00611
00612
00613
00614
00615 bool ProgramBase::
00616 remove_option(const string &option) {
00617 OptionsByName::iterator oi = _options_by_name.find(option);
00618 if (oi == _options_by_name.end()) {
00619 return false;
00620 }
00621 _options_by_name.erase(oi);
00622 _sorted_options = false;
00623 return true;
00624 }
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634 void ProgramBase::
00635 add_path_replace_options() {
00636 add_option
00637 ("pr", "path_replace", 40,
00638 "Sometimes references to other files (textures, external references) "
00639 "are stored with a full path that is appropriate for some other system, "
00640 "but does not exist here. This option may be used to specify how "
00641 "those invalid paths map to correct paths. Generally, this is of "
00642 "the form 'orig_prefix=replacement_prefix', which indicates a "
00643 "particular initial sequence of characters that should be replaced "
00644 "with a new sequence; e.g. '/c/home/models=/beta/fish'. "
00645 "If the replacement prefix does not begin with a slash, the file "
00646 "will then be searched for along the search path specified by -pp. "
00647 "You may use standard filename matching characters ('*', '?', etc.) in "
00648 "the original prefix, and '**' as a component by itself stands for "
00649 "any number of components.\n\n"
00650
00651 "This option may be repeated as necessary; each file will be tried "
00652 "against each specified method, in the order in which they appear in "
00653 "the command line, until the file is found. If the file is not found, "
00654 "the last matching prefix is used anyway.",
00655 &ProgramBase::dispatch_path_replace, NULL, _path_replace.p());
00656
00657 add_option
00658 ("pp", "dirname", 40,
00659 "Adds the indicated directory name to the list of directories to "
00660 "search for filenames referenced by the source file. This is used "
00661 "only for relative paths, or for paths that are made relative by a "
00662 "-pr replacement string that doesn't begin with a leading slash. "
00663 "The model-path is always implicitly searched anyway.",
00664 &ProgramBase::dispatch_search_path, NULL, &(_path_replace->_path));
00665 }
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675 void ProgramBase::
00676 add_path_store_options() {
00677
00678
00679 _path_replace->_path_store = PS_relative;
00680
00681 add_option
00682 ("ps", "path_store", 40,
00683 "Specifies the way an externally referenced file is to be "
00684 "represented in the resulting output file. This "
00685 "assumes the named filename actually exists; "
00686 "see -pr to indicate how to deal with external "
00687 "references that have bad pathnames. "
00688 "This option will not help you to find a missing file, but simply "
00689 "controls how filenames are represented in the output.\n\n"
00690
00691 "The option may be one of: rel, abs, rel_abs, strip, or keep. If "
00692 "either rel or rel_abs is specified, the files are made relative to "
00693 "the directory specified by -pd. The default is rel.",
00694 &ProgramBase::dispatch_path_store, &_got_path_store,
00695 &(_path_replace->_path_store));
00696
00697 add_option
00698 ("pd", "path_directory", 40,
00699 "Specifies the name of a directory to make paths relative to, if "
00700 "'-ps rel' or '-ps rel_abs' is specified. If this is omitted, the "
00701 "directory name is taken from the name of the output file.",
00702 &ProgramBase::dispatch_filename, &_got_path_directory,
00703 &(_path_replace->_path_directory));
00704
00705 add_option
00706 ("pc", "target_directory", 40,
00707 "Copies textures and other dependent files into the indicated "
00708 "directory. If a relative pathname is specified, it is relative "
00709 "to the directory specified with -pd, above.",
00710 &ProgramBase::dispatch_filename, &(_path_replace->_copy_files),
00711 &(_path_replace->_copy_into_directory));
00712 }
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725 bool ProgramBase::
00726 dispatch_none(const string &, const string &, void *) {
00727 return true;
00728 }
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741 bool ProgramBase::
00742 dispatch_true(const string &, const string &, void *var) {
00743 bool *bp = (bool *)var;
00744 (*bp) = true;
00745 return true;
00746 }
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759 bool ProgramBase::
00760 dispatch_false(const string &, const string &, void *var) {
00761 bool *bp = (bool *)var;
00762 (*bp) = false;
00763 return true;
00764 }
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775 bool ProgramBase::
00776 dispatch_count(const string &, const string &, void *var) {
00777 int *ip = (int *)var;
00778 (*ip)++;
00779
00780 return true;
00781 }
00782
00783
00784
00785
00786
00787
00788
00789
00790 bool ProgramBase::
00791 dispatch_int(const string &opt, const string &arg, void *var) {
00792 int *ip = (int *)var;
00793
00794 if (!string_to_int(arg, *ip)) {
00795 nout << "Invalid integer parameter for -" << opt << ": "
00796 << arg << "\n";
00797 return false;
00798 }
00799
00800 return true;
00801 }
00802
00803
00804
00805
00806
00807
00808
00809
00810 bool ProgramBase::
00811 dispatch_int_pair(const string &opt, const string &arg, void *var) {
00812 int *ip = (int *)var;
00813
00814 vector_string words;
00815 tokenize(arg, words, ",");
00816
00817 bool okflag = false;
00818 if (words.size() == 2) {
00819 okflag =
00820 string_to_int(words[0], ip[0]) &&
00821 string_to_int(words[1], ip[1]);
00822 }
00823
00824 if (!okflag) {
00825 nout << "-" << opt
00826 << " requires a pair of integers separated by a comma.\n";
00827 return false;
00828 }
00829
00830 return true;
00831 }
00832
00833
00834
00835
00836
00837
00838
00839
00840 bool ProgramBase::
00841 dispatch_double(const string &opt, const string &arg, void *var) {
00842 double *ip = (double *)var;
00843
00844 if (!string_to_double(arg, *ip)) {
00845 nout << "Invalid numeric parameter for -" << opt << ": "
00846 << arg << "\n";
00847 return false;
00848 }
00849
00850 return true;
00851 }
00852
00853
00854
00855
00856
00857
00858
00859
00860 bool ProgramBase::
00861 dispatch_double_pair(const string &opt, const string &arg, void *var) {
00862 double *ip = (double *)var;
00863
00864 vector_string words;
00865 tokenize(arg, words, ",");
00866
00867 bool okflag = false;
00868 if (words.size() == 2) {
00869 okflag =
00870 string_to_double(words[0], ip[0]) &&
00871 string_to_double(words[1], ip[1]);
00872 }
00873
00874 if (!okflag) {
00875 nout << "-" << opt
00876 << " requires a pair of numbers separated by a comma.\n";
00877 return false;
00878 }
00879
00880 return true;
00881 }
00882
00883
00884
00885
00886
00887
00888
00889
00890 bool ProgramBase::
00891 dispatch_double_triple(const string &opt, const string &arg, void *var) {
00892 double *ip = (double *)var;
00893
00894 vector_string words;
00895 tokenize(arg, words, ",");
00896
00897 bool okflag = false;
00898 if (words.size() == 3) {
00899 okflag =
00900 string_to_double(words[0], ip[0]) &&
00901 string_to_double(words[1], ip[1]) &&
00902 string_to_double(words[2], ip[2]);
00903 }
00904
00905 if (!okflag) {
00906 nout << "-" << opt
00907 << " requires three numbers separated by commas.\n";
00908 return false;
00909 }
00910
00911 return true;
00912 }
00913
00914
00915
00916
00917
00918
00919
00920
00921 bool ProgramBase::
00922 dispatch_double_quad(const string &opt, const string &arg, void *var) {
00923 double *ip = (double *)var;
00924
00925 vector_string words;
00926 tokenize(arg, words, ",");
00927
00928 bool okflag = false;
00929 if (words.size() == 4) {
00930 okflag =
00931 string_to_double(words[0], ip[0]) &&
00932 string_to_double(words[1], ip[1]) &&
00933 string_to_double(words[2], ip[2]) &&
00934 string_to_double(words[3], ip[3]);
00935 }
00936
00937 if (!okflag) {
00938 nout << "-" << opt
00939 << " requires four numbers separated by commas.\n";
00940 return false;
00941 }
00942
00943 return true;
00944 }
00945
00946
00947
00948
00949
00950
00951
00952
00953 bool ProgramBase::
00954 dispatch_color(const string &opt, const string &arg, void *var) {
00955 PN_stdfloat *ip = (PN_stdfloat *)var;
00956
00957 vector_string words;
00958 tokenize(arg, words, ",");
00959
00960 bool okflag = false;
00961 switch (words.size()) {
00962 case 4:
00963 okflag =
00964 string_to_float(words[0], ip[0]) &&
00965 string_to_float(words[1], ip[1]) &&
00966 string_to_float(words[2], ip[2]) &&
00967 string_to_float(words[3], ip[3]);
00968 break;
00969
00970 case 3:
00971 okflag =
00972 string_to_float(words[0], ip[0]) &&
00973 string_to_float(words[1], ip[1]) &&
00974 string_to_float(words[2], ip[2]);
00975 ip[3] = 1.0;
00976 break;
00977
00978 case 2:
00979 okflag =
00980 string_to_float(words[0], ip[0]) &&
00981 string_to_float(words[1], ip[3]);
00982 ip[1] = ip[0];
00983 ip[2] = ip[0];
00984 break;
00985
00986 case 1:
00987 okflag =
00988 string_to_float(words[0], ip[0]);
00989 ip[1] = ip[0];
00990 ip[2] = ip[0];
00991 ip[3] = 1.0;
00992 break;
00993 }
00994
00995 if (!okflag) {
00996 nout << "-" << opt
00997 << " requires one through four numbers separated by commas.\n";
00998 return false;
00999 }
01000
01001 return true;
01002 }
01003
01004
01005
01006
01007
01008
01009
01010
01011 bool ProgramBase::
01012 dispatch_string(const string &, const string &arg, void *var) {
01013 string *ip = (string *)var;
01014 (*ip) = arg;
01015
01016 return true;
01017 }
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031 bool ProgramBase::
01032 dispatch_vector_string(const string &, const string &arg, void *var) {
01033 vector_string *ip = (vector_string *)var;
01034 (*ip).push_back(arg);
01035
01036 return true;
01037 }
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048 bool ProgramBase::
01049 dispatch_vector_string_comma(const string &, const string &arg, void *var) {
01050 vector_string *ip = (vector_string *)var;
01051
01052 vector_string words;
01053 tokenize(arg, words, ",");
01054
01055 vector_string::const_iterator wi;
01056 for (wi = words.begin(); wi != words.end(); ++wi) {
01057 (*ip).push_back(*wi);
01058 }
01059
01060 return true;
01061 }
01062
01063
01064
01065
01066
01067
01068
01069
01070 bool ProgramBase::
01071 dispatch_filename(const string &opt, const string &arg, void *var) {
01072 if (arg.empty()) {
01073 nout << "-" << opt << " requires a filename parameter.\n";
01074 return false;
01075 }
01076
01077 Filename *ip = (Filename *)var;
01078 (*ip) = Filename::from_os_specific(arg);
01079
01080 return true;
01081 }
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093 bool ProgramBase::
01094 dispatch_search_path(const string &opt, const string &arg, void *var) {
01095 if (arg.empty()) {
01096 nout << "-" << opt << " requires a search path parameter.\n";
01097 return false;
01098 }
01099
01100 DSearchPath *ip = (DSearchPath *)var;
01101 ip->append_directory(Filename::from_os_specific(arg));
01102
01103 return true;
01104 }
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114 bool ProgramBase::
01115 dispatch_coordinate_system(const string &opt, const string &arg, void *var) {
01116 CoordinateSystem *ip = (CoordinateSystem *)var;
01117 (*ip) = parse_coordinate_system_string(arg);
01118
01119 if ((*ip) == CS_invalid) {
01120 nout << "Invalid coordinate system for -" << opt << ": " << arg << "\n"
01121 << "Valid coordinate system strings are any of 'y-up', 'z-up', "
01122 "'y-up-left', or 'z-up-left'.\n";
01123 return false;
01124 }
01125
01126 return true;
01127 }
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137 bool ProgramBase::
01138 dispatch_units(const string &opt, const string &arg, void *var) {
01139 DistanceUnit *ip = (DistanceUnit *)var;
01140 (*ip) = string_distance_unit(arg);
01141
01142 if ((*ip) == DU_invalid) {
01143 nout << "Invalid units for -" << opt << ": " << arg << "\n"
01144 << "Valid units are mm, cm, m, km, yd, ft, in, nmi, and mi.\n";
01145 return false;
01146 }
01147
01148 return true;
01149 }
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159 bool ProgramBase::
01160 dispatch_image_type(const string &opt, const string &arg, void *var) {
01161 PNMFileType **ip = (PNMFileType **)var;
01162
01163 PNMFileTypeRegistry *reg = PNMFileTypeRegistry::get_global_ptr();
01164
01165 (*ip) = reg->get_type_from_extension(arg);
01166
01167 if ((*ip) == (PNMFileType *)NULL) {
01168 nout << "Invalid image type for -" << opt << ": " << arg << "\n"
01169 << "The following image types are known:\n";
01170 reg->write(nout, 2);
01171 return false;
01172 }
01173
01174 return true;
01175 }
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185 bool ProgramBase::
01186 dispatch_path_replace(const string &opt, const string &arg, void *var) {
01187 PathReplace *ip = (PathReplace *)var;
01188 size_t equals = arg.find('=');
01189 if (equals == string::npos) {
01190 nout << "Invalid path replacement string for -" << opt << ": " << arg << "\n"
01191 << "String should be of the form 'old-prefix=new-prefix'.\n";
01192 return false;
01193 }
01194 ip->add_pattern(arg.substr(0, equals), arg.substr(equals + 1));
01195
01196 return true;
01197 }
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207 bool ProgramBase::
01208 dispatch_path_store(const string &opt, const string &arg, void *var) {
01209 PathStore *ip = (PathStore *)var;
01210 (*ip) = string_path_store(arg);
01211
01212 if ((*ip) == PS_invalid) {
01213 nout << "Invalid path store for -" << opt << ": " << arg << "\n"
01214 << "Valid path store strings are any of 'rel', 'abs', "
01215 << "'rel_abs', 'strip', or 'keep'.\n";
01216 return false;
01217 }
01218
01219 return true;
01220 }
01221
01222
01223
01224
01225
01226
01227
01228 bool ProgramBase::
01229 handle_help_option(const string &, const string &, void *data) {
01230 ProgramBase *me = (ProgramBase *)data;
01231 me->show_description();
01232 me->show_usage();
01233 me->show_options();
01234 exit(0);
01235
01236 return false;
01237 }
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263 void ProgramBase::
01264 format_text(ostream &out, bool &last_newline,
01265 const string &prefix, int indent_width,
01266 const string &text, int line_width) {
01267 indent_width = min(indent_width, line_width - 20);
01268 int indent_amount = indent_width;
01269 bool initial_break = false;
01270
01271 if (!prefix.empty()) {
01272 out << prefix;
01273 indent_amount = indent_width - prefix.length();
01274 if ((int)prefix.length() + 1 > indent_width) {
01275 out << "\n";
01276 initial_break = true;
01277 indent_amount = indent_width;
01278 }
01279 }
01280
01281 size_t p = 0;
01282
01283
01284 while (p < text.length() && isspace(text[p])) {
01285 if (text[p] == '\r' ||
01286 (p > 0 && text[p] == '\n' && text[p - 1] == '\n') ||
01287 (p == 0 && text[p] == '\n' && last_newline)) {
01288 if (!initial_break) {
01289
01290 out << "\n";
01291 initial_break = true;
01292 }
01293 indent_amount = indent_width;
01294
01295 } else if (text[p] == '\n') {
01296
01297 indent_amount = indent_width;
01298
01299 } else if (text[p] == ' ') {
01300
01301 indent_amount++;
01302 }
01303 p++;
01304 }
01305
01306 last_newline = (!text.empty() && text[text.length() - 1] == '\n');
01307
01308 while (p < text.length()) {
01309
01310
01311 size_t par = text.find_first_of("\n\r", p);
01312 bool is_paragraph_break = false;
01313 if (par == string::npos) {
01314 par = text.length();
01315
01316
01317
01318
01319
01320 }
01321
01322 indent(out, indent_amount);
01323
01324 size_t eol = p + (line_width - indent_width);
01325 if (eol >= par) {
01326
01327 eol = par;
01328
01329 } else {
01330
01331
01332
01333 size_t min_eol = max((int)p, (int)eol - 25);
01334 size_t q = eol;
01335 while (q > min_eol && !isspace(text[q])) {
01336 q--;
01337 }
01338
01339 while (q > min_eol && isspace(text[q])) {
01340 q--;
01341 }
01342
01343 if (q != min_eol) {
01344
01345 eol = q + 1;
01346
01347 } else {
01348
01349
01350 eol = par;
01351 }
01352 }
01353 out << text.substr(p, eol - p) << "\n";
01354 p = eol;
01355
01356
01357 while (p < text.length() && isspace(text[p])) {
01358 if (text[p] == '\r' ||
01359 (p > 0 && text[p] == '\n' && text[p - 1] == '\n')) {
01360 is_paragraph_break = true;
01361 }
01362 p++;
01363 }
01364
01365 if (eol == par && is_paragraph_break) {
01366
01367 out << "\n";
01368 if (p >= text.length()) {
01369
01370
01371 last_newline = false;
01372 }
01373 }
01374
01375 indent_amount = indent_width;
01376 }
01377 }
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387 void ProgramBase::
01388 sort_options() {
01389 if (!_sorted_options) {
01390 _options_by_index.clear();
01391
01392 OptionsByName::const_iterator oi;
01393 for (oi = _options_by_name.begin(); oi != _options_by_name.end(); ++oi) {
01394 _options_by_index.push_back(&(*oi).second);
01395 }
01396
01397 sort(_options_by_index.begin(), _options_by_index.end(),
01398 SortOptionsByIndex());
01399 _sorted_options = true;
01400 }
01401 }
01402
01403
01404
01405
01406
01407
01408
01409 void ProgramBase::
01410 get_terminal_width() {
01411 if (!_got_terminal_width) {
01412 _got_terminal_width = true;
01413 _got_option_indent = false;
01414
01415 #ifdef IOCTL_TERMINAL_WIDTH
01416 if (use_terminal_width) {
01417 struct winsize size;
01418 int result = ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&size);
01419 if (result < 0 || size.ws_col < 10) {
01420
01421
01422 _terminal_width = default_terminal_width;
01423 } else {
01424
01425
01426 _terminal_width = size.ws_col - min(8, (int)(size.ws_col * 0.1));
01427 }
01428 return;
01429 }
01430 #endif // IOCTL_TERMINAL_WIDTH
01431
01432 _terminal_width = default_terminal_width;
01433 }
01434 }