| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466 |
- // Filename: programBase.cxx
- // Created by: drose (13Feb00)
- //
- ////////////////////////////////////////////////////////////////////
- //
- // PANDA 3D SOFTWARE
- // Copyright (c) Carnegie Mellon University. All rights reserved.
- //
- // All use of this software is subject to the terms of the revised BSD
- // license. You should have received a copy of this license along
- // with this source code in a file named "LICENSE."
- //
- ////////////////////////////////////////////////////////////////////
- #include "programBase.h"
- #include "wordWrapStream.h"
- #include "pnmFileTypeRegistry.h"
- #include "indent.h"
- #include "dSearchPath.h"
- #include "coordinateSystem.h"
- #include "dconfig.h"
- #include "config_dconfig.h"
- #include "string_utils.h"
- #include "vector_string.h"
- #include "configVariableInt.h"
- #include "configVariableBool.h"
- #include "panda_getopt_long.h"
- #include "preprocess_argv.h"
- #include <stdlib.h>
- #include <algorithm>
- #include <ctype.h>
- // This manifest is defined if we are running on a system (e.g. most
- // any Unix) that allows us to determine the width of the terminal
- // screen via an ioctl() call. It's just handy to know for formatting
- // output nicely for the user.
- #ifdef IOCTL_TERMINAL_WIDTH
- #include <termios.h>
- #ifndef TIOCGWINSZ
- #include <sys/ioctl.h>
- #elif __APPLE__
- #include <sys/ioctl.h>
- #endif // TIOCGWINSZ
- #endif // IOCTL_TERMINAL_WIDTH
- bool ProgramBase::SortOptionsByIndex::
- operator () (const Option *a, const Option *b) const {
- if (a->_index_group != b->_index_group) {
- return a->_index_group < b->_index_group;
- }
- return a->_sequence < b->_sequence;
- }
- // This should be called at program termination just to make sure
- // Notify gets properly flushed before we exit, if someone calls
- // exit(). It's probably not necessary, but why not be phobic about
- // it?
- static void flush_nout() {
- nout << flush;
- }
- static ConfigVariableInt default_terminal_width
- ("default-terminal-width", 72,
- PRC_DESC("Specify the column at which to wrap output lines "
- "from pandatool-based programs, if it cannot be determined "
- "automatically."));
- static ConfigVariableBool use_terminal_width
- ("use-terminal-width", true,
- PRC_DESC("True to try to determine the terminal width automatically from "
- "the operating system, if supported; false to use the width "
- "specified by default-terminal-width even if the operating system "
- "appears to report a valid width."));
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::Constructor
- // Access: Public
- // Description:
- ////////////////////////////////////////////////////////////////////
- ProgramBase::
- ProgramBase() {
- // Set up Notify to write output to our own formatted stream.
- Notify::ptr()->set_ostream_ptr(new WordWrapStream(this), true);
- // And we'll want to be sure to flush that in all normal exit cases.
- atexit(&flush_nout);
- _path_replace = new PathReplace;
- // If a program never adds the path store options, the default path
- // store is PS_absolute. This is the most robust solution for
- // programs that read files but do not need to write them.
- _path_replace->_path_store = PS_absolute;
- _got_path_store = false;
- _got_path_directory = false;
- _next_sequence = 0;
- _sorted_options = false;
- _last_newline = false;
- _got_terminal_width = false;
- _got_option_indent = false;
- add_option("h", "", 100,
- "Display this help page.",
- &ProgramBase::handle_help_option, NULL, (void *)this);
- // It's nice to start with a blank line.
- nout << "\r";
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::Destructor
- // Access: Public, Virtual
- // Description:
- ////////////////////////////////////////////////////////////////////
- ProgramBase::
- ~ProgramBase() {
- // Reset Notify in case any messages get sent after our
- // destruction--our stream is no longer valid.
- Notify::ptr()->set_ostream_ptr(NULL, false);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::show_description
- // Access: Public
- // Description: Writes the program description to stderr.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- show_description() {
- nout << _description << "\n";
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::show_usage
- // Access: Public
- // Description: Writes the usage line(s) to stderr.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- show_usage() {
- nout << "\rUsage:\n";
- Runlines::const_iterator ri;
- string prog = " " +_program_name.get_basename_wo_extension();
- for (ri = _runlines.begin(); ri != _runlines.end(); ++ri) {
- show_text(prog, prog.length() + 1, *ri);
- }
- nout << "\r";
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::show_options
- // Access: Public
- // Description: Describes each of the available options to stderr.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- show_options() {
- sort_options();
- if (!_got_option_indent) {
- get_terminal_width();
- _option_indent = min(15, (int)(_terminal_width * 0.25));
- _got_option_indent = true;
- }
- nout << "Options:\n";
- OptionsByIndex::const_iterator oi;
- for (oi = _options_by_index.begin(); oi != _options_by_index.end(); ++oi) {
- const Option &opt = *(*oi);
- string prefix = " -" + opt._option + " " + opt._parm_name;
- show_text(prefix, _option_indent, opt._description + "\r");
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::show_text
- // Access: Public
- // Description: Formats the indicated text and its prefix for output
- // to stderr with the known _terminal_width.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- show_text(const string &prefix, int indent_width, string text) {
- get_terminal_width();
- // This is correct! It goes go to cerr, not to nout. Sending it to
- // nout would be cyclic, since nout is redefined to map back through
- // this function.
- format_text(cerr, _last_newline,
- prefix, indent_width, text, _terminal_width);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::parse_command_line
- // Access: Public, Virtual
- // Description: Dispatches on each of the options on the command
- // line, and passes the remaining parameters to
- // handle_args(). If an error on the command line is
- // detected, will automatically call show_usage() and
- // exit(1).
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- parse_command_line(int argc, char **argv) {
- preprocess_argv(argc, argv);
- // Setting this variable to zero reinitializes the options parser
- // This is only necessary for processing multiple command lines in
- // the same program (mainly the MaxToEgg converter plugin)
- extern int optind;
- optind = 0;
- _program_name = Filename::from_os_specific(argv[0]);
- int i;
- for (i = 1; i < argc; i++) {
- _program_args.push_back(argv[i]);
- }
- // Build up the long options list and the short options string for
- // getopt_long_only().
- pvector<struct option> long_options;
- string short_options;
- // We also need to build a temporary map of int index numbers to
- // Option pointers. We'll pass these index numbers to GNU's
- // getopt_long() so we can tell one option from another.
- typedef pmap<int, const Option *> Options;
- Options options;
- OptionsByName::const_iterator oi;
- int next_index = 256;
- // Let's prefix the option string with "-" to tell getopt that we
- // want it to tell us the post-option arguments, instead of trying
- // to meddle with ARGC and ARGV (which we aren't using directly).
- short_options = "-";
- for (oi = _options_by_name.begin(); oi != _options_by_name.end(); ++oi) {
- const Option &opt = (*oi).second;
- int index;
- if (opt._option.length() == 1) {
- // This is a "short" option; its option string consists of only
- // one letter. Its index is the letter itself.
- index = (int)opt._option[0];
- short_options += opt._option;
- if (!opt._parm_name.empty()) {
- // This option takes an argument.
- short_options += ':';
- }
- } else {
- // This is a "long" option; we'll assign it the next available
- // index.
- index = ++next_index;
- }
- // Now add it to the GNU data structures.
- struct option gopt;
- gopt.name = (char *)opt._option.c_str();
- gopt.has_arg = (opt._parm_name.empty()) ?
- no_argument : required_argument;
- gopt.flag = (int *)NULL;
- // Return an index into the _options_by_index array, offset by 256
- // so we don't confuse it with '?'.
- gopt.val = index;
- long_options.push_back(gopt);
- options[index] = &opt;
- }
- // Finally, add one more structure, all zeroes, to indicate the end
- // of the options.
- struct option gopt;
- memset(&gopt, 0, sizeof(gopt));
- long_options.push_back(gopt);
- // We'll use this vector to save the non-option arguments.
- // Generally, these will all be at the end, but with the GNU
- // extensions, they need not be.
- Args remaining_args;
- // Now call getopt_long() to actually parse the arguments.
- extern char *optarg;
- const struct option *long_opts = &long_options[0];
- int flag =
- getopt_long_only(argc, argv, short_options.c_str(), long_opts, NULL);
- while (flag != EOF) {
- string arg;
- if (optarg != NULL) {
- arg = optarg;
- }
- switch (flag) {
- case '?':
- // Invalid option or parameter.
- show_usage();
- exit(1);
- case '\x1':
- // A special return value from getopt() indicating a non-option
- // argument.
- remaining_args.push_back(arg);
- break;
- default:
- {
- // A normal option. Figure out which one it is.
- Options::const_iterator ii;
- ii = options.find(flag);
- if (ii == options.end()) {
- nout << "Internal error! Invalid option index returned.\n";
- abort();
- }
- const Option &opt = *(*ii).second;
- bool okflag = true;
- if (opt._option_function != (OptionDispatchFunction)NULL) {
- okflag = (*opt._option_function)(opt._option, arg, opt._option_data);
- }
- if (opt._option_method != (OptionDispatchMethod)NULL) {
- okflag = (*opt._option_method)(this, opt._option, arg, opt._option_data);
- }
- if (opt._bool_var != (bool *)NULL) {
- (*opt._bool_var) = true;
- }
- if (!okflag) {
- show_usage();
- exit(1);
- }
- }
- }
- flag =
- getopt_long_only(argc, argv, short_options.c_str(), long_opts, NULL);
- }
- if (!handle_args(remaining_args)) {
- show_usage();
- exit(1);
- }
- if (!post_command_line()) {
- show_usage();
- exit(1);
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::get_exec_command
- // Access: Public
- // Description: Returns the command that invoked this program, as a
- // shell-friendly string, suitable for pasting into the
- // comments of output files.
- ////////////////////////////////////////////////////////////////////
- string ProgramBase::
- get_exec_command() const {
- string command;
- command = _program_name.get_basename_wo_extension();
- Args::const_iterator ai;
- for (ai = _program_args.begin(); ai != _program_args.end(); ++ai) {
- const string &arg = (*ai);
- // First, check to see if the string is shell-acceptable.
- bool legal = true;
- string::const_iterator si;
- for (si = arg.begin(); legal && si != arg.end(); ++si) {
- switch (*si) {
- case ' ':
- case '\n':
- case '\t':
- case '*':
- case '?':
- case '\\':
- case '(':
- case ')':
- case '|':
- case '&':
- case '<':
- case '>':
- case '"':
- case ';':
- case '$':
- legal = false;
- }
- }
- if (legal) {
- command += " " + arg;
- } else {
- command += " '" + arg + "'";
- }
- }
- return command;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::handle_args
- // Access: Protected, Virtual
- // Description: Does something with the additional arguments on the
- // command line (after all the -options have been
- // parsed). Returns true if the arguments are good,
- // false otherwise.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- handle_args(ProgramBase::Args &args) {
- if (!args.empty()) {
- nout << "Unexpected arguments on command line:\n";
- Args::const_iterator ai;
- for (ai = args.begin(); ai != args.end(); ++ai) {
- nout << (*ai) << " ";
- }
- nout << "\r";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::post_command_line
- // Access: Protected, Virtual
- // Description: This is called after the command line has been
- // completely processed, and it gives the program a
- // chance to do some last-minute processing and
- // validation of the options and arguments. It should
- // return true if everything is fine, false if there is
- // an error.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- post_command_line() {
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::set_program_description
- // Access: Protected
- // Description: Sets the description of the program that will be
- // reported by show_usage(). The description should be
- // one long string of text. Embedded newline characters
- // are interpreted as paragraph breaks and printed as
- // blank lines.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- set_program_description(const string &description) {
- _description = description;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::clear_runlines
- // Access: Protected
- // Description: Removes all of the runlines that were previously
- // added, presumably before adding some new ones.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- clear_runlines() {
- _runlines.clear();
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::add_runline
- // Access: Protected
- // Description: Adds an additional line to the list of lines that
- // will be displayed to describe briefly how the program
- // is to be run. Each line should be something like
- // "[opts] arg1 arg2", that is, it does *not* include
- // the name of the program, but it includes everything
- // that should be printed after the name of the program.
- //
- // Normally there is only one runline for a given
- // program, but it is possible to define more than one.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- add_runline(const string &runline) {
- _runlines.push_back(runline);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::clear_options
- // Access: Protected
- // Description: Removes all of the options that were previously
- // added, presumably before adding some new ones.
- // Normally you wouldn't want to do this unless you want
- // to completely replace all of the options defined by
- // base classes.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- clear_options() {
- _options_by_name.clear();
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::add_option
- // Access: Protected
- // Description: Adds (or redefines) a command line option. When
- // parse_command_line() is executed it will look for
- // these options (followed by a hyphen) on the command
- // line; when a particular option is found it will call
- // the indicated option_function, supplying the provided
- // option_data. This allows the user to define a
- // function that does some special behavior for any
- // given option, or to use any of a number of generic
- // pre-defined functions to fill in data for each
- // option.
- //
- // Each option may or may not take a parameter. If
- // parm_name is nonempty, it is assumed that the option
- // does take a parameter (and parm_name contains the
- // name that will be printed by show_options()). This
- // parameter will be supplied as the second parameter to
- // the dispatch function. If parm_name is empty, it is
- // assumed that the option does not take a parameter.
- // There is no provision for optional parameters.
- //
- // The options are listed first in order by their
- // index_group number, and then in the order that
- // add_option() was called. This provides a mechanism
- // for listing the options defined in derived classes
- // before those of the base classes.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- add_option(const string &option, const string &parm_name,
- int index_group, const string &description,
- OptionDispatchFunction option_function,
- bool *bool_var, void *option_data) {
- Option opt;
- opt._option = option;
- opt._parm_name = parm_name;
- opt._index_group = index_group;
- opt._sequence = ++_next_sequence;
- opt._description = description;
- opt._option_function = option_function;
- opt._option_method = (OptionDispatchMethod)NULL;
- opt._bool_var = bool_var;
- opt._option_data = option_data;
- _options_by_name[option] = opt;
- _sorted_options = false;
- if (bool_var != (bool *)NULL) {
- (*bool_var) = false;
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::add_option
- // Access: Protected
- // Description: This is another variant on add_option(), above,
- // except that it receives a pointer to a "method",
- // which is really just another static (or global)
- // function, whose first parameter is a ProgramBase *.
- //
- // We can't easily add a variant that accepts a real
- // method, because the C++ syntax for methods requires
- // us to know exactly what class object the method is
- // defined for, and we want to support adding pointers
- // for methods that are defined in other classes. So we
- // have this hacky thing, which requires the "method" to
- // be declared static, and receive its this pointer
- // explicitly, as the first argument.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- add_option(const string &option, const string &parm_name,
- int index_group, const string &description,
- OptionDispatchMethod option_method,
- bool *bool_var, void *option_data) {
- Option opt;
- opt._option = option;
- opt._parm_name = parm_name;
- opt._index_group = index_group;
- opt._sequence = ++_next_sequence;
- opt._description = description;
- opt._option_function = (OptionDispatchFunction)NULL;
- opt._option_method = option_method;
- opt._bool_var = bool_var;
- opt._option_data = option_data;
- _options_by_name[option] = opt;
- _sorted_options = false;
- if (bool_var != (bool *)NULL) {
- (*bool_var) = false;
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::redescribe_option
- // Access: Protected
- // Description: Changes the description associated with a
- // previously-defined option. Returns true if the
- // option was changed, false if it hadn't been defined.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- redescribe_option(const string &option, const string &description) {
- OptionsByName::iterator oi = _options_by_name.find(option);
- if (oi == _options_by_name.end()) {
- return false;
- }
- (*oi).second._description = description;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::remove_option
- // Access: Protected
- // Description: Removes a previously-defined option. Returns true if
- // the option was removed, false if it hadn't existed.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- remove_option(const string &option) {
- OptionsByName::iterator oi = _options_by_name.find(option);
- if (oi == _options_by_name.end()) {
- return false;
- }
- _options_by_name.erase(oi);
- _sorted_options = false;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::add_path_replace_options
- // Access: Public
- // Description: Adds -pr etc. as valid options for this program.
- // These are appropriate for a model converter or model
- // reader type program, and specify how to locate
- // possibly-invalid pathnames in the source model file.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- add_path_replace_options() {
- add_option
- ("pr", "path_replace", 40,
- "Sometimes references to other files (textures, external references) "
- "are stored with a full path that is appropriate for some other system, "
- "but does not exist here. This option may be used to specify how "
- "those invalid paths map to correct paths. Generally, this is of "
- "the form 'orig_prefix=replacement_prefix', which indicates a "
- "particular initial sequence of characters that should be replaced "
- "with a new sequence; e.g. '/c/home/models=/beta/fish'. "
- "If the replacement prefix does not begin with a slash, the file "
- "will then be searched for along the search path specified by -pp. "
- "You may use standard filename matching characters ('*', '?', etc.) in "
- "the original prefix, and '**' as a component by itself stands for "
- "any number of components.\n\n"
- "This option may be repeated as necessary; each file will be tried "
- "against each specified method, in the order in which they appear in "
- "the command line, until the file is found. If the file is not found, "
- "the last matching prefix is used anyway.",
- &ProgramBase::dispatch_path_replace, NULL, _path_replace.p());
- add_option
- ("pp", "dirname", 40,
- "Adds the indicated directory name to the list of directories to "
- "search for filenames referenced by the source file. This is used "
- "only for relative paths, or for paths that are made relative by a "
- "-pr replacement string that doesn't begin with a leading slash. "
- "The model-path is always implicitly searched anyway.",
- &ProgramBase::dispatch_search_path, NULL, &(_path_replace->_path));
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::add_path_store_options
- // Access: Public
- // Description: Adds -ps etc. as valid options for this program.
- // These are appropriate for a model converter type
- // program, and specify how to represent filenames in
- // the output file.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- add_path_store_options() {
- // If a program has path store options at all, the default path
- // store is relative.
- _path_replace->_path_store = PS_relative;
- add_option
- ("ps", "path_store", 40,
- "Specifies the way an externally referenced file is to be "
- "represented in the resulting output file. This "
- "assumes the named filename actually exists; "
- "see -pr to indicate how to deal with external "
- "references that have bad pathnames. "
- "This option will not help you to find a missing file, but simply "
- "controls how filenames are represented in the output.\n\n"
- "The option may be one of: rel, abs, rel_abs, strip, or keep. If "
- "either rel or rel_abs is specified, the files are made relative to "
- "the directory specified by -pd. The default is rel.",
- &ProgramBase::dispatch_path_store, &_got_path_store,
- &(_path_replace->_path_store));
- add_option
- ("pd", "path_directory", 40,
- "Specifies the name of a directory to make paths relative to, if "
- "'-ps rel' or '-ps rel_abs' is specified. If this is omitted, the "
- "directory name is taken from the name of the output file.",
- &ProgramBase::dispatch_filename, &_got_path_directory,
- &(_path_replace->_path_directory));
- add_option
- ("pc", "target_directory", 40,
- "Copies textures and other dependent files into the indicated "
- "directory. If a relative pathname is specified, it is relative "
- "to the directory specified with -pd, above.",
- &ProgramBase::dispatch_filename, &(_path_replace->_copy_files),
- &(_path_replace->_copy_into_directory));
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_none
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // no parameters, and does nothing special. Typically
- // this would be used for a boolean flag, whose presence
- // means something and whose absence means something
- // else. Use the bool_var parameter to add_option() to
- // determine whether the option appears on the command
- // line or not.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_none(const string &, const string &, void *) {
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_true
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // no parameters, and when it is present sets a bool
- // variable to the 'true' value. This is another way to
- // handle a boolean flag. See also dispatch_none() and
- // dispatch_false().
- //
- // The data pointer is to a bool variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_true(const string &, const string &, void *var) {
- bool *bp = (bool *)var;
- (*bp) = true;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_false
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // no parameters, and when it is present sets a bool
- // variable to the 'false' value. This is another way to
- // handle a boolean flag. See also dispatch_none() and
- // dispatch_true().
- //
- // The data pointer is to a bool variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_false(const string &, const string &, void *var) {
- bool *bp = (bool *)var;
- (*bp) = false;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_count
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // no parameters, but whose presence on the command line
- // increments an integer counter for each time it
- // appears. -v is often an option that works this way.
- // The data pointer is to an int counter variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_count(const string &, const string &, void *var) {
- int *ip = (int *)var;
- (*ip)++;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_int
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as an
- // integer. The data pointer is to an int variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_int(const string &opt, const string &arg, void *var) {
- int *ip = (int *)var;
- if (!string_to_int(arg, *ip)) {
- nout << "Invalid integer parameter for -" << opt << ": "
- << arg << "\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_int_pair
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // a pair of integer parameters. The data pointer is to
- // an array of two integers.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_int_pair(const string &opt, const string &arg, void *var) {
- int *ip = (int *)var;
- vector_string words;
- tokenize(arg, words, ",");
- bool okflag = false;
- if (words.size() == 2) {
- okflag =
- string_to_int(words[0], ip[0]) &&
- string_to_int(words[1], ip[1]);
- }
- if (!okflag) {
- nout << "-" << opt
- << " requires a pair of integers separated by a comma.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_int_quad
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // a quad of integer parameters. The data pointer is to
- // an array of four integers.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_int_quad(const string &opt, const string &arg, void *var) {
- int *ip = (int *)var;
- vector_string words;
- tokenize(arg, words, ",");
- bool okflag = false;
- if (words.size() == 4) {
- okflag =
- string_to_int(words[0], ip[0]) &&
- string_to_int(words[1], ip[1]) &&
- string_to_int(words[1], ip[2]) &&
- string_to_int(words[1], ip[3]);
- }
- if (!okflag) {
- nout << "-" << opt
- << " requires a quad of integers separated by a comma.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_double
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // double. The data pointer is to an double variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_double(const string &opt, const string &arg, void *var) {
- double *ip = (double *)var;
- if (!string_to_double(arg, *ip)) {
- nout << "Invalid numeric parameter for -" << opt << ": "
- << arg << "\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_double_pair
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // a pair of double parameters. The data pointer is to
- // an array of two doubles.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_double_pair(const string &opt, const string &arg, void *var) {
- double *ip = (double *)var;
- vector_string words;
- tokenize(arg, words, ",");
- bool okflag = false;
- if (words.size() == 2) {
- okflag =
- string_to_double(words[0], ip[0]) &&
- string_to_double(words[1], ip[1]);
- }
- if (!okflag) {
- nout << "-" << opt
- << " requires a pair of numbers separated by a comma.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_double_triple
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // a triple of double parameters. The data pointer is to
- // an array of three doubles.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_double_triple(const string &opt, const string &arg, void *var) {
- double *ip = (double *)var;
- vector_string words;
- tokenize(arg, words, ",");
- bool okflag = false;
- if (words.size() == 3) {
- okflag =
- string_to_double(words[0], ip[0]) &&
- string_to_double(words[1], ip[1]) &&
- string_to_double(words[2], ip[2]);
- }
- if (!okflag) {
- nout << "-" << opt
- << " requires three numbers separated by commas.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_double_quad
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // a quad of double parameters. The data pointer is to
- // an array of four doubles.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_double_quad(const string &opt, const string &arg, void *var) {
- double *ip = (double *)var;
- vector_string words;
- tokenize(arg, words, ",");
- bool okflag = false;
- if (words.size() == 4) {
- okflag =
- string_to_double(words[0], ip[0]) &&
- string_to_double(words[1], ip[1]) &&
- string_to_double(words[2], ip[2]) &&
- string_to_double(words[3], ip[3]);
- }
- if (!okflag) {
- nout << "-" << opt
- << " requires four numbers separated by commas.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_color
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes a
- // color, as l or l,a or r,g,b or r,g,b,a. The data
- // pointer is to an array of four floats, e.g. a LColor.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_color(const string &opt, const string &arg, void *var) {
- PN_stdfloat *ip = (PN_stdfloat *)var;
- vector_string words;
- tokenize(arg, words, ",");
- bool okflag = false;
- switch (words.size()) {
- case 4:
- okflag =
- string_to_float(words[0], ip[0]) &&
- string_to_float(words[1], ip[1]) &&
- string_to_float(words[2], ip[2]) &&
- string_to_float(words[3], ip[3]);
- break;
- case 3:
- okflag =
- string_to_float(words[0], ip[0]) &&
- string_to_float(words[1], ip[1]) &&
- string_to_float(words[2], ip[2]);
- ip[3] = 1.0;
- break;
- case 2:
- okflag =
- string_to_float(words[0], ip[0]) &&
- string_to_float(words[1], ip[3]);
- ip[1] = ip[0];
- ip[2] = ip[0];
- break;
- case 1:
- okflag =
- string_to_float(words[0], ip[0]);
- ip[1] = ip[0];
- ip[2] = ip[0];
- ip[3] = 1.0;
- break;
- }
- if (!okflag) {
- nout << "-" << opt
- << " requires one through four numbers separated by commas.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_string
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // string. The data pointer is to a string variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_string(const string &, const string &arg, void *var) {
- string *ip = (string *)var;
- (*ip) = arg;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_vector_string
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // string. This is different from dispatch_string in
- // that the parameter may be repeated multiple times,
- // and each time the string value is appended to a
- // vector.
- //
- // The data pointer is to a vector_string variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_vector_string(const string &, const string &arg, void *var) {
- vector_string *ip = (vector_string *)var;
- (*ip).push_back(arg);
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_vector_string_comma
- // Access: Protected, Static
- // Description: Similar to dispatch_vector_string, but a comma is
- // allowed to separate multiple tokens in one argument,
- // without having to repeat the argument for each token.
- //
- // The data pointer is to a vector_string variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_vector_string_comma(const string &, const string &arg, void *var) {
- vector_string *ip = (vector_string *)var;
- vector_string words;
- tokenize(arg, words, ",");
- vector_string::const_iterator wi;
- for (wi = words.begin(); wi != words.end(); ++wi) {
- (*ip).push_back(*wi);
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_filename
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // filename. The data pointer is to a Filename variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_filename(const string &opt, const string &arg, void *var) {
- if (arg.empty()) {
- nout << "-" << opt << " requires a filename parameter.\n";
- return false;
- }
- Filename *ip = (Filename *)var;
- (*ip) = Filename::from_os_specific(arg);
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_search_path
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // single directory name to add to a search path. The
- // data pointer is to a DSearchPath variable. This kind
- // of option may appear multiple times on the command
- // line; each time, the new directory is appended.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_search_path(const string &opt, const string &arg, void *var) {
- if (arg.empty()) {
- nout << "-" << opt << " requires a search path parameter.\n";
- return false;
- }
- DSearchPath *ip = (DSearchPath *)var;
- ip->append_directory(Filename::from_os_specific(arg));
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_coordinate_system
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // coordinate system string. The data pointer is to a
- // CoordinateSystem variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_coordinate_system(const string &opt, const string &arg, void *var) {
- CoordinateSystem *ip = (CoordinateSystem *)var;
- (*ip) = parse_coordinate_system_string(arg);
- if ((*ip) == CS_invalid) {
- nout << "Invalid coordinate system for -" << opt << ": " << arg << "\n"
- << "Valid coordinate system strings are any of 'y-up', 'z-up', "
- "'y-up-left', or 'z-up-left'.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_units
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // unit of distance measurement. The data pointer is to
- // a DistanceUnit variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_units(const string &opt, const string &arg, void *var) {
- DistanceUnit *ip = (DistanceUnit *)var;
- (*ip) = string_distance_unit(arg);
- if ((*ip) == DU_invalid) {
- nout << "Invalid units for -" << opt << ": " << arg << "\n"
- << "Valid units are mm, cm, m, km, yd, ft, in, nmi, and mi.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_image_type
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to indicate an image file
- // type, like rgb, bmp, jpg, etc. The data pointer is
- // to a PNMFileType pointer.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_image_type(const string &opt, const string &arg, void *var) {
- PNMFileType **ip = (PNMFileType **)var;
- PNMFileTypeRegistry *reg = PNMFileTypeRegistry::get_global_ptr();
- (*ip) = reg->get_type_from_extension(arg);
- if ((*ip) == (PNMFileType *)NULL) {
- nout << "Invalid image type for -" << opt << ": " << arg << "\n"
- << "The following image types are known:\n";
- reg->write(nout, 2);
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_path_replace
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // single component of a path replace request. The data
- // pointer is to a PathReplace variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_path_replace(const string &opt, const string &arg, void *var) {
- PathReplace *ip = (PathReplace *)var;
- size_t equals = arg.find('=');
- if (equals == string::npos) {
- nout << "Invalid path replacement string for -" << opt << ": " << arg << "\n"
- << "String should be of the form 'old-prefix=new-prefix'.\n";
- return false;
- }
- ip->add_pattern(arg.substr(0, equals), arg.substr(equals + 1));
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_path_store
- // Access: Protected, Static
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // path store string. The data pointer is to a
- // PathStore variable.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- dispatch_path_store(const string &opt, const string &arg, void *var) {
- PathStore *ip = (PathStore *)var;
- (*ip) = string_path_store(arg);
- if ((*ip) == PS_invalid) {
- nout << "Invalid path store for -" << opt << ": " << arg << "\n"
- << "Valid path store strings are any of 'rel', 'abs', "
- << "'rel_abs', 'strip', or 'keep'.\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::handle_help_option
- // Access: Protected, Static
- // Description: Called when the user enters '-h', this describes how
- // to use the program and then exits.
- ////////////////////////////////////////////////////////////////////
- bool ProgramBase::
- handle_help_option(const string &, const string &, void *data) {
- ProgramBase *me = (ProgramBase *)data;
- me->show_description();
- me->show_usage();
- me->show_options();
- exit(0);
- return false;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::format_text
- // Access: Protected, Static
- // Description: Word-wraps the indicated text to the indicated output
- // stream. The first line is prefixed with the
- // indicated prefix, then tabbed over to indent_width
- // where the text actually begins. A newline is
- // inserted at or before column line_width. Each
- // subsequent line begins with indent_width spaces.
- //
- // An embedded newline character ('\n') forces a line
- // break, while an embedded carriage-return character
- // ('\r'), or two or more consecutive newlines, marks a
- // paragraph break, which is usually printed as a blank
- // line. Redundant newline and carriage-return
- // characters are generally ignored.
- //
- // The flag last_newline should be initialized to false
- // for the first call to format_text, and then preserved
- // for future calls; it tracks the state of trailing
- // newline characters between calls so we can correctly
- // identify doubled newlines.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- format_text(ostream &out, bool &last_newline,
- const string &prefix, int indent_width,
- const string &text, int line_width) {
- indent_width = min(indent_width, line_width - 20);
- int indent_amount = indent_width;
- bool initial_break = false;
- if (!prefix.empty()) {
- out << prefix;
- indent_amount = indent_width - prefix.length();
- if ((int)prefix.length() + 1 > indent_width) {
- out << "\n";
- initial_break = true;
- indent_amount = indent_width;
- }
- }
- size_t p = 0;
- // Skip any initial whitespace and newlines.
- while (p < text.length() && isspace(text[p])) {
- if (text[p] == '\r' ||
- (p > 0 && text[p] == '\n' && text[p - 1] == '\n') ||
- (p == 0 && text[p] == '\n' && last_newline)) {
- if (!initial_break) {
- // Here's an initial paragraph break, however.
- out << "\n";
- initial_break = true;
- }
- indent_amount = indent_width;
- } else if (text[p] == '\n') {
- // Largely ignore an initial newline.
- indent_amount = indent_width;
- } else if (text[p] == ' ') {
- // Do count up leading spaces.
- indent_amount++;
- }
- p++;
- }
- last_newline = (!text.empty() && text[text.length() - 1] == '\n');
- while (p < text.length()) {
- // Look for the paragraph or line break--the next newline
- // character, if any.
- size_t par = text.find_first_of("\n\r", p);
- bool is_paragraph_break = false;
- if (par == string::npos) {
- par = text.length();
- /*
- This shouldn't be necessary.
- } else {
- is_paragraph_break = (text[par] == '\r');
- */
- }
- indent(out, indent_amount);
- size_t eol = p + (line_width - indent_width);
- if (eol >= par) {
- // The rest of the paragraph fits completely on the line.
- eol = par;
- } else {
- // The paragraph doesn't fit completely on the line. Determine
- // the best place to break the line. Look for the last space
- // before the ideal eol.
- size_t min_eol = max((int)p, (int)eol - 25);
- size_t q = eol;
- while (q > min_eol && !isspace(text[q])) {
- q--;
- }
- // Now roll back to the last non-space before this one.
- while (q > min_eol && isspace(text[q])) {
- q--;
- }
- if (q != min_eol) {
- // Here's a good place to stop!
- eol = q + 1;
- } else {
- // The line cannot be broken cleanly. Just let it keep going;
- // don't try to wrap it.
- eol = par;
- }
- }
- out << text.substr(p, eol - p) << "\n";
- p = eol;
- // Skip additional whitespace between the lines.
- while (p < text.length() && isspace(text[p])) {
- if (text[p] == '\r' ||
- (p > 0 && text[p] == '\n' && text[p - 1] == '\n')) {
- is_paragraph_break = true;
- }
- p++;
- }
- if (eol == par && is_paragraph_break) {
- // Print the paragraph break as a blank line.
- out << "\n";
- if (p >= text.length()) {
- // If we end on a paragraph break, don't try to insert a new
- // one in the next pass.
- last_newline = false;
- }
- }
- indent_amount = indent_width;
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::sort_options
- // Access: Private
- // Description: Puts all the options in order by index number
- // (e.g. in the order they were added, within
- // index_groups), for output by show_options().
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- sort_options() {
- if (!_sorted_options) {
- _options_by_index.clear();
- OptionsByName::const_iterator oi;
- for (oi = _options_by_name.begin(); oi != _options_by_name.end(); ++oi) {
- _options_by_index.push_back(&(*oi).second);
- }
- sort(_options_by_index.begin(), _options_by_index.end(),
- SortOptionsByIndex());
- _sorted_options = true;
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::get_terminal_width
- // Access: Private
- // Description: Attempts to determine the ideal terminal width for
- // formatting output.
- ////////////////////////////////////////////////////////////////////
- void ProgramBase::
- get_terminal_width() {
- if (!_got_terminal_width) {
- _got_terminal_width = true;
- _got_option_indent = false;
- #ifdef IOCTL_TERMINAL_WIDTH
- if (use_terminal_width) {
- struct winsize size;
- int result = ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&size);
- if (result < 0 || size.ws_col < 10) {
- // Couldn't determine the width for some reason. Instead of
- // complaining, just punt.
- _terminal_width = default_terminal_width;
- } else {
-
- // Subtract 10% for the comfort margin at the edge.
- _terminal_width = size.ws_col - min(8, (int)(size.ws_col * 0.1));
- }
- return;
- }
- #endif // IOCTL_TERMINAL_WIDTH
-
- _terminal_width = default_terminal_width;
- }
- }
|