Panda3D
|
00001 // Filename: programBase.cxx 00002 // Created by: drose (13Feb00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 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 // This manifest is defined if we are running on a system (e.g. most 00036 // any Unix) that allows us to determine the width of the terminal 00037 // screen via an ioctl() call. It's just handy to know for formatting 00038 // output nicely for the user. 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 // This should be called at program termination just to make sure 00057 // Notify gets properly flushed before we exit, if someone calls 00058 // exit(). It's probably not necessary, but why not be phobic about 00059 // it? 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 // Function: ProgramBase::Constructor 00079 // Access: Public 00080 // Description: 00081 //////////////////////////////////////////////////////////////////// 00082 ProgramBase:: 00083 ProgramBase() { 00084 // Set up Notify to write output to our own formatted stream. 00085 Notify::ptr()->set_ostream_ptr(new WordWrapStream(this), true); 00086 00087 // And we'll want to be sure to flush that in all normal exit cases. 00088 atexit(&flush_nout); 00089 00090 _path_replace = new PathReplace; 00091 00092 // If a program never adds the path store options, the default path 00093 // store is PS_absolute. This is the most robust solution for 00094 // programs that read files but do not need to write them. 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 // It's nice to start with a blank line. 00110 nout << "\r"; 00111 } 00112 00113 //////////////////////////////////////////////////////////////////// 00114 // Function: ProgramBase::Destructor 00115 // Access: Public, Virtual 00116 // Description: 00117 //////////////////////////////////////////////////////////////////// 00118 ProgramBase:: 00119 ~ProgramBase() { 00120 // Reset Notify in case any messages get sent after our 00121 // destruction--our stream is no longer valid. 00122 Notify::ptr()->set_ostream_ptr(NULL, false); 00123 } 00124 00125 //////////////////////////////////////////////////////////////////// 00126 // Function: ProgramBase::show_description 00127 // Access: Public 00128 // Description: Writes the program description to stderr. 00129 //////////////////////////////////////////////////////////////////// 00130 void ProgramBase:: 00131 show_description() { 00132 nout << _description << "\n"; 00133 } 00134 00135 //////////////////////////////////////////////////////////////////// 00136 // Function: ProgramBase::show_usage 00137 // Access: Public 00138 // Description: Writes the usage line(s) to stderr. 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 // Function: ProgramBase::show_options 00154 // Access: Public 00155 // Description: Describes each of the available options to stderr. 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 // Function: ProgramBase::show_text 00177 // Access: Public 00178 // Description: Formats the indicated text and its prefix for output 00179 // to stderr with the known _terminal_width. 00180 //////////////////////////////////////////////////////////////////// 00181 void ProgramBase:: 00182 show_text(const string &prefix, int indent_width, string text) { 00183 get_terminal_width(); 00184 00185 // This is correct! It goes go to cerr, not to nout. Sending it to 00186 // nout would be cyclic, since nout is redefined to map back through 00187 // this function. 00188 format_text(cerr, _last_newline, 00189 prefix, indent_width, text, _terminal_width); 00190 } 00191 00192 //////////////////////////////////////////////////////////////////// 00193 // Function: ProgramBase::parse_command_line 00194 // Access: Public, Virtual 00195 // Description: Dispatches on each of the options on the command 00196 // line, and passes the remaining parameters to 00197 // handle_args(). If an error on the command line is 00198 // detected, will automatically call show_usage() and 00199 // exit(1). 00200 //////////////////////////////////////////////////////////////////// 00201 void ProgramBase:: 00202 parse_command_line(int argc, char **argv) { 00203 preprocess_argv(argc, argv); 00204 00205 // Setting this variable to zero reinitializes the options parser 00206 // This is only necessary for processing multiple command lines in 00207 // the same program (mainly the MaxToEgg converter plugin) 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 // Build up the long options list and the short options string for 00219 // getopt_long_only(). 00220 pvector<struct option> long_options; 00221 string short_options; 00222 00223 // We also need to build a temporary map of int index numbers to 00224 // Option pointers. We'll pass these index numbers to GNU's 00225 // getopt_long() so we can tell one option from another. 00226 typedef pmap<int, const Option *> Options; 00227 Options options; 00228 00229 OptionsByName::const_iterator oi; 00230 int next_index = 256; 00231 00232 // Let's prefix the option string with "-" to tell getopt that we 00233 // want it to tell us the post-option arguments, instead of trying 00234 // to meddle with ARGC and ARGV (which we aren't using directly). 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 // This is a "short" option; its option string consists of only 00243 // one letter. Its index is the letter itself. 00244 index = (int)opt._option[0]; 00245 00246 short_options += opt._option; 00247 if (!opt._parm_name.empty()) { 00248 // This option takes an argument. 00249 short_options += ':'; 00250 } 00251 } else { 00252 // This is a "long" option; we'll assign it the next available 00253 // index. 00254 index = ++next_index; 00255 } 00256 00257 // Now add it to the GNU data structures. 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 // Return an index into the _options_by_index array, offset by 256 00265 // so we don't confuse it with '?'. 00266 gopt.val = index; 00267 00268 long_options.push_back(gopt); 00269 00270 options[index] = &opt; 00271 } 00272 00273 // Finally, add one more structure, all zeroes, to indicate the end 00274 // of the options. 00275 struct option gopt; 00276 memset(&gopt, 0, sizeof(gopt)); 00277 long_options.push_back(gopt); 00278 00279 // We'll use this vector to save the non-option arguments. 00280 // Generally, these will all be at the end, but with the GNU 00281 // extensions, they need not be. 00282 Args remaining_args; 00283 00284 // Now call getopt_long() to actually parse the arguments. 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 // Invalid option or parameter. 00299 show_usage(); 00300 exit(1); 00301 00302 case '\x1': 00303 // A special return value from getopt() indicating a non-option 00304 // argument. 00305 remaining_args.push_back(arg); 00306 break; 00307 00308 default: 00309 { 00310 // A normal option. Figure out which one it is. 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 // Function: ProgramBase::get_exec_command 00354 // Access: Public 00355 // Description: Returns the command that invoked this program, as a 00356 // shell-friendly string, suitable for pasting into the 00357 // comments of output files. 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 // First, check to see if the string is shell-acceptable. 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 // Function: ProgramBase::handle_args 00405 // Access: Protected, Virtual 00406 // Description: Does something with the additional arguments on the 00407 // command line (after all the -options have been 00408 // parsed). Returns true if the arguments are good, 00409 // false otherwise. 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 // Function: ProgramBase::post_command_line 00428 // Access: Protected, Virtual 00429 // Description: This is called after the command line has been 00430 // completely processed, and it gives the program a 00431 // chance to do some last-minute processing and 00432 // validation of the options and arguments. It should 00433 // return true if everything is fine, false if there is 00434 // an error. 00435 //////////////////////////////////////////////////////////////////// 00436 bool ProgramBase:: 00437 post_command_line() { 00438 return true; 00439 } 00440 00441 //////////////////////////////////////////////////////////////////// 00442 // Function: ProgramBase::set_program_description 00443 // Access: Protected 00444 // Description: Sets the description of the program that will be 00445 // reported by show_usage(). The description should be 00446 // one long string of text. Embedded newline characters 00447 // are interpreted as paragraph breaks and printed as 00448 // blank lines. 00449 //////////////////////////////////////////////////////////////////// 00450 void ProgramBase:: 00451 set_program_description(const string &description) { 00452 _description = description; 00453 } 00454 00455 //////////////////////////////////////////////////////////////////// 00456 // Function: ProgramBase::clear_runlines 00457 // Access: Protected 00458 // Description: Removes all of the runlines that were previously 00459 // added, presumably before adding some new ones. 00460 //////////////////////////////////////////////////////////////////// 00461 void ProgramBase:: 00462 clear_runlines() { 00463 _runlines.clear(); 00464 } 00465 00466 //////////////////////////////////////////////////////////////////// 00467 // Function: ProgramBase::add_runline 00468 // Access: Protected 00469 // Description: Adds an additional line to the list of lines that 00470 // will be displayed to describe briefly how the program 00471 // is to be run. Each line should be something like 00472 // "[opts] arg1 arg2", that is, it does *not* include 00473 // the name of the program, but it includes everything 00474 // that should be printed after the name of the program. 00475 // 00476 // Normally there is only one runline for a given 00477 // program, but it is possible to define more than one. 00478 //////////////////////////////////////////////////////////////////// 00479 void ProgramBase:: 00480 add_runline(const string &runline) { 00481 _runlines.push_back(runline); 00482 } 00483 00484 //////////////////////////////////////////////////////////////////// 00485 // Function: ProgramBase::clear_options 00486 // Access: Protected 00487 // Description: Removes all of the options that were previously 00488 // added, presumably before adding some new ones. 00489 // Normally you wouldn't want to do this unless you want 00490 // to completely replace all of the options defined by 00491 // base classes. 00492 //////////////////////////////////////////////////////////////////// 00493 void ProgramBase:: 00494 clear_options() { 00495 _options_by_name.clear(); 00496 } 00497 00498 //////////////////////////////////////////////////////////////////// 00499 // Function: ProgramBase::add_option 00500 // Access: Protected 00501 // Description: Adds (or redefines) a command line option. When 00502 // parse_command_line() is executed it will look for 00503 // these options (followed by a hyphen) on the command 00504 // line; when a particular option is found it will call 00505 // the indicated option_function, supplying the provided 00506 // option_data. This allows the user to define a 00507 // function that does some special behavior for any 00508 // given option, or to use any of a number of generic 00509 // pre-defined functions to fill in data for each 00510 // option. 00511 // 00512 // Each option may or may not take a parameter. If 00513 // parm_name is nonempty, it is assumed that the option 00514 // does take a parameter (and parm_name contains the 00515 // name that will be printed by show_options()). This 00516 // parameter will be supplied as the second parameter to 00517 // the dispatch function. If parm_name is empty, it is 00518 // assumed that the option does not take a parameter. 00519 // There is no provision for optional parameters. 00520 // 00521 // The options are listed first in order by their 00522 // index_group number, and then in the order that 00523 // add_option() was called. This provides a mechanism 00524 // for listing the options defined in derived classes 00525 // before those of the base classes. 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 // Function: ProgramBase::add_option 00553 // Access: Protected 00554 // Description: This is another variant on add_option(), above, 00555 // except that it receives a pointer to a "method", 00556 // which is really just another static (or global) 00557 // function, whose first parameter is a ProgramBase *. 00558 // 00559 // We can't easily add a variant that accepts a real 00560 // method, because the C++ syntax for methods requires 00561 // us to know exactly what class object the method is 00562 // defined for, and we want to support adding pointers 00563 // for methods that are defined in other classes. So we 00564 // have this hacky thing, which requires the "method" to 00565 // be declared static, and receive its this pointer 00566 // explicitly, as the first argument. 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 // Function: ProgramBase::redescribe_option 00594 // Access: Protected 00595 // Description: Changes the description associated with a 00596 // previously-defined option. Returns true if the 00597 // option was changed, false if it hadn't been defined. 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 // Function: ProgramBase::remove_option 00611 // Access: Protected 00612 // Description: Removes a previously-defined option. Returns true if 00613 // the option was removed, false if it hadn't existed. 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 // Function: ProgramBase::add_path_replace_options 00628 // Access: Public 00629 // Description: Adds -pr etc. as valid options for this program. 00630 // These are appropriate for a model converter or model 00631 // reader type program, and specify how to locate 00632 // possibly-invalid pathnames in the source model file. 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 // Function: ProgramBase::add_path_store_options 00669 // Access: Public 00670 // Description: Adds -ps etc. as valid options for this program. 00671 // These are appropriate for a model converter type 00672 // program, and specify how to represent filenames in 00673 // the output file. 00674 //////////////////////////////////////////////////////////////////// 00675 void ProgramBase:: 00676 add_path_store_options() { 00677 // If a program has path store options at all, the default path 00678 // store is relative. 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 // Function: ProgramBase::dispatch_none 00716 // Access: Protected, Static 00717 // Description: Standard dispatch function for an option that takes 00718 // no parameters, and does nothing special. Typically 00719 // this would be used for a boolean flag, whose presence 00720 // means something and whose absence means something 00721 // else. Use the bool_var parameter to add_option() to 00722 // determine whether the option appears on the command 00723 // line or not. 00724 //////////////////////////////////////////////////////////////////// 00725 bool ProgramBase:: 00726 dispatch_none(const string &, const string &, void *) { 00727 return true; 00728 } 00729 00730 //////////////////////////////////////////////////////////////////// 00731 // Function: ProgramBase::dispatch_true 00732 // Access: Protected, Static 00733 // Description: Standard dispatch function for an option that takes 00734 // no parameters, and when it is present sets a bool 00735 // variable to the 'true' value. This is another way to 00736 // handle a boolean flag. See also dispatch_none() and 00737 // dispatch_false(). 00738 // 00739 // The data pointer is to a bool variable. 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 // Function: ProgramBase::dispatch_false 00750 // Access: Protected, Static 00751 // Description: Standard dispatch function for an option that takes 00752 // no parameters, and when it is present sets a bool 00753 // variable to the 'false' value. This is another way to 00754 // handle a boolean flag. See also dispatch_none() and 00755 // dispatch_true(). 00756 // 00757 // The data pointer is to a bool variable. 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 // Function: ProgramBase::dispatch_count 00768 // Access: Protected, Static 00769 // Description: Standard dispatch function for an option that takes 00770 // no parameters, but whose presence on the command line 00771 // increments an integer counter for each time it 00772 // appears. -v is often an option that works this way. 00773 // The data pointer is to an int counter variable. 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 // Function: ProgramBase::dispatch_int 00785 // Access: Protected, Static 00786 // Description: Standard dispatch function for an option that takes 00787 // one parameter, which is to be interpreted as an 00788 // integer. The data pointer is to an int variable. 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 // Function: ProgramBase::dispatch_int_pair 00805 // Access: Protected, Static 00806 // Description: Standard dispatch function for an option that takes 00807 // a pair of integer parameters. The data pointer is to 00808 // an array of two integers. 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 // Function: ProgramBase::dispatch_double 00835 // Access: Protected, Static 00836 // Description: Standard dispatch function for an option that takes 00837 // one parameter, which is to be interpreted as a 00838 // double. The data pointer is to an double variable. 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 // Function: ProgramBase::dispatch_double_pair 00855 // Access: Protected, Static 00856 // Description: Standard dispatch function for an option that takes 00857 // a pair of double parameters. The data pointer is to 00858 // an array of two doubles. 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 // Function: ProgramBase::dispatch_double_triple 00885 // Access: Protected, Static 00886 // Description: Standard dispatch function for an option that takes 00887 // a triple of double parameters. The data pointer is to 00888 // an array of three doubles. 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 // Function: ProgramBase::dispatch_double_quad 00916 // Access: Protected, Static 00917 // Description: Standard dispatch function for an option that takes 00918 // a quad of double parameters. The data pointer is to 00919 // an array of four doubles. 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 // Function: ProgramBase::dispatch_color 00948 // Access: Protected, Static 00949 // Description: Standard dispatch function for an option that takes a 00950 // color, as l or l,a or r,g,b or r,g,b,a. The data 00951 // pointer is to an array of four floats, e.g. a LColor. 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 // Function: ProgramBase::dispatch_string 01006 // Access: Protected, Static 01007 // Description: Standard dispatch function for an option that takes 01008 // one parameter, which is to be interpreted as a 01009 // string. The data pointer is to a string variable. 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 // Function: ProgramBase::dispatch_vector_string 01021 // Access: Protected, Static 01022 // Description: Standard dispatch function for an option that takes 01023 // one parameter, which is to be interpreted as a 01024 // string. This is different from dispatch_string in 01025 // that the parameter may be repeated multiple times, 01026 // and each time the string value is appended to a 01027 // vector. 01028 // 01029 // The data pointer is to a vector_string variable. 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 // Function: ProgramBase::dispatch_vector_string_comma 01041 // Access: Protected, Static 01042 // Description: Similar to dispatch_vector_string, but a comma is 01043 // allowed to separate multiple tokens in one argument, 01044 // without having to repeat the argument for each token. 01045 // 01046 // The data pointer is to a vector_string variable. 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 // Function: ProgramBase::dispatch_filename 01065 // Access: Protected, Static 01066 // Description: Standard dispatch function for an option that takes 01067 // one parameter, which is to be interpreted as a 01068 // filename. The data pointer is to a Filename variable. 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 // Function: ProgramBase::dispatch_search_path 01085 // Access: Protected, Static 01086 // Description: Standard dispatch function for an option that takes 01087 // one parameter, which is to be interpreted as a 01088 // single directory name to add to a search path. The 01089 // data pointer is to a DSearchPath variable. This kind 01090 // of option may appear multiple times on the command 01091 // line; each time, the new directory is appended. 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 // Function: ProgramBase::dispatch_coordinate_system 01108 // Access: Protected, Static 01109 // Description: Standard dispatch function for an option that takes 01110 // one parameter, which is to be interpreted as a 01111 // coordinate system string. The data pointer is to a 01112 // CoordinateSystem variable. 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 // Function: ProgramBase::dispatch_units 01131 // Access: Protected, Static 01132 // Description: Standard dispatch function for an option that takes 01133 // one parameter, which is to be interpreted as a 01134 // unit of distance measurement. The data pointer is to 01135 // a DistanceUnit variable. 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 // Function: ProgramBase::dispatch_image_type 01153 // Access: Protected, Static 01154 // Description: Standard dispatch function for an option that takes 01155 // one parameter, which is to indicate an image file 01156 // type, like rgb, bmp, jpg, etc. The data pointer is 01157 // to a PNMFileType pointer. 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 // Function: ProgramBase::dispatch_path_replace 01179 // Access: Protected, Static 01180 // Description: Standard dispatch function for an option that takes 01181 // one parameter, which is to be interpreted as a 01182 // single component of a path replace request. The data 01183 // pointer is to a PathReplace variable. 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 // Function: ProgramBase::dispatch_path_store 01201 // Access: Protected, Static 01202 // Description: Standard dispatch function for an option that takes 01203 // one parameter, which is to be interpreted as a 01204 // path store string. The data pointer is to a 01205 // PathStore variable. 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 // Function: ProgramBase::handle_help_option 01224 // Access: Protected, Static 01225 // Description: Called when the user enters '-h', this describes how 01226 // to use the program and then exits. 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 // Function: ProgramBase::format_text 01242 // Access: Protected, Static 01243 // Description: Word-wraps the indicated text to the indicated output 01244 // stream. The first line is prefixed with the 01245 // indicated prefix, then tabbed over to indent_width 01246 // where the text actually begins. A newline is 01247 // inserted at or before column line_width. Each 01248 // subsequent line begins with indent_width spaces. 01249 // 01250 // An embedded newline character ('\n') forces a line 01251 // break, while an embedded carriage-return character 01252 // ('\r'), or two or more consecutive newlines, marks a 01253 // paragraph break, which is usually printed as a blank 01254 // line. Redundant newline and carriage-return 01255 // characters are generally ignored. 01256 // 01257 // The flag last_newline should be initialized to false 01258 // for the first call to format_text, and then preserved 01259 // for future calls; it tracks the state of trailing 01260 // newline characters between calls so we can correctly 01261 // identify doubled newlines. 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 // Skip any initial whitespace and newlines. 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 // Here's an initial paragraph break, however. 01290 out << "\n"; 01291 initial_break = true; 01292 } 01293 indent_amount = indent_width; 01294 01295 } else if (text[p] == '\n') { 01296 // Largely ignore an initial newline. 01297 indent_amount = indent_width; 01298 01299 } else if (text[p] == ' ') { 01300 // Do count up leading spaces. 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 // Look for the paragraph or line break--the next newline 01310 // character, if any. 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 This shouldn't be necessary. 01317 } else { 01318 is_paragraph_break = (text[par] == '\r'); 01319 */ 01320 } 01321 01322 indent(out, indent_amount); 01323 01324 size_t eol = p + (line_width - indent_width); 01325 if (eol >= par) { 01326 // The rest of the paragraph fits completely on the line. 01327 eol = par; 01328 01329 } else { 01330 // The paragraph doesn't fit completely on the line. Determine 01331 // the best place to break the line. Look for the last space 01332 // before the ideal eol. 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 // Now roll back to the last non-space before this one. 01339 while (q > min_eol && isspace(text[q])) { 01340 q--; 01341 } 01342 01343 if (q != min_eol) { 01344 // Here's a good place to stop! 01345 eol = q + 1; 01346 01347 } else { 01348 // The line cannot be broken cleanly. Just let it keep going; 01349 // don't try to wrap it. 01350 eol = par; 01351 } 01352 } 01353 out << text.substr(p, eol - p) << "\n"; 01354 p = eol; 01355 01356 // Skip additional whitespace between the lines. 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 // Print the paragraph break as a blank line. 01367 out << "\n"; 01368 if (p >= text.length()) { 01369 // If we end on a paragraph break, don't try to insert a new 01370 // one in the next pass. 01371 last_newline = false; 01372 } 01373 } 01374 01375 indent_amount = indent_width; 01376 } 01377 } 01378 01379 01380 //////////////////////////////////////////////////////////////////// 01381 // Function: ProgramBase::sort_options 01382 // Access: Private 01383 // Description: Puts all the options in order by index number 01384 // (e.g. in the order they were added, within 01385 // index_groups), for output by show_options(). 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 // Function: ProgramBase::get_terminal_width 01405 // Access: Private 01406 // Description: Attempts to determine the ideal terminal width for 01407 // formatting output. 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 // Couldn't determine the width for some reason. Instead of 01421 // complaining, just punt. 01422 _terminal_width = default_terminal_width; 01423 } else { 01424 01425 // Subtract 10% for the comfort margin at the edge. 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 }