Panda3D
 All Classes Functions Variables Enumerations
panda_getopt_impl.cxx
1 // Filename: panda_getopt_impl.cxx
2 // Created by: drose (19Jul11)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "panda_getopt_impl.h"
16 #include "pvector.h"
17 
18 #if defined(HAVE_GETOPT) && defined(HAVE_GETOPT_LONG_ONLY)
19 // If the system provides both of these functions, we don't need to
20 // provide our own implementation, so in that case this file does
21 // nothing.
22 
23 #else
24 // If the system does lack one or the other of these functions, then
25 // we'll go ahead and provide it instead.
26 
27 
28 char *optarg = NULL;
29 int optind = 0;
30 int opterr = 1;
31 int optopt = 0;
32 
33 ////////////////////////////////////////////////////////////////////
34 // Class : PandaGetopt
35 // Description : The implementation within this file of the various
36 // getopt() functions. This class is not visible
37 // outside of this file; instead, the interface is via
38 // the getopt() functions themselves.
39 ////////////////////////////////////////////////////////////////////
40 class PandaGetopt {
41 public:
42  PandaGetopt(int argc, char *const argv[], const char *optstring,
43  const struct option *longopts, bool allow_one_hyphen_long);
44 
45  void permute(int argc, char **mutable_argv);
46  int process(int opterr, int *longindex, char *&optarg, int &optind, int &optopt);
47 
48 private:
49  size_t find_short_option(char short_option);
50  size_t find_long_option(const string &long_option);
51 
52  void scan_options(const char *optstring, const struct option *longopts);
53  void scan_args(int argc, char *const argv[]);
54 
55  // We build a list of Options, which correspond to the input defined
56  // in optstring and/or in the longopts list. These are the short
57  // and long options that are available, whether or not the user
58  // tries to use any of them. This list is populated by
59  // scan_options().
60  class Option {
61  public:
62  Option(char short_option, int has_arg);
63  Option(const struct option *longopts, int longopts_index);
64 
65  char _short_option;
66  string _long_option;
67  int _has_arg;
68  const struct option *_option;
69  int _longopts_index;
70  };
71 
72  // We next build a list of Params, which are the parameter options
73  // that are parsed out of the argv array--those options that the
74  // user has actually specified. This list does not contain the
75  // non-option arguments, the words that follow the options on the
76  // command line (those end up in the _arguments list instead). This
77  // list is populated by scan_args().
78  class Param {
79  public:
80  Param(size_t opt_index, size_t argv_index,
81  char short_option, char *argument = NULL);
82 
83  size_t _opt_index;
84  size_t _argv_index;
85  char _short_option;
86  char *_argument;
87  };
88 
89  // The list of available options.
90  typedef pvector<Option> Options;
91  Options _options;
92 
93  // The list of invoked options.
94  typedef pvector<Param> Params;
95  Params _params;
96 
97  typedef pvector<char *> Arguments;
98 
99  // The list of option arguments on the command line, with pointers
100  // back into the original argv array. This is similar to the
101  // _params list, above, but it is the pointers to the original
102  // unprocessed strings. We use this list to premute the argv array
103  // into proper order if needed.
104  Arguments _output_argv;
105 
106  // The list of non-option arguments on the command line, following
107  // the options. The vector contains the actual pointers back into
108  // the original argv array; we use it to permute the argv array into
109  // proper order if needed.
110  Arguments _arguments;
111 
112  // See the PandaGetopt constructor for an explanation of these
113  // two flags.
114  bool _return_in_order;
115  bool _require_order;
116 
117  // If we are invoked via getopt_long_only(), then a single hyphen is
118  // allowed to introduce a long option, as well as a double hyphen.
119  bool _allow_one_hyphen_long;
120 
121  // This member is used to hold our place in the parameters list
122  // across multiple calls to process().
123  size_t _next_param;
124 
125  // This is the index of the first non-option argument in the argv
126  // list. It's filled into optind when process() reaches the end of
127  // its processing.
128  size_t _next_argv_index;
129 };
130 
131 // This global pointer is used to differentiate between getopt() being
132 // called the first time, vs. subsequent times.
133 static PandaGetopt *pgetopt = NULL;
134 
135 ////////////////////////////////////////////////////////////////////
136 // Function: PandaGetopt::Constructor
137 // Access: Public
138 // Description:
139 ////////////////////////////////////////////////////////////////////
140 PandaGetopt::
141 PandaGetopt(int argc, char *const argv[], const char *optstring,
142  const struct option *longopts, bool allow_one_hyphen_long) {
143  assert(optstring != NULL);
144 
145  _return_in_order = false;
146  _require_order = false;
147  _allow_one_hyphen_long = allow_one_hyphen_long;
148  _next_param = 0;
149 
150  // _options[0] is used for invalid characters.
151  _options.push_back(Option('?', no_argument));
152 
153  if (optstring[0] == '-') {
154  // RETURN_IN_ORDER: Non-option arguments (operands) are handled as
155  // if they were the argument to an option with the value 1
156  // ('\001').
157  ++optstring;
158  _return_in_order = true;
159 
160  // _options[1] is option '\001'.
161  _options.push_back(Option('\001', required_argument));
162 
163  } else if (optstring[0] == '+') {
164  // REQUIRE_ORDER: option processing stops when the first
165  // non-option argument is reached, or when the element of argv is
166  // "--".
167  ++optstring;
168  _require_order = true;
169 
170  } else if (getenv("POSIXLY_CORRECT") != NULL) {
171  // REQUIRE_ORDER.
172  _require_order = true;
173 
174  } else {
175  // PERMUTE: the order of arguments in argv is altered so that all
176  // options (and their arguments) are moved in front of all of the
177  // operands.
178  }
179 
180  scan_options(optstring, longopts);
181  scan_args(argc, argv);
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: PandaGetopt::permute
186 // Access: Public
187 // Description: Permutes the argv array so that the non-option
188 // arguments are at the end of the list (if
189 // POSIXLY_CORRECT is not set), as the gnu
190 // implementation does.
191 ////////////////////////////////////////////////////////////////////
192 void PandaGetopt::
193 permute(int argc, char **mutable_argv) {
194  if (!_require_order && !_return_in_order) {
195  // Rebuild the argv array to reflect the reordered options.
196  size_t i = 1;
197  Arguments::const_iterator gi;
198  for (gi = _output_argv.begin(); gi != _output_argv.end(); ++gi) {
199  assert((int)i < argc);
200  mutable_argv[i] = (*gi);
201  ++i;
202  }
203  _next_argv_index = i;
204  for (gi = _arguments.begin(); gi != _arguments.end(); ++gi) {
205  assert((int)i < argc);
206  mutable_argv[i] = (*gi);
207  ++i;
208  }
209  assert((int)i == argc);
210  }
211 }
212 
213 ////////////////////////////////////////////////////////////////////
214 // Function: PandaGetopt::process
215 // Access: Public
216 // Description: Can be called repeatedly to extract out the option
217 // arguments scanned from the argv list, one at a time.
218 // Sets *longindex, optarg, optind, optopt.
219 // Returns EOF when finished.
220 ////////////////////////////////////////////////////////////////////
221 int PandaGetopt::
222 process(int opterr, int *longindex, char *&optarg, int &optind, int &optopt) {
223  if (_next_param >= _params.size()) {
224  optind = _next_argv_index;
225  return EOF;
226  }
227 
228  const Param &param = _params[_next_param];
229  ++_next_param;
230  const Option &option = _options[param._opt_index];
231 
232  optarg = param._argument;
233  optind = (int)param._argv_index;
234  if (longindex != NULL) {
235  *longindex = option._longopts_index;
236  }
237 
238  if (option._option != NULL) {
239  // This was a long option. Check the special longopt handling
240  // parameters.
241  if (option._option->flag == NULL) {
242  return option._option->val;
243  }
244  *(option._option->flag) = option._option->val;
245  return 0;
246  }
247 
248  if (param._opt_index == 0 && opterr) {
249  // This was an invalid character.
250  optopt = param._short_option;
251  cerr << "Illegal option: -" << param._short_option << "\n";
252  return '?';
253  }
254 
255  // This was a short option. Return the option itself.
256  return param._short_option;
257 }
258 
259 ////////////////////////////////////////////////////////////////////
260 // Function: PandaGetopt::find_short_option
261 // Access: Private
262 // Description: Returns the index within the _options array of the
263 // option with the indicated short_option letter, or 0
264 // if the option is not found.
265 ////////////////////////////////////////////////////////////////////
266 size_t PandaGetopt::
267 find_short_option(char short_option) {
268  size_t opt_index = 1;
269  while (opt_index < _options.size()) {
270  if (_options[opt_index]._short_option == short_option) {
271  return opt_index;
272  }
273  ++opt_index;
274  }
275 
276  return 0;
277 }
278 
279 ////////////////////////////////////////////////////////////////////
280 // Function: PandaGetopt::find_long_option
281 // Access: Private
282 // Description: Returns the index within the _options array of the
283 // option with the indicated long_option word, or 0
284 // if the option is not found. If the word contains an
285 // '=' sign, only the text before this sign is
286 // considered.
287 ////////////////////////////////////////////////////////////////////
288 size_t PandaGetopt::
289 find_long_option(const string &long_option) {
290  string search = long_option;
291  size_t equals = search.find('=');
292  if (equals != string::npos) {
293  search = search.substr(0, equals);
294  }
295 
296  size_t opt_index = 1;
297  while (opt_index < _options.size()) {
298  if (_options[opt_index]._long_option == search) {
299  return opt_index;
300  }
301  ++opt_index;
302  }
303 
304  return 0;
305 }
306 
307 ////////////////////////////////////////////////////////////////////
308 // Function: PandaGetopt::scan_options
309 // Access: Private
310 // Description: Parses the optstring and longopts list to understand
311 // the options we should be searching for, and populate
312 // the internal _options array.
313 ////////////////////////////////////////////////////////////////////
314 void PandaGetopt::
315 scan_options(const char *optstring, const struct option *longopts) {
316  const char *p = optstring;
317  while (*p != '\0') {
318  char short_option = *p;
319  int has_arg = no_argument;
320  ++p;
321  if (*p == ':') {
322  has_arg = required_argument;
323  ++p;
324  if (*p == ':') {
325  has_arg = optional_argument;
326  ++p;
327  }
328  }
329 
330  _options.push_back(Option(short_option, has_arg));
331  }
332 
333  if (longopts != NULL) {
334  int longopts_index = 0;
335  while (longopts[longopts_index].name != NULL) {
336  _options.push_back(Option(longopts, longopts_index));
337  ++longopts_index;
338  }
339  }
340 }
341 
342 ////////////////////////////////////////////////////////////////////
343 // Function: PandaGetopt::scan_args
344 // Access: Private
345 // Description: Parses the argv list to understand the arguments
346 // passed by the user, and populates the _params and
347 // _arguments arrays.
348 ////////////////////////////////////////////////////////////////////
349 void PandaGetopt::
350 scan_args(int argc, char *const argv[]) {
351  size_t ai = 1;
352  bool end_of_processing = false;
353 
354  while ((int)ai < argc) {
355  assert(argv[ai] != NULL);
356 
357  if (argv[ai][0] != '-' || end_of_processing) {
358  // This is a non-option argument.
359  if (_require_order) {
360  break;
361  }
362  if (_return_in_order) {
363  // Record it as an argument of _options[1], which is '\001'.
364  _params.push_back(Param(1, ai, '\001', argv[ai]));
365  _output_argv.push_back(argv[ai]);
366  } else {
367  // Push the non-option argument onto its list, and continue scanning.
368  _arguments.push_back(argv[ai]);
369  }
370 
371  } else if (strcmp(argv[ai], "--") == 0) {
372  // Special case: this ends processing. Everything after this
373  // is a non-option argument.
374  _output_argv.push_back(argv[ai]);
375  end_of_processing = true;
376 
377  } else {
378  // An option argument.
379 
380  char *option = NULL;
381  char *argument = NULL;
382  size_t opt_index = 0;
383  bool is_long_option = false;
384  bool has_argument = false;
385 
386  if (argv[ai][1] == '-') {
387  // This is a long option.
388  option = argv[ai] + 2;
389  opt_index = find_long_option(option);
390  is_long_option = true;
391  } else {
392  // This is one or more short options, or a short option and
393  // its argument.
394  option = argv[ai] + 1;
395  if (_allow_one_hyphen_long) {
396  // Or maybe it's a long option.
397  opt_index = find_long_option(option);
398  if (opt_index != 0) {
399  is_long_option = true;
400  }
401  }
402  if (!is_long_option) {
403  opt_index = find_short_option(option[0]);
404  while (opt_index != 0 &&
405  _options[opt_index]._has_arg == no_argument &&
406  option[1] != '\0') {
407  // There are multiple short options jammed into a single word.
408  _params.push_back(Param(opt_index, ai, option[0]));
409  ++option;
410  opt_index = find_short_option(option[0]);
411  }
412 
413  if (opt_index != 0 && _options[opt_index]._has_arg != no_argument) {
414  if (option[1] != '\0') {
415  // There's an argument embedded in the same word.
416  argument = option + 1;
417  has_argument = true;
418  }
419  }
420  }
421  }
422 
423  if (is_long_option) {
424  char *equals = strchr(option, '=');
425  if (equals != NULL) {
426  argument = equals + 1;
427  has_argument = true;
428  }
429  }
430 
431  size_t argv_index = ai;
432 
433  if (opt_index != 0 && _options[opt_index]._has_arg == required_argument &&
434  !has_argument) {
435  // Check the next word for an argument.
436  _output_argv.push_back(argv[ai]);
437  ++ai;
438  if ((int)ai < argc) {
439  argument = argv[ai];
440  has_argument = true;
441  }
442  }
443 
444  _params.push_back(Param(opt_index, argv_index, option[0], argument));
445  _output_argv.push_back(argv[ai]);
446  }
447  ++ai;
448  }
449 
450  _next_argv_index = ai;
451 
452  // Now record the non-option arguments that followed the option arguments.
453  while ((int)ai < argc) {
454  assert(argv[ai] != NULL);
455  _arguments.push_back(argv[ai]);
456  ++ai;
457  }
458 }
459 
460 ////////////////////////////////////////////////////////////////////
461 // Function: PandaGetopt::Option::Constructor
462 // Access: Public
463 // Description: The constructor for a short_option. Receives the
464 // letter that is the short option, and one of
465 // no_argument, required_argument, or optional_argument.
466 ////////////////////////////////////////////////////////////////////
467 PandaGetopt::Option::
468 Option(char short_option, int has_arg) :
469  _short_option(short_option),
470  _has_arg(has_arg),
471  _option(NULL),
472  _longopts_index(-1)
473 {
474 }
475 
476 ////////////////////////////////////////////////////////////////////
477 // Function: PandaGetopt::Option::Constructor
478 // Access: Public
479 // Description: The constructor for a long_option. Receives the
480 // longopts array and the index within the array for
481 // this particular option.
482 ////////////////////////////////////////////////////////////////////
483 PandaGetopt::Option::
484 Option(const struct option *longopts, int longopts_index) :
485  _short_option(0),
486  _long_option(longopts[longopts_index].name),
487  _has_arg(longopts[longopts_index].has_arg),
488  _option(&longopts[longopts_index]),
489  _longopts_index(longopts_index)
490 {
491 }
492 
493 ////////////////////////////////////////////////////////////////////
494 // Function: PandaGetopt::Param::Constructor
495 // Access: Public
496 // Description:
497 ////////////////////////////////////////////////////////////////////
498 PandaGetopt::Param::
499 Param(size_t opt_index, size_t argv_index, char short_option, char *argument) :
500  _opt_index(opt_index),
501  _argv_index(argv_index),
502  _short_option(short_option),
503  _argument(argument)
504 {
505 }
506 
507 int
508 getopt(int argc, char *const argv[], const char *optstring) {
509  if (pgetopt == NULL) {
510  pgetopt = new PandaGetopt(argc, argv, optstring, NULL, false);
511  pgetopt->permute(argc, (char **)argv);
512  }
513  return pgetopt->process(opterr, NULL, optarg, optind, optopt);
514 }
515 
516 int
517 getopt_long(int argc, char *const argv[], const char *optstring,
518  const struct option *longopts, int *longindex) {
519  if (pgetopt == NULL) {
520  pgetopt = new PandaGetopt(argc, argv, optstring, longopts, false);
521  pgetopt->permute(argc, (char **)argv);
522  }
523  return pgetopt->process(opterr, longindex, optarg, optind, optopt);
524 }
525 
526 int
527 getopt_long_only(int argc, char *const argv[], const char *optstring,
528  const struct option *longopts, int *longindex) {
529  if (pgetopt == NULL) {
530  pgetopt = new PandaGetopt(argc, argv, optstring, longopts, true);
531  pgetopt->permute(argc, (char **)argv);
532  }
533  return pgetopt->process(opterr, longindex, optarg, optind, optopt);
534 }
535 
536 #endif // defined(HAVE_GETOPT) && defined(HAVE_GETOPT_LONG_ONLY)
void permute(int argc, char **mutable_argv)
Permutes the argv array so that the non-option arguments are at the end of the list (if POSIXLY_CORRE...
int process(int panda_opterr, int *longindex, char *&panda_optarg, int &panda_optind, int &panda_optopt)
Can be called repeatedly to extract out the option arguments scanned from the argv list...
The implementation within this file of the various getopt() functions.