| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936 |
- // Filename: programBase.cxx
- // Created by: drose (13Feb00)
- //
- ////////////////////////////////////////////////////////////////////
- #include "programBase.h"
- #include "wordWrapStream.h"
- #include <pystub.h>
- // Since programBase.cxx includes pystub.h, no program that links with
- // progbase needs to do so. No Python code should attempt to link
- // with libprogbase.so.
- #include <indent.h>
- #include <dSearchPath.h>
- #include <coordinateSystem.h>
- #include <dconfig.h>
- #include <config_dconfig.h>
- #include <stdlib.h>
- #include <algorithm>
- #include <ctype.h>
- // If our system getopt() doesn't come with getopt_long_only(), then use
- // the GNU flavor that we've got in tool for this purpose.
- #ifndef HAVE_GETOPT_LONG_ONLY
- #include <gnu_getopt.h>
- #else
- #include <getopt.h>
- #endif
- // 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>
- #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;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::Constructor
- // Access: Public
- // Description:
- ////////////////////////////////////////////////////////////////////
- ProgramBase::
- ProgramBase() {
- // A call to pystub() to force libpystub.so to be linked in.
- pystub();
- // 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);
- _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);
- // Should we report DConfig's debugging information?
- if (dconfig_cat.is_debug()) {
- dconfig_cat.debug()
- << "DConfig took " << Config::get_total_time_config_init()
- << " CPU seconds initializing, and "
- << Config::get_total_time_external_init()
- << " CPU seconds calling external initialization routines.\n";
- dconfig_cat.debug()
- << "ConfigTable::GetSym() was called "
- << Config::get_total_num_get() << " times.\n";
- }
- // 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();
- 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[]) {
- _program_name = 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().
- vector<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 map<int, const Option *> Options;
- Options options;
- OptionsByName::const_iterator oi;
- int next_index = 256;
- // Let's prefix the option string with "-" to tell GNU 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 != (OptionDispatch)NULL) {
- okflag = (this->*opt._option_function)(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::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";
- copy(args.begin(), args.end(), ostream_iterator<string>(nout, " "));
- 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,
- OptionDispatch 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._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::dispatch_none
- // Access: Protected
- // 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_count
- // Access: Protected
- // 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
- // 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) {
- if (arg.empty()) {
- nout << "-" << opt << " requires an integer parameter.\n";
- return false;
- }
- int *ip = (int *)var;
- const char *arg_str = arg.c_str();
- char *endptr;
- (*ip) = strtol(arg_str, &endptr, 0);
- if (*endptr != '\0') {
- nout << "Invalid integer parameter for -" << opt << ": "
- << arg << "\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_int_pair
- // Access: Protected
- // 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) {
- if (arg.empty()) {
- nout << "-" << opt
- << " requires an pair of integers separated by a comma.\n";
- return false;
- }
- size_t comma = arg.find(',');
- if (comma == string::npos) {
- nout << "-" << opt
- << " requires an pair of integers separated by a comma.\n";
- return false;
- }
- string first = arg.substr(0, comma);
- string second = arg.substr(comma + 1);
- int *ip = (int *)var;
- char *endptr;
- const char *first_str = first.c_str();
- ip[0] = strtol(first_str, &endptr, 0);
- if (*endptr != '\0') {
- nout << "Invalid integer parameter for -" << opt << ": "
- << first << "\n";
- return false;
- }
- const char *second_str = second.c_str();
- ip[1] = strtol(second_str, &endptr, 0);
- if (*endptr != '\0') {
- nout << "Invalid integer parameter for -" << opt << ": "
- << second << "\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_double
- // Access: Protected
- // 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) {
- if (arg.empty()) {
- nout << "-" << opt << " requires a floating-point parameter.\n";
- return false;
- }
- double *ip = (double *)var;
- const char *arg_str = arg.c_str();
- char *endptr;
- (*ip) = strtod(arg_str, &endptr);
- if (*endptr != '\0') {
- nout << "Invalid floating-point parameter for -" << opt << ": "
- << arg << "\n";
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_string
- // Access: Protected
- // 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_filename
- // Access: Protected
- // 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) = arg;
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_search_path
- // Access: Protected
- // Description: Standard dispatch function for an option that takes
- // one parameter, which is to be interpreted as a
- // colon-delimited 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 search paths are 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_path(arg);
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: ProgramBase::dispatch_coordinate_system
- // Access: Protected
- // 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::handle_help_option
- // Access: Protected
- // 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 *) {
- show_description();
- show_usage();
- 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) {
- #ifdef IOCTL_TERMINAL_WIDTH
- struct winsize size;
- int result = ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&size);
- if (result < 0) {
- // Couldn't determine the width for some reason. Instead of
- // complaining, just punt.
- _terminal_width = 72;
- } else {
- // Subtract 10% for the comfort margin at the edge.
- _terminal_width = size.ws_col - min(8, (int)(size.ws_col * 0.1));
- }
- #else // IOCTL_TERMINAL_WIDTH
- _terminal_width = 72;
- #endif // IOCTL_TERMINAL_WIDTH
- _got_terminal_width = true;
- _got_option_indent = false;
- }
- }
|