Panda3D
programBase.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file programBase.cxx
10  * @author drose
11  * @date 2000-02-13
12  */
13 
14 #include "programBase.h"
15 #include "wordWrapStream.h"
16 
17 #include "pnmFileTypeRegistry.h"
18 #include "indent.h"
19 #include "dSearchPath.h"
20 #include "coordinateSystem.h"
21 #include "dconfig.h"
22 #include "string_utils.h"
23 #include "vector_string.h"
24 #include "configVariableInt.h"
25 #include "configVariableBool.h"
26 #include "panda_getopt_long.h"
27 #include "preprocess_argv.h"
28 #include "pandaSystem.h"
29 
30 #include <stdlib.h>
31 #include <algorithm>
32 #include <ctype.h>
33 
34 // This manifest is defined if we are running on a system (e.g. most any
35 // Unix) that allows us to determine the width of the terminal screen via an
36 // ioctl() call. It's just handy to know for formatting output nicely for the
37 // user.
38 #ifdef IOCTL_TERMINAL_WIDTH
39  #include <termios.h>
40  #ifndef TIOCGWINSZ
41  #include <sys/ioctl.h>
42  #elif __APPLE__
43  #include <sys/ioctl.h>
44  #endif // TIOCGWINSZ
45 #endif // IOCTL_TERMINAL_WIDTH
46 
47 using std::cerr;
48 using std::cout;
49 using std::max;
50 using std::min;
51 using std::string;
52 
53 bool ProgramBase::SortOptionsByIndex::
54 operator () (const Option *a, const Option *b) const {
55  if (a->_index_group != b->_index_group) {
56  return a->_index_group < b->_index_group;
57  }
58  return a->_sequence < b->_sequence;
59 }
60 
61 // This should be called at program termination just to make sure Notify gets
62 // properly flushed before we exit, if someone calls exit(). It's probably
63 // not necessary, but why not be phobic about it?
64 static void flush_nout() {
65  nout << std::flush;
66 }
67 
68 static ConfigVariableInt default_terminal_width
69 ("default-terminal-width", 72,
70  PRC_DESC("Specify the column at which to wrap output lines "
71  "from pandatool-based programs, if it cannot be determined "
72  "automatically."));
73 
74 static ConfigVariableBool use_terminal_width
75 ("use-terminal-width", true,
76  PRC_DESC("True to try to determine the terminal width automatically from "
77  "the operating system, if supported; false to use the width "
78  "specified by default-terminal-width even if the operating system "
79  "appears to report a valid width."));
80 
81 /**
82  *
83  */
84 ProgramBase::
85 ProgramBase(const string &name) : _name(name) {
86  // Set up Notify to write output to our own formatted stream.
87  Notify::ptr()->set_ostream_ptr(new WordWrapStream(this), true);
88 
89  // And we'll want to be sure to flush that in all normal exit cases.
90  atexit(&flush_nout);
91 
92  _path_replace = new PathReplace;
93 
94  // If a program never adds the path store options, the default path store is
95  // PS_absolute. This is the most robust solution for programs that read
96  // files but do not need to write them.
97  _path_replace->_path_store = PS_absolute;
98  _got_path_store = false;
99  _got_path_directory = false;
100 
101  _next_sequence = 0;
102  _sorted_options = false;
103  _last_newline = false;
104  _got_terminal_width = false;
105  _got_option_indent = false;
106 
107  add_option("h", "", 100,
108  "Display this help page.",
109  &ProgramBase::handle_help_option, nullptr, (void *)this);
110 
111  // It's nice to start with a blank line.
112  nout << "\r";
113 }
114 
115 /**
116  *
117  */
118 ProgramBase::
119 ~ProgramBase() {
120  // Reset Notify in case any messages get sent after our destruction--our
121  // stream is no longer valid.
122  Notify::ptr()->set_ostream_ptr(nullptr, false);
123 }
124 
125 /**
126  * Writes the program description to stderr.
127  */
128 void ProgramBase::
130  nout << _description << "\n";
131 }
132 
133 /**
134  * Writes the usage line(s) to stderr.
135  */
136 void ProgramBase::
138  nout << "\rUsage:\n";
139  Runlines::const_iterator ri;
140  string prog = " " + _program_name.get_basename_wo_extension();
141 
142  for (ri = _runlines.begin(); ri != _runlines.end(); ++ri) {
143  show_text(prog, prog.length() + 1, *ri);
144  }
145  nout << "\r";
146 }
147 
148 /**
149  * Describes each of the available options to stderr.
150  */
151 void ProgramBase::
153  sort_options();
154  if (!_got_option_indent) {
155  get_terminal_width();
156  _option_indent = min(15, (int)(_terminal_width * 0.25));
157  _got_option_indent = true;
158  }
159 
160  nout << "Options:\n";
161  OptionsByIndex::const_iterator oi;
162  for (oi = _options_by_index.begin(); oi != _options_by_index.end(); ++oi) {
163  const Option &opt = *(*oi);
164  string prefix = " -" + opt._option + " " + opt._parm_name;
165  show_text(prefix, _option_indent, opt._description + "\r");
166  }
167 }
168 
169 /**
170  * Formats the indicated text and its prefix for output to stderr with the
171  * known _terminal_width.
172  */
173 void ProgramBase::
174 show_text(const string &prefix, int indent_width, string text) {
175  get_terminal_width();
176 
177  // This is correct! It goes go to cerr, not to nout. Sending it to nout
178  // would be cyclic, since nout is redefined to map back through this
179  // function.
180  format_text(cerr, _last_newline,
181  prefix, indent_width, text, _terminal_width);
182 }
183 
184 /**
185  * Generates a man page in nroff syntax based on the description and options.
186  * This is useful when creating a man page for this utility.
187  */
188 void ProgramBase::
189 write_man_page(std::ostream &out) {
190  string prog = _program_name.get_basename_wo_extension();
191  out << ".\\\" Automatically generated by " << prog << " -write-man\n";
192 
193  // Format the man page title as the uppercase version of the program name,
194  // as per the UNIX manual conventions.
195  out << ".TH ";
196  string::const_iterator si;
197  for (si = _name.begin(); si != _name.end(); ++si) {
198  out << (char)toupper(*si);
199  }
200 
201  // Generate a date string for inclusion into the footer.
202  char date_str[256];
203  date_str[0] = 0;
204  time_t current_time = time(nullptr);
205 
206  if (current_time != (time_t) -1) {
207  tm *today = localtime(&current_time);
208  if (today == nullptr || 0 == strftime(date_str, 256, "%d %B %Y", today)) {
209  date_str[0] = 0;
210  }
211  }
212 
213  out << " 1 \"" << date_str << "\" \""
214  << PandaSystem::get_version_string() << "\" Panda3D\n";
215 
216  out << ".SH NAME\n";
217  if (_brief.empty()) {
218  out << _name << "\n";
219  } else {
220  out << _name << " \\- " << _brief << "\n";
221  }
222 
223  out << ".SH SYNOPSIS\n";
224  Runlines::const_iterator ri = _runlines.begin();
225  if (ri != _runlines.end()) {
226  out << "\\fB" << prog << "\\fR " << *ri << "\n";
227  ++ri;
228  }
229 
230  for (; ri != _runlines.end(); ++ri) {
231  out << ".br\n";
232  out << "\\fB" << prog << "\\fR " << *ri << "\n";
233  }
234 
235  out << ".SH DESCRIPTION\n";
236  string::const_iterator di;
237  char prev = 0;
238  for (di = _description.begin(); di != _description.end(); ++di) {
239  if ((*di) == '-') {
240  // We have to escape hyphens.
241  out << "\\-";
242  } else if (prev == '\n' && (*di) == '\n') {
243  // Indicate the start of a new paragraph.
244  out << ".PP\n";
245  } else {
246  out << (char)(*di);
247  }
248  prev = (*di);
249  }
250  out << "\n";
251 
252  out << ".SH OPTIONS\n";
253  sort_options();
254  OptionsByIndex::const_iterator oi;
255  for (oi = _options_by_index.begin(); oi != _options_by_index.end(); ++oi) {
256  const Option &opt = *(*oi);
257  out << ".TP\n";
258 
259  if (opt._parm_name.empty()) {
260  out << ".B \\-" << opt._option << "\n";
261  } else {
262  out << ".BI \"\\-" << opt._option << " \" \"" << opt._parm_name << "\"\n";
263  }
264  out << opt._description << "\n";
265  }
266 }
267 
268 /**
269  * Dispatches on each of the options on the command line, and passes the
270  * remaining parameters to handle_args(). If an error on the command line is
271  * detected, will automatically call show_usage() and exit(1).
272  */
273 void ProgramBase::
274 parse_command_line(int argc, char **argv) {
275  preprocess_argv(argc, argv);
276 
277  // Setting this variable to zero reinitializes the options parser This is
278  // only necessary for processing multiple command lines in the same program
279  // (mainly the MaxToEgg converter plugin)
280  extern int optind;
281  optind = 0;
282 
283  _program_name = Filename::from_os_specific(argv[0]);
284  int i;
285  for (i = 1; i < argc; i++) {
286  _program_args.push_back(argv[i]);
287  }
288 
289  if (_name.empty()) {
290  _name = _program_name.get_basename_wo_extension();
291  }
292 
293  // Catch a special hidden option: -write-man, which causes the tool to
294  // generate a manual page.
295  if (argc > 1 && strcmp(argv[1], "-write-man") == 0) {
296  if (argc == 2) {
297  write_man_page(cout);
298 
299  } else if (argc == 3) {
300  if (strlen(argv[2]) == 1 && argv[2][0] == '-') {
301  write_man_page(cout);
302 
303  } else {
304  pofstream man_out(argv[2], std::ios::out | std::ios::trunc);
305  if (!man_out) {
306  cerr << "Failed to open output file " << argv[2] << "!\n";
307  }
308  write_man_page(man_out);
309  man_out.close();
310  }
311  } else {
312  cerr << "Invalid number of options for -write-man!\n";
313  exit(1);
314  }
315  exit(0);
316  }
317 
318  // Build up the long options list and the short options string for
319  // getopt_long_only().
320  pvector<struct option> long_options;
321  string short_options;
322 
323  // We also need to build a temporary map of int index numbers to Option
324  // pointers. We'll pass these index numbers to GNU's getopt_long() so we
325  // can tell one option from another.
326  typedef pmap<int, const Option *> Options;
327  Options options;
328 
329  OptionsByName::const_iterator oi;
330  int next_index = 256;
331 
332  // Let's prefix the option string with "-" to tell getopt that we want it to
333  // tell us the post-option arguments, instead of trying to meddle with ARGC
334  // and ARGV (which we aren't using directly).
335  short_options = "-";
336 
337  for (oi = _options_by_name.begin(); oi != _options_by_name.end(); ++oi) {
338  const Option &opt = (*oi).second;
339 
340  int index;
341  if (opt._option.length() == 1) {
342  // This is a "short" option; its option string consists of only one
343  // letter. Its index is the letter itself.
344  index = (int)opt._option[0];
345 
346  short_options += opt._option;
347  if (!opt._parm_name.empty()) {
348  // This option takes an argument.
349  short_options += ':';
350  }
351  } else {
352  // This is a "long" option; we'll assign it the next available index.
353  index = ++next_index;
354  }
355 
356  // Now add it to the GNU data structures.
357  struct option gopt;
358  gopt.name = (char *)opt._option.c_str();
359  gopt.has_arg = (opt._parm_name.empty()) ?
360  no_argument : required_argument;
361  gopt.flag = nullptr;
362 
363  // Return an index into the _options_by_index array, offset by 256 so we
364  // don't confuse it with '?'.
365  gopt.val = index;
366 
367  long_options.push_back(gopt);
368 
369  options[index] = &opt;
370  }
371 
372  // Finally, add one more structure, all zeroes, to indicate the end of the
373  // options.
374  struct option gopt;
375  memset(&gopt, 0, sizeof(gopt));
376  long_options.push_back(gopt);
377 
378  // We'll use this vector to save the non-option arguments. Generally, these
379  // will all be at the end, but with the GNU extensions, they need not be.
380  Args remaining_args;
381 
382  // Now call getopt_long() to actually parse the arguments.
383  extern char *optarg;
384  const struct option *long_opts = &long_options[0];
385 
386  int flag =
387  getopt_long_only(argc, argv, short_options.c_str(), long_opts, nullptr);
388  while (flag != EOF) {
389  string arg;
390  if (optarg != nullptr) {
391  arg = optarg;
392  }
393 
394  switch (flag) {
395  case '?':
396  // Invalid option or parameter.
397  show_usage();
398  exit(1);
399 
400  case '\x1':
401  // A special return value from getopt() indicating a non-option
402  // argument.
403  remaining_args.push_back(arg);
404  break;
405 
406  default:
407  {
408  // A normal option. Figure out which one it is.
409  Options::const_iterator ii;
410  ii = options.find(flag);
411  if (ii == options.end()) {
412  nout << "Internal error! Invalid option index returned.\n";
413  abort();
414  }
415 
416  const Option &opt = *(*ii).second;
417  bool okflag = true;
418  if (opt._option_function != (OptionDispatchFunction)nullptr) {
419  okflag = (*opt._option_function)(opt._option, arg, opt._option_data);
420  }
421  if (opt._option_method != (OptionDispatchMethod)nullptr) {
422  okflag = (*opt._option_method)(this, opt._option, arg, opt._option_data);
423  }
424  if (opt._bool_var != nullptr) {
425  (*opt._bool_var) = true;
426  }
427 
428  if (!okflag) {
429  show_usage();
430  exit(1);
431  }
432  }
433  }
434 
435  flag =
436  getopt_long_only(argc, argv, short_options.c_str(), long_opts, nullptr);
437  }
438 
439  if (!handle_args(remaining_args)) {
440  show_usage();
441  exit(1);
442  }
443 
444  if (!post_command_line()) {
445  show_usage();
446  exit(1);
447  }
448 }
449 
450 /**
451  * Returns the command that invoked this program, as a shell-friendly string,
452  * suitable for pasting into the comments of output files.
453  */
454 string ProgramBase::
456  string command;
457 
458  command = _program_name.get_basename_wo_extension();
459  Args::const_iterator ai;
460  for (ai = _program_args.begin(); ai != _program_args.end(); ++ai) {
461  const string &arg = (*ai);
462 
463  // First, check to see if the string is shell-acceptable.
464  bool legal = true;
465  string::const_iterator si;
466  for (si = arg.begin(); legal && si != arg.end(); ++si) {
467  switch (*si) {
468  case ' ':
469  case '\n':
470  case '\t':
471  case '*':
472  case '?':
473  case '\\':
474  case '(':
475  case ')':
476  case '|':
477  case '&':
478  case '<':
479  case '>':
480  case '"':
481  case ';':
482  case '$':
483  legal = false;
484  }
485  }
486 
487  if (legal) {
488  command += " " + arg;
489  } else {
490  command += " '" + arg + "'";
491  }
492  }
493 
494  return command;
495 }
496 
497 
498 /**
499  * Does something with the additional arguments on the command line (after all
500  * the -options have been parsed). Returns true if the arguments are good,
501  * false otherwise.
502  */
503 bool ProgramBase::
504 handle_args(ProgramBase::Args &args) {
505  if (!args.empty()) {
506  nout << "Unexpected arguments on command line:\n";
507  Args::const_iterator ai;
508  for (ai = args.begin(); ai != args.end(); ++ai) {
509  nout << (*ai) << " ";
510  }
511  nout << "\r";
512  return false;
513  }
514 
515  return true;
516 }
517 
518 /**
519  * This is called after the command line has been completely processed, and it
520  * gives the program a chance to do some last-minute processing and validation
521  * of the options and arguments. It should return true if everything is fine,
522  * false if there is an error.
523  */
524 bool ProgramBase::
525 post_command_line() {
526  return true;
527 }
528 
529 /**
530  * Sets a brief synopsis of the program's function. This is currently only
531  * used for generating the synopsis of the program's man page.
532  *
533  * This should be of the format: "perform operation foo on bar files"
534  */
535 void ProgramBase::
536 set_program_brief(const string &brief) {
537  _brief = brief;
538 }
539 
540 /**
541  * Sets the description of the program that will be reported by show_usage().
542  * The description should be one long string of text. Embedded newline
543  * characters are interpreted as paragraph breaks and printed as blank lines.
544  */
545 void ProgramBase::
546 set_program_description(const string &description) {
547  _description = description;
548 }
549 
550 /**
551  * Removes all of the runlines that were previously added, presumably before
552  * adding some new ones.
553  */
554 void ProgramBase::
555 clear_runlines() {
556  _runlines.clear();
557 }
558 
559 /**
560  * Adds an additional line to the list of lines that will be displayed to
561  * describe briefly how the program is to be run. Each line should be
562  * something like "[opts] arg1 arg2", that is, it does *not* include the name
563  * of the program, but it includes everything that should be printed after the
564  * name of the program.
565  *
566  * Normally there is only one runline for a given program, but it is possible
567  * to define more than one.
568  */
569 void ProgramBase::
570 add_runline(const string &runline) {
571  _runlines.push_back(runline);
572 }
573 
574 /**
575  * Removes all of the options that were previously added, presumably before
576  * adding some new ones. Normally you wouldn't want to do this unless you
577  * want to completely replace all of the options defined by base classes.
578  */
579 void ProgramBase::
580 clear_options() {
581  _options_by_name.clear();
582 }
583 
584 /**
585  * Adds (or redefines) a command line option. When parse_command_line() is
586  * executed it will look for these options (followed by a hyphen) on the
587  * command line; when a particular option is found it will call the indicated
588  * option_function, supplying the provided option_data. This allows the user
589  * to define a function that does some special behavior for any given option,
590  * or to use any of a number of generic pre-defined functions to fill in data
591  * for each option.
592  *
593  * Each option may or may not take a parameter. If parm_name is nonempty, it
594  * is assumed that the option does take a parameter (and parm_name contains
595  * the name that will be printed by show_options()). This parameter will be
596  * supplied as the second parameter to the dispatch function. If parm_name is
597  * empty, it is assumed that the option does not take a parameter. There is
598  * no provision for optional parameters.
599  *
600  * The options are listed first in order by their index_group number, and then
601  * in the order that add_option() was called. This provides a mechanism for
602  * listing the options defined in derived classes before those of the base
603  * classes.
604  */
605 void ProgramBase::
606 add_option(const string &option, const string &parm_name,
607  int index_group, const string &description,
608  OptionDispatchFunction option_function,
609  bool *bool_var, void *option_data) {
610  Option opt;
611  opt._option = option;
612  opt._parm_name = parm_name;
613  opt._index_group = index_group;
614  opt._sequence = ++_next_sequence;
615  opt._description = description;
616  opt._option_function = option_function;
617  opt._option_method = (OptionDispatchMethod)nullptr;
618  opt._bool_var = bool_var;
619  opt._option_data = option_data;
620 
621  _options_by_name[option] = opt;
622  _sorted_options = false;
623 
624  if (bool_var != nullptr) {
625  (*bool_var) = false;
626  }
627 }
628 
629 /**
630  * This is another variant on add_option(), above, except that it receives a
631  * pointer to a "method", which is really just another static (or global)
632  * function, whose first parameter is a ProgramBase *.
633  *
634  * We can't easily add a variant that accepts a real method, because the C++
635  * syntax for methods requires us to know exactly what class object the method
636  * is defined for, and we want to support adding pointers for methods that are
637  * defined in other classes. So we have this hacky thing, which requires the
638  * "method" to be declared static, and receive its this pointer explicitly, as
639  * the first argument.
640  */
641 void ProgramBase::
642 add_option(const string &option, const string &parm_name,
643  int index_group, const string &description,
644  OptionDispatchMethod option_method,
645  bool *bool_var, void *option_data) {
646  Option opt;
647  opt._option = option;
648  opt._parm_name = parm_name;
649  opt._index_group = index_group;
650  opt._sequence = ++_next_sequence;
651  opt._description = description;
652  opt._option_function = (OptionDispatchFunction)nullptr;
653  opt._option_method = option_method;
654  opt._bool_var = bool_var;
655  opt._option_data = option_data;
656 
657  _options_by_name[option] = opt;
658  _sorted_options = false;
659 
660  if (bool_var != nullptr) {
661  (*bool_var) = false;
662  }
663 }
664 
665 /**
666  * Changes the description associated with a previously-defined option.
667  * Returns true if the option was changed, false if it hadn't been defined.
668  */
669 bool ProgramBase::
670 redescribe_option(const string &option, const string &description) {
671  OptionsByName::iterator oi = _options_by_name.find(option);
672  if (oi == _options_by_name.end()) {
673  return false;
674  }
675  (*oi).second._description = description;
676  return true;
677 }
678 
679 /**
680  * Removes a previously-defined option. Returns true if the option was
681  * removed, false if it hadn't existed.
682  */
683 bool ProgramBase::
684 remove_option(const string &option) {
685  OptionsByName::iterator oi = _options_by_name.find(option);
686  if (oi == _options_by_name.end()) {
687  return false;
688  }
689  _options_by_name.erase(oi);
690  _sorted_options = false;
691  return true;
692 }
693 
694 /**
695  * Adds -pr etc. as valid options for this program. These are appropriate
696  * for a model converter or model reader type program, and specify how to
697  * locate possibly-invalid pathnames in the source model file.
698  */
699 void ProgramBase::
700 add_path_replace_options() {
701  add_option
702  ("pr", "path_replace", 40,
703  "Sometimes references to other files (textures, external references) "
704  "are stored with a full path that is appropriate for some other system, "
705  "but does not exist here. This option may be used to specify how "
706  "those invalid paths map to correct paths. Generally, this is of "
707  "the form 'orig_prefix=replacement_prefix', which indicates a "
708  "particular initial sequence of characters that should be replaced "
709  "with a new sequence; e.g. '/c/home/models=/beta/fish'. "
710  "If the replacement prefix does not begin with a slash, the file "
711  "will then be searched for along the search path specified by -pp. "
712  "You may use standard filename matching characters ('*', '?', etc.) in "
713  "the original prefix, and '**' as a component by itself stands for "
714  "any number of components.\n\n"
715 
716  "This option may be repeated as necessary; each file will be tried "
717  "against each specified method, in the order in which they appear in "
718  "the command line, until the file is found. If the file is not found, "
719  "the last matching prefix is used anyway.",
720  &ProgramBase::dispatch_path_replace, nullptr, _path_replace.p());
721 
722  add_option
723  ("pp", "dirname", 40,
724  "Adds the indicated directory name to the list of directories to "
725  "search for filenames referenced by the source file. This is used "
726  "only for relative paths, or for paths that are made relative by a "
727  "-pr replacement string that doesn't begin with a leading slash. "
728  "The model-path is always implicitly searched anyway.",
729  &ProgramBase::dispatch_search_path, nullptr, &(_path_replace->_path));
730 }
731 
732 /**
733  * Adds -ps etc. as valid options for this program. These are appropriate
734  * for a model converter type program, and specify how to represent filenames
735  * in the output file.
736  */
737 void ProgramBase::
738 add_path_store_options() {
739  // If a program has path store options at all, the default path store is
740  // relative.
741  _path_replace->_path_store = PS_relative;
742 
743  add_option
744  ("ps", "path_store", 40,
745  "Specifies the way an externally referenced file is to be "
746  "represented in the resulting output file. This "
747  "assumes the named filename actually exists; "
748  "see -pr to indicate how to deal with external "
749  "references that have bad pathnames. "
750  "This option will not help you to find a missing file, but simply "
751  "controls how filenames are represented in the output.\n\n"
752 
753  "The option may be one of: rel, abs, rel_abs, strip, or keep. If "
754  "either rel or rel_abs is specified, the files are made relative to "
755  "the directory specified by -pd. The default is rel.",
756  &ProgramBase::dispatch_path_store, &_got_path_store,
757  &(_path_replace->_path_store));
758 
759  add_option
760  ("pd", "path_directory", 40,
761  "Specifies the name of a directory to make paths relative to, if "
762  "'-ps rel' or '-ps rel_abs' is specified. If this is omitted, the "
763  "directory name is taken from the name of the output file.",
764  &ProgramBase::dispatch_filename, &_got_path_directory,
765  &(_path_replace->_path_directory));
766 
767  add_option
768  ("pc", "target_directory", 40,
769  "Copies textures and other dependent files into the indicated "
770  "directory. If a relative pathname is specified, it is relative "
771  "to the directory specified with -pd, above.",
772  &ProgramBase::dispatch_filename, &(_path_replace->_copy_files),
773  &(_path_replace->_copy_into_directory));
774 }
775 
776 /**
777  * Standard dispatch function for an option that takes no parameters, and does
778  * nothing special. Typically this would be used for a boolean flag, whose
779  * presence means something and whose absence means something else. Use the
780  * bool_var parameter to add_option() to determine whether the option appears
781  * on the command line or not.
782  */
783 bool ProgramBase::
784 dispatch_none(const string &, const string &, void *) {
785  return true;
786 }
787 
788 /**
789  * Standard dispatch function for an option that takes no parameters, and when
790  * it is present sets a bool variable to the 'true' value. This is another
791  * way to handle a boolean flag. See also dispatch_none() and
792  * dispatch_false().
793  *
794  * The data pointer is to a bool variable.
795  */
796 bool ProgramBase::
797 dispatch_true(const string &, const string &, void *var) {
798  bool *bp = (bool *)var;
799  (*bp) = true;
800  return true;
801 }
802 
803 /**
804  * Standard dispatch function for an option that takes no parameters, and when
805  * it is present sets a bool variable to the 'false' value. This is another
806  * way to handle a boolean flag. See also dispatch_none() and
807  * dispatch_true().
808  *
809  * The data pointer is to a bool variable.
810  */
811 bool ProgramBase::
812 dispatch_false(const string &, const string &, void *var) {
813  bool *bp = (bool *)var;
814  (*bp) = false;
815  return true;
816 }
817 
818 /**
819  * Standard dispatch function for an option that takes no parameters, but
820  * whose presence on the command line increments an integer counter for each
821  * time it appears. -v is often an option that works this way. The data
822  * pointer is to an int counter variable.
823  */
824 bool ProgramBase::
825 dispatch_count(const string &, const string &, void *var) {
826  int *ip = (int *)var;
827  (*ip)++;
828 
829  return true;
830 }
831 
832 /**
833  * Standard dispatch function for an option that takes one parameter, which is
834  * to be interpreted as an integer. The data pointer is to an int variable.
835  */
836 bool ProgramBase::
837 dispatch_int(const string &opt, const string &arg, void *var) {
838  int *ip = (int *)var;
839 
840  if (!string_to_int(arg, *ip)) {
841  nout << "Invalid integer parameter for -" << opt << ": "
842  << arg << "\n";
843  return false;
844  }
845 
846  return true;
847 }
848 
849 /**
850  * Standard dispatch function for an option that takes a pair of integer
851  * parameters. The data pointer is to an array of two integers.
852  */
853 bool ProgramBase::
854 dispatch_int_pair(const string &opt, const string &arg, void *var) {
855  int *ip = (int *)var;
856 
857  vector_string words;
858  tokenize(arg, words, ",");
859 
860  bool okflag = false;
861  if (words.size() == 2) {
862  okflag =
863  string_to_int(words[0], ip[0]) &&
864  string_to_int(words[1], ip[1]);
865  }
866 
867  if (!okflag) {
868  nout << "-" << opt
869  << " requires a pair of integers separated by a comma.\n";
870  return false;
871  }
872 
873  return true;
874 }
875 
876 /**
877  * Standard dispatch function for an option that takes a quad of integer
878  * parameters. The data pointer is to an array of four integers.
879  */
880 bool ProgramBase::
881 dispatch_int_quad(const string &opt, const string &arg, void *var) {
882  int *ip = (int *)var;
883 
884  vector_string words;
885  tokenize(arg, words, ",");
886 
887  bool okflag = false;
888  if (words.size() == 4) {
889  okflag =
890  string_to_int(words[0], ip[0]) &&
891  string_to_int(words[1], ip[1]) &&
892  string_to_int(words[1], ip[2]) &&
893  string_to_int(words[1], ip[3]);
894  }
895 
896  if (!okflag) {
897  nout << "-" << opt
898  << " requires a quad of integers separated by a comma.\n";
899  return false;
900  }
901 
902  return true;
903 }
904 
905 /**
906  * Standard dispatch function for an option that takes one parameter, which is
907  * to be interpreted as a double. The data pointer is to an double variable.
908  */
909 bool ProgramBase::
910 dispatch_double(const string &opt, const string &arg, void *var) {
911  double *ip = (double *)var;
912 
913  if (!string_to_double(arg, *ip)) {
914  nout << "Invalid numeric parameter for -" << opt << ": "
915  << arg << "\n";
916  return false;
917  }
918 
919  return true;
920 }
921 
922 /**
923  * Standard dispatch function for an option that takes a pair of double
924  * parameters. The data pointer is to an array of two doubles.
925  */
926 bool ProgramBase::
927 dispatch_double_pair(const string &opt, const string &arg, void *var) {
928  double *ip = (double *)var;
929 
930  vector_string words;
931  tokenize(arg, words, ",");
932 
933  bool okflag = false;
934  if (words.size() == 2) {
935  okflag =
936  string_to_double(words[0], ip[0]) &&
937  string_to_double(words[1], ip[1]);
938  }
939 
940  if (!okflag) {
941  nout << "-" << opt
942  << " requires a pair of numbers separated by a comma.\n";
943  return false;
944  }
945 
946  return true;
947 }
948 
949 /**
950  * Standard dispatch function for an option that takes a triple of double
951  * parameters. The data pointer is to an array of three doubles.
952  */
953 bool ProgramBase::
954 dispatch_double_triple(const string &opt, const string &arg, void *var) {
955  double *ip = (double *)var;
956 
957  vector_string words;
958  tokenize(arg, words, ",");
959 
960  bool okflag = false;
961  if (words.size() == 3) {
962  okflag =
963  string_to_double(words[0], ip[0]) &&
964  string_to_double(words[1], ip[1]) &&
965  string_to_double(words[2], ip[2]);
966  }
967 
968  if (!okflag) {
969  nout << "-" << opt
970  << " requires three numbers separated by commas.\n";
971  return false;
972  }
973 
974  return true;
975 }
976 
977 /**
978  * Standard dispatch function for an option that takes a quad of double
979  * parameters. The data pointer is to an array of four doubles.
980  */
981 bool ProgramBase::
982 dispatch_double_quad(const string &opt, const string &arg, void *var) {
983  double *ip = (double *)var;
984 
985  vector_string words;
986  tokenize(arg, words, ",");
987 
988  bool okflag = false;
989  if (words.size() == 4) {
990  okflag =
991  string_to_double(words[0], ip[0]) &&
992  string_to_double(words[1], ip[1]) &&
993  string_to_double(words[2], ip[2]) &&
994  string_to_double(words[3], ip[3]);
995  }
996 
997  if (!okflag) {
998  nout << "-" << opt
999  << " requires four numbers separated by commas.\n";
1000  return false;
1001  }
1002 
1003  return true;
1004 }
1005 
1006 /**
1007  * Standard dispatch function for an option that takes a color, as l or l,a or
1008  * r,g,b or r,g,b,a. The data pointer is to an array of four floats, e.g. a
1009  * LColor.
1010  */
1011 bool ProgramBase::
1012 dispatch_color(const string &opt, const string &arg, void *var) {
1013  PN_stdfloat *ip = (PN_stdfloat *)var;
1014 
1015  vector_string words;
1016  tokenize(arg, words, ",");
1017 
1018  bool okflag = false;
1019  switch (words.size()) {
1020  case 4:
1021  okflag =
1022  string_to_stdfloat(words[0], ip[0]) &&
1023  string_to_stdfloat(words[1], ip[1]) &&
1024  string_to_stdfloat(words[2], ip[2]) &&
1025  string_to_stdfloat(words[3], ip[3]);
1026  break;
1027 
1028  case 3:
1029  okflag =
1030  string_to_stdfloat(words[0], ip[0]) &&
1031  string_to_stdfloat(words[1], ip[1]) &&
1032  string_to_stdfloat(words[2], ip[2]);
1033  ip[3] = 1.0;
1034  break;
1035 
1036  case 2:
1037  okflag =
1038  string_to_stdfloat(words[0], ip[0]) &&
1039  string_to_stdfloat(words[1], ip[3]);
1040  ip[1] = ip[0];
1041  ip[2] = ip[0];
1042  break;
1043 
1044  case 1:
1045  okflag =
1046  string_to_stdfloat(words[0], ip[0]);
1047  ip[1] = ip[0];
1048  ip[2] = ip[0];
1049  ip[3] = 1.0;
1050  break;
1051  }
1052 
1053  if (!okflag) {
1054  nout << "-" << opt
1055  << " requires one through four numbers separated by commas.\n";
1056  return false;
1057  }
1058 
1059  return true;
1060 }
1061 
1062 /**
1063  * Standard dispatch function for an option that takes one parameter, which is
1064  * to be interpreted as a string. The data pointer is to a string variable.
1065  */
1066 bool ProgramBase::
1067 dispatch_string(const string &, const string &arg, void *var) {
1068  string *ip = (string *)var;
1069  (*ip) = arg;
1070 
1071  return true;
1072 }
1073 
1074 /**
1075  * Standard dispatch function for an option that takes one parameter, which is
1076  * to be interpreted as a string. This is different from dispatch_string in
1077  * that the parameter may be repeated multiple times, and each time the string
1078  * value is appended to a vector.
1079  *
1080  * The data pointer is to a vector_string variable.
1081  */
1082 bool ProgramBase::
1083 dispatch_vector_string(const string &, const string &arg, void *var) {
1084  vector_string *ip = (vector_string *)var;
1085  (*ip).push_back(arg);
1086 
1087  return true;
1088 }
1089 
1090 /**
1091  * Similar to dispatch_vector_string, but a comma is allowed to separate
1092  * multiple tokens in one argument, without having to repeat the argument for
1093  * each token.
1094  *
1095  * The data pointer is to a vector_string variable.
1096  */
1097 bool ProgramBase::
1098 dispatch_vector_string_comma(const string &, const string &arg, void *var) {
1099  vector_string *ip = (vector_string *)var;
1100 
1101  vector_string words;
1102  tokenize(arg, words, ",");
1103 
1104  vector_string::const_iterator wi;
1105  for (wi = words.begin(); wi != words.end(); ++wi) {
1106  (*ip).push_back(*wi);
1107  }
1108 
1109  return true;
1110 }
1111 
1112 /**
1113  * Standard dispatch function for an option that takes one parameter, which is
1114  * to be interpreted as a filename. The data pointer is to a Filename
1115  * variable.
1116  */
1117 bool ProgramBase::
1118 dispatch_filename(const string &opt, const string &arg, void *var) {
1119  if (arg.empty()) {
1120  nout << "-" << opt << " requires a filename parameter.\n";
1121  return false;
1122  }
1123 
1124  Filename *ip = (Filename *)var;
1125  (*ip) = Filename::from_os_specific(arg);
1126 
1127  return true;
1128 }
1129 
1130 /**
1131  * Standard dispatch function for an option that takes one parameter, which is
1132  * to be interpreted as a single directory name to add to a search path. The
1133  * data pointer is to a DSearchPath variable. This kind of option may appear
1134  * multiple times on the command line; each time, the new directory is
1135  * appended.
1136  */
1137 bool ProgramBase::
1138 dispatch_search_path(const string &opt, const string &arg, void *var) {
1139  if (arg.empty()) {
1140  nout << "-" << opt << " requires a search path parameter.\n";
1141  return false;
1142  }
1143 
1144  DSearchPath *ip = (DSearchPath *)var;
1146 
1147  return true;
1148 }
1149 
1150 /**
1151  * Standard dispatch function for an option that takes one parameter, which is
1152  * to be interpreted as a coordinate system string. The data pointer is to a
1153  * CoordinateSystem variable.
1154  */
1155 bool ProgramBase::
1156 dispatch_coordinate_system(const string &opt, const string &arg, void *var) {
1157  CoordinateSystem *ip = (CoordinateSystem *)var;
1158  (*ip) = parse_coordinate_system_string(arg);
1159 
1160  if ((*ip) == CS_invalid) {
1161  nout << "Invalid coordinate system for -" << opt << ": " << arg << "\n"
1162  << "Valid coordinate system strings are any of 'y-up', 'z-up', "
1163  "'y-up-left', or 'z-up-left'.\n";
1164  return false;
1165  }
1166 
1167  return true;
1168 }
1169 
1170 /**
1171  * Standard dispatch function for an option that takes one parameter, which is
1172  * to be interpreted as a unit of distance measurement. The data pointer is
1173  * to a DistanceUnit variable.
1174  */
1175 bool ProgramBase::
1176 dispatch_units(const string &opt, const string &arg, void *var) {
1177  DistanceUnit *ip = (DistanceUnit *)var;
1178  (*ip) = string_distance_unit(arg);
1179 
1180  if ((*ip) == DU_invalid) {
1181  nout << "Invalid units for -" << opt << ": " << arg << "\n"
1182  << "Valid units are mm, cm, m, km, yd, ft, in, nmi, and mi.\n";
1183  return false;
1184  }
1185 
1186  return true;
1187 }
1188 
1189 /**
1190  * Standard dispatch function for an option that takes one parameter, which is
1191  * to indicate an image file type, like rgb, bmp, jpg, etc. The data pointer
1192  * is to a PNMFileType pointer.
1193  */
1194 bool ProgramBase::
1195 dispatch_image_type(const string &opt, const string &arg, void *var) {
1196  PNMFileType **ip = (PNMFileType **)var;
1197 
1199 
1200  (*ip) = reg->get_type_from_extension(arg);
1201 
1202  if ((*ip) == nullptr) {
1203  nout << "Invalid image type for -" << opt << ": " << arg << "\n"
1204  << "The following image types are known:\n";
1205  reg->write(nout, 2);
1206  return false;
1207  }
1208 
1209  return true;
1210 }
1211 
1212 /**
1213  * Standard dispatch function for an option that takes one parameter, which is
1214  * to be interpreted as a single component of a path replace request. The
1215  * data pointer is to a PathReplace variable.
1216  */
1217 bool ProgramBase::
1218 dispatch_path_replace(const string &opt, const string &arg, void *var) {
1219  PathReplace *ip = (PathReplace *)var;
1220  size_t equals = arg.find('=');
1221  if (equals == string::npos) {
1222  nout << "Invalid path replacement string for -" << opt << ": " << arg << "\n"
1223  << "String should be of the form 'old-prefix=new-prefix'.\n";
1224  return false;
1225  }
1226  ip->add_pattern(arg.substr(0, equals), arg.substr(equals + 1));
1227 
1228  return true;
1229 }
1230 
1231 /**
1232  * Standard dispatch function for an option that takes one parameter, which is
1233  * to be interpreted as a path store string. The data pointer is to a
1234  * PathStore variable.
1235  */
1236 bool ProgramBase::
1237 dispatch_path_store(const string &opt, const string &arg, void *var) {
1238  PathStore *ip = (PathStore *)var;
1239  (*ip) = string_path_store(arg);
1240 
1241  if ((*ip) == PS_invalid) {
1242  nout << "Invalid path store for -" << opt << ": " << arg << "\n"
1243  << "Valid path store strings are any of 'rel', 'abs', "
1244  << "'rel_abs', 'strip', or 'keep'.\n";
1245  return false;
1246  }
1247 
1248  return true;
1249 }
1250 
1251 /**
1252  * Called when the user enters '-h', this describes how to use the program and
1253  * then exits.
1254  */
1255 bool ProgramBase::
1256 handle_help_option(const string &, const string &, void *data) {
1257  ProgramBase *me = (ProgramBase *)data;
1258  me->show_description();
1259  me->show_usage();
1260  me->show_options();
1261  exit(0);
1262 
1263  return false;
1264 }
1265 
1266 
1267 /**
1268  * Word-wraps the indicated text to the indicated output stream. The first
1269  * line is prefixed with the indicated prefix, then tabbed over to
1270  * indent_width where the text actually begins. A newline is inserted at or
1271  * before column line_width. Each subsequent line begins with indent_width
1272  * spaces.
1273  *
1274  * An embedded newline character ('\n') forces a line break, while an embedded
1275  * carriage-return character ('\r'), or two or more consecutive newlines,
1276  * marks a paragraph break, which is usually printed as a blank line.
1277  * Redundant newline and carriage-return characters are generally ignored.
1278  *
1279  * The flag last_newline should be initialized to false for the first call to
1280  * format_text, and then preserved for future calls; it tracks the state of
1281  * trailing newline characters between calls so we can correctly identify
1282  * doubled newlines.
1283  */
1284 void ProgramBase::
1285 format_text(std::ostream &out, bool &last_newline,
1286  const string &prefix, int indent_width,
1287  const string &text, int line_width) {
1288  indent_width = min(indent_width, line_width - 20);
1289  int indent_amount = indent_width;
1290  bool initial_break = false;
1291 
1292  if (!prefix.empty()) {
1293  out << prefix;
1294  indent_amount = indent_width - prefix.length();
1295  if ((int)prefix.length() + 1 > indent_width) {
1296  out << "\n";
1297  initial_break = true;
1298  indent_amount = indent_width;
1299  }
1300  }
1301 
1302  size_t p = 0;
1303 
1304  // Skip any initial whitespace and newlines.
1305  while (p < text.length() && isspace(text[p])) {
1306  if (text[p] == '\r' ||
1307  (p > 0 && text[p] == '\n' && text[p - 1] == '\n') ||
1308  (p == 0 && text[p] == '\n' && last_newline)) {
1309  if (!initial_break) {
1310  // Here's an initial paragraph break, however.
1311  out << "\n";
1312  initial_break = true;
1313  }
1314  indent_amount = indent_width;
1315 
1316  } else if (text[p] == '\n') {
1317  // Largely ignore an initial newline.
1318  indent_amount = indent_width;
1319 
1320  } else if (text[p] == ' ') {
1321  // Do count up leading spaces.
1322  indent_amount++;
1323  }
1324  p++;
1325  }
1326 
1327  last_newline = (!text.empty() && text[text.length() - 1] == '\n');
1328 
1329  while (p < text.length()) {
1330  // Look for the paragraph or line break--the next newline character, if
1331  // any.
1332  size_t par = text.find_first_of("\n\r", p);
1333  bool is_paragraph_break = false;
1334  if (par == string::npos) {
1335  par = text.length();
1336  /*
1337  This shouldn't be necessary.
1338  } else {
1339  is_paragraph_break = (text[par] == '\r');
1340  */
1341  }
1342 
1343  indent(out, indent_amount);
1344 
1345  size_t eol = p + (line_width - indent_width);
1346  if (eol >= par) {
1347  // The rest of the paragraph fits completely on the line.
1348  eol = par;
1349 
1350  } else {
1351  // The paragraph doesn't fit completely on the line. Determine the best
1352  // place to break the line. Look for the last space before the ideal
1353  // eol.
1354  size_t min_eol = max((int)p, (int)eol - 25);
1355  size_t q = eol;
1356  while (q > min_eol && !isspace(text[q])) {
1357  q--;
1358  }
1359  // Now roll back to the last non-space before this one.
1360  while (q > min_eol && isspace(text[q])) {
1361  q--;
1362  }
1363 
1364  if (q != min_eol) {
1365  // Here's a good place to stop!
1366  eol = q + 1;
1367 
1368  } else {
1369  // The line cannot be broken cleanly. Just let it keep going; don't
1370  // try to wrap it.
1371  eol = par;
1372  }
1373  }
1374  out << text.substr(p, eol - p) << "\n";
1375  p = eol;
1376 
1377  // Skip additional whitespace between the lines.
1378  while (p < text.length() && isspace(text[p])) {
1379  if (text[p] == '\r' ||
1380  (p > 0 && text[p] == '\n' && text[p - 1] == '\n')) {
1381  is_paragraph_break = true;
1382  }
1383  p++;
1384  }
1385 
1386  if (eol == par && is_paragraph_break) {
1387  // Print the paragraph break as a blank line.
1388  out << "\n";
1389  if (p >= text.length()) {
1390  // If we end on a paragraph break, don't try to insert a new one in
1391  // the next pass.
1392  last_newline = false;
1393  }
1394  }
1395 
1396  indent_amount = indent_width;
1397  }
1398 }
1399 
1400 
1401 /**
1402  * Puts all the options in order by index number (e.g. in the order they were
1403  * added, within index_groups), for output by show_options().
1404  */
1405 void ProgramBase::
1406 sort_options() {
1407  if (!_sorted_options) {
1408  _options_by_index.clear();
1409 
1410  OptionsByName::const_iterator oi;
1411  for (oi = _options_by_name.begin(); oi != _options_by_name.end(); ++oi) {
1412  _options_by_index.push_back(&(*oi).second);
1413  }
1414 
1415  sort(_options_by_index.begin(), _options_by_index.end(),
1416  SortOptionsByIndex());
1417  _sorted_options = true;
1418  }
1419 }
1420 
1421 /**
1422  * Attempts to determine the ideal terminal width for formatting output.
1423  */
1424 void ProgramBase::
1425 get_terminal_width() {
1426  if (!_got_terminal_width) {
1427  _got_terminal_width = true;
1428  _got_option_indent = false;
1429 
1430 #ifdef IOCTL_TERMINAL_WIDTH
1431  if (use_terminal_width) {
1432  struct winsize size;
1433  int result = ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&size);
1434  if (result < 0 || size.ws_col < 10) {
1435  // Couldn't determine the width for some reason. Instead of
1436  // complaining, just punt.
1437  _terminal_width = default_terminal_width;
1438  } else {
1439 
1440  // Subtract 10% for the comfort margin at the edge.
1441  _terminal_width = size.ws_col - min(8, (int)(size.ws_col * 0.1));
1442  }
1443  return;
1444  }
1445 #endif // IOCTL_TERMINAL_WIDTH
1446 
1447  _terminal_width = default_terminal_width;
1448  }
1449 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
This is intended to be the base class for most general-purpose utility programs in the PANDATOOL tree...
Definition: programBase.h:34
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
void show_usage()
Writes the usage line(s) to stderr.
void write(std::ostream &out, int indent_level=0) const
Writes a list of supported image file types to the indicated output stream, one per line.
This is a convenience class to specialize ConfigVariable as a boolean type.
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_pattern(const std::string &orig_prefix, const std::string &replacement_prefix)
Adds the indicated original/replace pattern to the specification.
Definition: pathReplace.I:47
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:289
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
DistanceUnit
This enumerated type lists all the kinds of units we're likely to come across in model conversion pro...
Definition: distanceUnit.h:23
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
void show_description()
Writes the program description to stderr.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
DistanceUnit string_distance_unit(const string &str)
Converts from a string, as might be input by the user, to one of the known DistanceUnit types.
std::string get_exec_command() const
Returns the command that invoked this program, as a shell-friendly string, suitable for pasting into ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void show_options()
Describes each of the available options to stderr.
A special ostream that formats all of its output through ProgramBase::show_text().
void write_man_page(std::ostream &out)
Generates a man page in nroff syntax based on the description and options.
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
void preprocess_argv(int &argc, char **&argv)
Processes the argc, argv pair as needed before passing it to getopt().
PathStore
This enumerated type lists the methods by which a filename path might be mangled before storing in a ...
Definition: pathStore.h:23
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
This class maintains the set of all known PNMFileTypes in the universe.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This encapsulates the user's command-line request to replace existing, incorrect pathnames to models ...
Definition: pathReplace.h:36
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a convenience class to specialize ConfigVariable as an integer type.
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PathStore string_path_store(const std::string &str)
Stores from a string, as might be input by the user, to one of the known PathStore types.
Definition: pathStore.cxx:60
void show_text(const std::string &text)
Formats the indicated text to stderr with the known _terminal_width.
Definition: programBase.I:18
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
void set_ostream_ptr(std::ostream *ostream_ptr, bool delete_later)
Changes the ostream that all subsequent Notify messages will be written to.
Definition: notify.cxx:71