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