Panda3D
|
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 ¶m = _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)