Panda3D

panda_getopt_impl.cxx

00001 // Filename: panda_getopt_impl.cxx
00002 // Created by:  drose (19Jul11)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "panda_getopt_impl.h"
00016 #include "pvector.h"
00017 
00018 #if defined(HAVE_GETOPT) && defined(HAVE_GETOPT_LONG_ONLY)
00019 // If the system provides both of these functions, we don't need to
00020 // provide our own implementation, so in that case this file does
00021 // nothing.
00022 
00023 #else
00024 // If the system does lack one or the other of these functions, then
00025 // we'll go ahead and provide it instead.
00026 
00027 
00028 char *optarg = NULL;
00029 int optind = 0;
00030 int opterr = 1;
00031 int optopt = 0;
00032 
00033 ////////////////////////////////////////////////////////////////////
00034 //       Class : PandaGetopt
00035 // Description : The implementation within this file of the various
00036 //               getopt() functions.  This class is not visible
00037 //               outside of this file; instead, the interface is via
00038 //               the getopt() functions themselves.
00039 ////////////////////////////////////////////////////////////////////
00040 class PandaGetopt {
00041 public:
00042   PandaGetopt(int argc, char *const argv[], const char *optstring,
00043               const struct option *longopts, bool allow_one_hyphen_long);
00044 
00045   void permute(int argc, char **mutable_argv);
00046   int process(int opterr, int *longindex, char *&optarg, int &optind, int &optopt);
00047 
00048 private:
00049   size_t find_short_option(char short_option);
00050   size_t find_long_option(const string &long_option);
00051 
00052   void scan_options(const char *optstring, const struct option *longopts);
00053   void scan_args(int argc, char *const argv[]);
00054 
00055   // We build a list of Options, which correspond to the input defined
00056   // in optstring and/or in the longopts list.  These are the short
00057   // and long options that are available, whether or not the user
00058   // tries to use any of them.  This list is populated by
00059   // scan_options().
00060   class Option {
00061   public:
00062     Option(char short_option, int has_arg);
00063     Option(const struct option *longopts, int longopts_index);
00064 
00065     char _short_option;
00066     string _long_option;
00067     int _has_arg;
00068     const struct option *_option;
00069     int _longopts_index;
00070   };
00071 
00072   // We next build a list of Params, which are the parameter options
00073   // that are parsed out of the argv array--those options that the
00074   // user has actually specified.  This list does not contain the
00075   // non-option arguments, the words that follow the options on the
00076   // command line (those end up in the _arguments list instead).  This
00077   // list is populated by scan_args().
00078   class Param {
00079   public:
00080     Param(size_t opt_index, size_t argv_index, 
00081           char short_option, char *argument = NULL);
00082 
00083     size_t _opt_index;
00084     size_t _argv_index;
00085     char _short_option;
00086     char *_argument;
00087   };
00088 
00089   // The list of available options.
00090   typedef pvector<Option> Options;
00091   Options _options;
00092 
00093   // The list of invoked options.
00094   typedef pvector<Param> Params;
00095   Params _params;
00096 
00097   typedef pvector<char *> Arguments;
00098 
00099   // The list of option arguments on the command line, with pointers
00100   // back into the original argv array.  This is similar to the
00101   // _params list, above, but it is the pointers to the original
00102   // unprocessed strings.  We use this list to premute the argv array
00103   // into proper order if needed.
00104   Arguments _output_argv;
00105 
00106   // The list of non-option arguments on the command line, following
00107   // the options.  The vector contains the actual pointers back into
00108   // the original argv array; we use it to permute the argv array into
00109   // proper order if needed.
00110   Arguments _arguments;
00111 
00112   // See the PandaGetopt constructor for an explanation of these
00113   // two flags.
00114   bool _return_in_order;
00115   bool _require_order;
00116 
00117   // If we are invoked via getopt_long_only(), then a single hyphen is
00118   // allowed to introduce a long option, as well as a double hyphen.
00119   bool _allow_one_hyphen_long;
00120 
00121   // This member is used to hold our place in the parameters list
00122   // across multiple calls to process().
00123   size_t _next_param;
00124 
00125   // This is the index of the first non-option argument in the argv
00126   // list.  It's filled into optind when process() reaches the end of
00127   // its processing.
00128   size_t _next_argv_index;
00129 };
00130 
00131 // This global pointer is used to differentiate between getopt() being
00132 // called the first time, vs. subsequent times.
00133 static PandaGetopt *pgetopt = NULL;
00134 
00135 ////////////////////////////////////////////////////////////////////
00136 //     Function: PandaGetopt::Constructor
00137 //       Access: Public
00138 //  Description:
00139 ////////////////////////////////////////////////////////////////////
00140 PandaGetopt::
00141 PandaGetopt(int argc, char *const argv[], const char *optstring,
00142             const struct option *longopts, bool allow_one_hyphen_long) {
00143   assert(optstring != NULL);
00144 
00145   _return_in_order = false;
00146   _require_order = false;
00147   _allow_one_hyphen_long = allow_one_hyphen_long;
00148   _next_param = 0;
00149 
00150   // _options[0] is used for invalid characters.
00151   _options.push_back(Option('?', no_argument));
00152 
00153   if (optstring[0] == '-') {
00154     // RETURN_IN_ORDER: Non-option arguments (operands) are handled as
00155     // if they were the argument to an option with the value 1
00156     // ('\001').
00157     ++optstring;
00158     _return_in_order = true;
00159 
00160     // _options[1] is option '\001'.
00161     _options.push_back(Option('\001', required_argument));
00162 
00163   } else if (optstring[0] == '+') {
00164     // REQUIRE_ORDER: option processing stops when the first
00165     // non-option argument is reached, or when the element of argv is
00166     // "--".
00167     ++optstring;
00168     _require_order = true;
00169 
00170   } else if (getenv("POSIXLY_CORRECT") != NULL) {
00171     // REQUIRE_ORDER.
00172     _require_order = true;
00173 
00174   } else {
00175     // PERMUTE: the order of arguments in argv is altered so that all
00176     // options (and their arguments) are moved in front of all of the
00177     // operands.
00178   }
00179 
00180   scan_options(optstring, longopts);
00181   scan_args(argc, argv);
00182 }
00183 
00184 ////////////////////////////////////////////////////////////////////
00185 //     Function: PandaGetopt::permute
00186 //       Access: Public
00187 //  Description: Permutes the argv array so that the non-option
00188 //               arguments are at the end of the list (if
00189 //               POSIXLY_CORRECT is not set), as the gnu
00190 //               implementation does.
00191 ////////////////////////////////////////////////////////////////////
00192 void PandaGetopt::
00193 permute(int argc, char **mutable_argv) {
00194   if (!_require_order && !_return_in_order) {
00195     // Rebuild the argv array to reflect the reordered options.
00196     size_t i = 1;
00197     Arguments::const_iterator gi;
00198     for (gi = _output_argv.begin(); gi != _output_argv.end(); ++gi) {
00199       assert((int)i < argc);
00200       mutable_argv[i] = (*gi);
00201       ++i;
00202     }
00203     _next_argv_index = i;
00204     for (gi = _arguments.begin(); gi != _arguments.end(); ++gi) {
00205       assert((int)i < argc);
00206       mutable_argv[i] = (*gi);
00207       ++i;
00208     }
00209     assert((int)i == argc);
00210   }
00211 }
00212 
00213 ////////////////////////////////////////////////////////////////////
00214 //     Function: PandaGetopt::process
00215 //       Access: Public
00216 //  Description: Can be called repeatedly to extract out the option
00217 //               arguments scanned from the argv list, one at a time.
00218 //               Sets *longindex, optarg, optind, optopt.
00219 //               Returns EOF when finished.
00220 ////////////////////////////////////////////////////////////////////
00221 int PandaGetopt::
00222 process(int opterr, int *longindex, char *&optarg, int &optind, int &optopt) {
00223   if (_next_param >= _params.size()) {
00224     optind = _next_argv_index;
00225     return EOF;
00226   }
00227 
00228   const Param &param = _params[_next_param];
00229   ++_next_param;
00230   const Option &option = _options[param._opt_index];
00231 
00232   optarg = param._argument;
00233   optind = (int)param._argv_index;
00234   if (longindex != NULL) {
00235     *longindex = option._longopts_index;
00236   }
00237 
00238   if (option._option != NULL) {
00239     // This was a long option.  Check the special longopt handling
00240     // parameters.
00241     if (option._option->flag == NULL) {
00242       return option._option->val;
00243     }
00244     *(option._option->flag) = option._option->val;
00245     return 0;
00246   }
00247 
00248   if (param._opt_index == 0 && opterr) {
00249     // This was an invalid character.
00250     optopt = param._short_option;
00251     cerr << "Illegal option: -" << param._short_option << "\n";
00252     return '?';
00253   }
00254 
00255   // This was a short option.  Return the option itself.
00256   return param._short_option;
00257 }
00258 
00259 ////////////////////////////////////////////////////////////////////
00260 //     Function: PandaGetopt::find_short_option
00261 //       Access: Private
00262 //  Description: Returns the index within the _options array of the
00263 //               option with the indicated short_option letter, or 0
00264 //               if the option is not found.
00265 ////////////////////////////////////////////////////////////////////
00266 size_t PandaGetopt::
00267 find_short_option(char short_option) {
00268   size_t opt_index = 1;
00269   while (opt_index < _options.size()) {
00270     if (_options[opt_index]._short_option == short_option) {
00271       return opt_index;
00272     }
00273     ++opt_index;
00274   }
00275 
00276   return 0;
00277 }
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: PandaGetopt::find_long_option
00281 //       Access: Private
00282 //  Description: Returns the index within the _options array of the
00283 //               option with the indicated long_option word, or 0
00284 //               if the option is not found.  If the word contains an
00285 //               '=' sign, only the text before this sign is
00286 //               considered.
00287 ////////////////////////////////////////////////////////////////////
00288 size_t PandaGetopt::
00289 find_long_option(const string &long_option) {
00290   string search = long_option;
00291   size_t equals = search.find('=');
00292   if (equals != string::npos) {
00293     search = search.substr(0, equals);
00294   }
00295 
00296   size_t opt_index = 1;
00297   while (opt_index < _options.size()) {
00298     if (_options[opt_index]._long_option == search) {
00299       return opt_index;
00300     }
00301     ++opt_index;
00302   }
00303 
00304   return 0;
00305 }
00306 
00307 ////////////////////////////////////////////////////////////////////
00308 //     Function: PandaGetopt::scan_options
00309 //       Access: Private
00310 //  Description: Parses the optstring and longopts list to understand
00311 //               the options we should be searching for, and populate
00312 //               the internal _options array.
00313 ////////////////////////////////////////////////////////////////////
00314 void PandaGetopt::
00315 scan_options(const char *optstring, const struct option *longopts) {
00316   const char *p = optstring;
00317   while (*p != '\0') {
00318     char short_option = *p;
00319     int has_arg = no_argument;
00320     ++p;
00321     if (*p == ':') {
00322       has_arg = required_argument;
00323       ++p;
00324       if (*p == ':') {
00325         has_arg = optional_argument;
00326         ++p;
00327       }
00328     }
00329     
00330     _options.push_back(Option(short_option, has_arg));
00331   }
00332 
00333   if (longopts != NULL) {
00334     int longopts_index = 0;
00335     while (longopts[longopts_index].name != NULL) {
00336       _options.push_back(Option(longopts, longopts_index));
00337       ++longopts_index;
00338     }
00339   }
00340 }
00341 
00342 ////////////////////////////////////////////////////////////////////
00343 //     Function: PandaGetopt::scan_args
00344 //       Access: Private
00345 //  Description: Parses the argv list to understand the arguments
00346 //               passed by the user, and populates the _params and
00347 //               _arguments arrays.
00348 ////////////////////////////////////////////////////////////////////
00349 void PandaGetopt::
00350 scan_args(int argc, char *const argv[]) {
00351   size_t ai = 1;
00352   bool end_of_processing = false;
00353 
00354   while ((int)ai < argc) {
00355     assert(argv[ai] != NULL);
00356 
00357     if (argv[ai][0] != '-' || end_of_processing) {
00358       // This is a non-option argument.
00359       if (_require_order) {
00360         break;
00361       }
00362       if (_return_in_order) {
00363         // Record it as an argument of _options[1], which is '\001'.
00364         _params.push_back(Param(1, ai, '\001', argv[ai]));
00365         _output_argv.push_back(argv[ai]);
00366       } else {
00367         // Push the non-option argument onto its list, and continue scanning.
00368         _arguments.push_back(argv[ai]);
00369       }
00370 
00371     } else if (strcmp(argv[ai], "--") == 0) {
00372       // Special case: this ends processing.  Everything after this
00373       // is a non-option argument.
00374       _output_argv.push_back(argv[ai]);
00375       end_of_processing = true;
00376 
00377     } else {
00378       // An option argument.
00379 
00380       char *option = NULL;
00381       char *argument = NULL;
00382       size_t opt_index = 0;
00383       bool is_long_option = false;
00384       bool has_argument = false;
00385 
00386       if (argv[ai][1] == '-') {
00387         // This is a long option.
00388         option = argv[ai] + 2;
00389         opt_index = find_long_option(option);
00390         is_long_option = true;
00391       } else {
00392         // This is one or more short options, or a short option and
00393         // its argument.
00394         option = argv[ai] + 1;
00395         if (_allow_one_hyphen_long) {
00396           // Or maybe it's a long option.
00397           opt_index = find_long_option(option);
00398           if (opt_index != 0) {
00399             is_long_option = true;
00400           }
00401         }
00402         if (!is_long_option) {
00403           opt_index = find_short_option(option[0]);
00404           while (opt_index != 0 && 
00405                  _options[opt_index]._has_arg == no_argument && 
00406                  option[1] != '\0') {
00407             // There are multiple short options jammed into a single word.
00408             _params.push_back(Param(opt_index, ai, option[0]));
00409             ++option;
00410             opt_index = find_short_option(option[0]);
00411           }
00412 
00413           if (opt_index != 0 && _options[opt_index]._has_arg != no_argument) {
00414             if (option[1] != '\0') {
00415               // There's an argument embedded in the same word.
00416               argument = option + 1;
00417               has_argument = true;
00418             }
00419           }
00420         }
00421       }
00422 
00423       if (is_long_option) {
00424         char *equals = strchr(option, '=');
00425         if (equals != NULL) {
00426           argument = equals + 1;
00427           has_argument = true;
00428         }
00429       }
00430 
00431       size_t argv_index = ai;
00432 
00433       if (opt_index != 0 && _options[opt_index]._has_arg == required_argument && 
00434           !has_argument) {
00435         // Check the next word for an argument.
00436         _output_argv.push_back(argv[ai]);
00437         ++ai;
00438         if ((int)ai < argc) {
00439           argument = argv[ai];
00440           has_argument = true;
00441         }
00442       }
00443 
00444       _params.push_back(Param(opt_index, argv_index, option[0], argument));
00445       _output_argv.push_back(argv[ai]);
00446     }
00447     ++ai;
00448   }
00449 
00450   _next_argv_index = ai;
00451 
00452   // Now record the non-option arguments that followed the option arguments.
00453   while ((int)ai < argc) {
00454     assert(argv[ai] != NULL);
00455     _arguments.push_back(argv[ai]);
00456     ++ai;
00457   }
00458 }
00459 
00460 ////////////////////////////////////////////////////////////////////
00461 //     Function: PandaGetopt::Option::Constructor
00462 //       Access: Public
00463 //  Description: The constructor for a short_option.  Receives the
00464 //               letter that is the short option, and one of
00465 //               no_argument, required_argument, or optional_argument.
00466 ////////////////////////////////////////////////////////////////////
00467 PandaGetopt::Option::
00468 Option(char short_option, int has_arg) :
00469   _short_option(short_option),
00470   _has_arg(has_arg),
00471   _option(NULL),
00472   _longopts_index(-1)
00473 {
00474 }
00475 
00476 ////////////////////////////////////////////////////////////////////
00477 //     Function: PandaGetopt::Option::Constructor
00478 //       Access: Public
00479 //  Description: The constructor for a long_option.  Receives the
00480 //               longopts array and the index within the array for
00481 //               this particular option.
00482 ////////////////////////////////////////////////////////////////////
00483 PandaGetopt::Option::
00484 Option(const struct option *longopts, int longopts_index) :
00485   _short_option(0),
00486   _long_option(longopts[longopts_index].name),
00487   _has_arg(longopts[longopts_index].has_arg),
00488   _option(&longopts[longopts_index]),
00489   _longopts_index(longopts_index)
00490 {
00491 }
00492 
00493 ////////////////////////////////////////////////////////////////////
00494 //     Function: PandaGetopt::Param::Constructor
00495 //       Access: Public
00496 //  Description: 
00497 ////////////////////////////////////////////////////////////////////
00498 PandaGetopt::Param::
00499 Param(size_t opt_index, size_t argv_index, char short_option, char *argument) :
00500   _opt_index(opt_index),
00501   _argv_index(argv_index),
00502   _short_option(short_option),
00503   _argument(argument)
00504 {
00505 }
00506 
00507 int
00508 getopt(int argc, char *const argv[], const char *optstring) {
00509   if (pgetopt == NULL) {
00510     pgetopt = new PandaGetopt(argc, argv, optstring, NULL, false);
00511     pgetopt->permute(argc, (char **)argv);
00512   }
00513   return pgetopt->process(opterr, NULL, optarg, optind, optopt);
00514 }
00515 
00516 int
00517 getopt_long(int argc, char *const argv[], const char *optstring, 
00518             const struct option *longopts, int *longindex) {
00519   if (pgetopt == NULL) {
00520     pgetopt = new PandaGetopt(argc, argv, optstring, longopts, false);
00521     pgetopt->permute(argc, (char **)argv);
00522   }
00523   return pgetopt->process(opterr, longindex, optarg, optind, optopt);
00524 }
00525 
00526 int
00527 getopt_long_only(int argc, char *const argv[], const char *optstring, 
00528                  const struct option *longopts, int *longindex) {
00529   if (pgetopt == NULL) {
00530     pgetopt = new PandaGetopt(argc, argv, optstring, longopts, true);
00531     pgetopt->permute(argc, (char **)argv);
00532   }
00533   return pgetopt->process(opterr, longindex, optarg, optind, optopt);
00534 }
00535 
00536 #endif  // defined(HAVE_GETOPT) && defined(HAVE_GETOPT_LONG_ONLY)
 All Classes Functions Variables Enumerations