Fawkes API  Fawkes Development Version
argparser.cpp
1 
2 /***************************************************************************
3  * argparser.cpp - Implementation of the argument parser
4  *
5  * Generated: Mon May 30 13:25:33 2005 (from FireVision)
6  * Copyright 2005-2006 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <core/exceptions/software.h>
25 #include <utils/system/argparser.h>
26 
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30 #include <libgen.h>
31 #include <string>
32 
33 namespace fawkes {
34 
35 /** @class ArgumentParser <utils/system/argparser.h>
36  * Parse command line arguments.
37  * Interface to GNU getopt and getopt_long. Parses command line arguments and
38  * separates long and short options.
39  *
40  * The supplied opt_string is a string containing the legitimate option
41  * characters. A character c denotes an option of the type "-c" (single dash).
42  * If such a character is followed by a colon, the option requires an argument,
43  * Two colons mean an option takes an optional arg.
44  *
45  * If long_options is supplied options started out by two dashes are recognized.
46  * Long option names may be abbreviated if the abbreviation is unique or is an
47  * exact match for some defined option. A long option may take a parameter, of
48  * the form --arg=param or --arg param.
49  *
50  * long_options is a pointer to the first element of an array of struct option
51  * declared in <getopt.h> as
52  *
53  * @code
54  * struct option {
55  * const char *name;
56  * int has_arg;
57  * int *flag;
58  * int val;
59  * };
60  * @endcode
61  *
62  * The meanings of the different fields are:
63  *
64  * name is the name of the long option.
65  *
66  * has_arg is: no_argument (or 0) if the option does not take an argument;
67  * required_argument (or 1) if the option requires an argument;
68  * or optional_argument (or 2) if the option takes an optional argument.
69  *
70  * flag specifies how results are returned for a long option. If flag is
71  * NULL, then getopt_long() returns val. (For example, the calling
72  * program may set val to the equivalent short option character.)
73  * Otherwise, getopt_long() returns 0, and flag points to a variable
74  * which is set to val if the option is found, but left unchanged if the
75  * option is not found. Handled internally in ArgumentParser
76  *
77  * For more information see man 3 getopt.
78  *
79  * All arguments that do not belong to parsed options are stored as items and can
80  * be retrieved via items().
81  */
82 
83 /** Constructor
84  * @param argc argument count.
85  * @param argv argument vector
86  * @param opt_string option string, see ArgumentParser
87  * @param long_options long options, see ArgumentParser
88  */
89 ArgumentParser::ArgumentParser(int argc, char **argv, const char *opt_string, option *long_options)
90 {
91  argc_ = argc;
92  argv_ = argv;
93 
94  opt_string_ = opt_string;
95 
96  if (long_options) {
97  option *tmplo = long_options;
98  while (tmplo->name != 0) {
99  long_opts_.push_back(*tmplo);
100  tmplo += 1;
101  }
102  }
103 
104  opts_.clear();
105  items_.clear();
106 
107 #ifdef _GNU_SOURCE
108  program_name_ = strdup(basename(argv[0]));
109 #else
110  // Non-GNU variants may modify the sting in place
111  char *tmp = strdup(argv[0]);
112  program_name_ = strdup(basename(tmp));
113  free(tmp);
114 #endif
115 
116  if (long_options == NULL) {
117  int c;
118  char tmp[2];
119 
120  while ((c = getopt(argc, argv, opt_string)) != -1) {
121  if (c == '?') {
122  throw UnknownArgumentException(c);
123  } else if (c == ':') {
124  throw MissingArgumentException(c);
125  }
126  sprintf(tmp, "%c", c);
127  opts_[tmp] = optarg;
128  }
129  } else {
130  int opt_ind = 0;
131  int c;
132  while ((c = getopt_long(argc, argv, opt_string, long_options, &opt_ind)) != -1) {
133  if (c == '?') {
134  throw UnknownArgumentException(c);
135  } else if (c == 0) {
136  // long options
137  opts_[long_options[opt_ind].name] = optarg;
138  } else {
139  char tmp[2];
140  sprintf(tmp, "%c", c);
141  opts_[tmp] = optarg;
142  }
143  }
144  }
145 
146  items_.clear();
147  int ind = optind;
148  while (ind < argc) {
149  items_.push_back(argv[ind++]);
150  }
151 }
152 
153 /** Destructor. */
155 {
156  free(program_name_);
157  opts_.clear();
158 }
159 
160 /** Check if argument has been supplied.
161  * @param argn argument name to check for
162  * @return true, if the argument was given on the command line, false otherwise
163  */
164 bool
165 ArgumentParser::has_arg(const char *argn)
166 {
167  return (opts_.count((char *)argn) > 0);
168 }
169 
170 /** Get argument value.
171  * Use this method to get the value supplied to the given option.
172  * @param argn argument name to retrieve
173  * @return the argument value. Pointer to static program array. Do not free!
174  * Returns NULL if argument was not supplied on command line.
175  */
176 const char *
177 ArgumentParser::arg(const char *argn)
178 {
179  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
180  return opts_[(char *)argn];
181  } else {
182  return NULL;
183  }
184 }
185 
186 /** Get argument while checking availability.
187  * The argument will be a newly allocated copy of the string. You have to
188  * free it after you are done with it.
189  * @param argn argument name to retrieve
190  * @param value a pointer to a newly allocated copy of the argument value will
191  * be stored here if the argument has been found.
192  * The value is unchanged if argument was not supplied.
193  * @return true, if the argument was supplied, false otherwise
194  */
195 bool
196 ArgumentParser::arg(const char *argn, char **value)
197 {
198  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
199  *value = strdup(opts_[(char *)argn]);
200  return true;
201  } else {
202  return false;
203  }
204 }
205 
206 /** Parse host:port string.
207  * The value referenced by the given argn is parsed for the pattern "host:port".
208  * If the string does not match this pattern an exception is thrown.
209  * The host will be a newly allocated copy of the string. You have to
210  * free it after you are done with it. If no port is supplied in the string (plain
211  * hostname string) the port argument is left unchanged. If the argument has not
212  * been supplied at all both values are left unchanged. Thus it is safe to put the
213  * default values into the variables before passing them to this method. Note
214  * however that you have to free the returned host string in case of a successful
215  * return, and only in that case probably!
216  * @param argn argument name to retrieve
217  * @param host Upon successful return contains a pointer to a newly alloated string
218  * with the hostname part. Free it after you are finished.
219  * @param port upon successful return contains the port part
220  * @return true, if the argument was supplied, false otherwise
221  * @exception OutOfBoundsException thrown if port is not in the range [0..65535]
222  */
223 bool
224 ArgumentParser::parse_hostport(const char *argn, char **host, unsigned short int *port)
225 {
226  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
227  parse_hostport_s(opts_[(char *)argn], host, port);
228  return true;
229  } else {
230  return false;
231  }
232 }
233 
234 /** Parse host:port string.
235  * The value referenced by the given argn is parsed for the pattern "host:port".
236  * If the string does not match this pattern an exception is thrown.
237  * The host will be a newly allocated copy of the string. You have to
238  * free it after you are done with it. If no port is supplied in the string (plain
239  * hostname string) the port argument is left unchanged. If the argument has not
240  * been supplied at all both values are left unchanged. Thus it is safe to put the
241  * default values into the variables before passing them to this method. Note
242  * however that you have to free the returned host string in case of a successful
243  * return, and only in that case probably!
244  * @param s string to parse
245  * @param host Upon successful return contains a pointer to a newly alloated string
246  * with the hostname part. Free it after you are finished.
247  * @param port upon successful return contains the port part
248  * @exception Exception thrown on parsing error
249  */
250 void
251 ArgumentParser::parse_hostport_s(const char *s, char **host, unsigned short int *port)
252 {
253  std::string tmp = s;
254  size_t num_colons = 0;
255  std::string::size_type idx = 0;
256  while ((idx = tmp.find(':', idx)) != std::string::npos) {
257  idx += 1;
258  num_colons += 1;
259  }
260 
261  if (num_colons == 1) {
262  idx = tmp.find(':');
263  *host = strdup(tmp.substr(0, idx).c_str());
264  if (!tmp.substr(idx + 1).empty()) {
265  *port = atoi(tmp.substr(idx + 1).c_str());
266  }
267  } else if (num_colons > 1) {
268  // IPv6
269  if (tmp[0] == '[') {
270  // notation that actually contains a port
271  std::string::size_type closing_idx = tmp.find(']');
272  if (closing_idx == std::string::npos) {
273  throw Exception("No closing bracket for IPv6 address");
274  } else if (closing_idx < (tmp.length() - 1)) {
275  // there might be a port
276  if (tmp[closing_idx + 1] != ':') {
277  throw Exception("Expected colon after closing IPv6 address bracket");
278  } else if (closing_idx > tmp.length() - 3) {
279  throw Exception(
280  "Malformed IPv6 address with port, not enough characters after closing bracket");
281  } else {
282  *host = strdup(tmp.substr(1, closing_idx - 1).c_str());
283  *port = atoi(tmp.substr(closing_idx + 2).c_str());
284  }
285  } else {
286  // Just an IPv6 in bracket notation
287  *host = strdup(tmp.substr(1, closing_idx - 2).c_str());
288  }
289  } else {
290  // no port, just an IPv6 address
291  *host = strdup(tmp.c_str());
292  }
293  } else {
294  // no port given
295  *host = strdup(tmp.c_str());
296  }
297 }
298 
299 /** Parse host:port string.
300  * The value referenced by the given argn is parsed for the pattern "host:port". If the
301  * string does not match this pattern an exception is thrown.
302  * If no port is supplied in the string (plain
303  * hostname string) the port argument is left unchanged. If the argument has not
304  * been supplied at all both values are left unchanged. Thus it is safe to put the default
305  * values into the variables before passing them to this method.
306  * @param argn argument name to retrieve
307  * @param host Upon successful return contains the hostname part
308  * @param port upon successful return contains the port part (unchanged if not supplied)
309  * @return true, if the argument was supplied, false otherwise
310  * @exception OutOfBoundsException thrown if port is not in the range [0..65535]
311  */
312 bool
313 ArgumentParser::parse_hostport(const char *argn, std::string &host, unsigned short int &port)
314 {
315  if ((opts_.count(argn) == 0) || (opts_[argn] == NULL))
316  return false;
317 
318  char * tmp_host = NULL;
319  unsigned short int tmp_port = port;
320  if (parse_hostport(argn, &tmp_host, &tmp_port)) {
321  host = tmp_host;
322  port = tmp_port;
323  return true;
324  }
325  return false;
326 }
327 
328 /** Parse host:port string.
329  * The value referenced by the given argn is parsed for the pattern "host:port". If the
330  * string does not match this pattern an exception is thrown.
331  * If no port is supplied in the string (plain
332  * hostname string) the port argument is left unchanged. If the argument has not
333  * been supplied at all both values are left unchanged. Thus it is safe to put the default
334  * values into the variables before passing them to this method.
335  * @param s string to parse
336  * @param host Upon successful return contains the hostname part
337  * @param port upon successful return contains the port part (unchanged if not supplied)
338  * @exception OutOfBoundsException thrown if port is not in the range [0..65535]
339  */
340 void
341 ArgumentParser::parse_hostport_s(const char *s, std::string &host, unsigned short int &port)
342 {
343  char * tmp_host = NULL;
344  unsigned short int tmp_port = port;
345  parse_hostport_s(s, &tmp_host, &tmp_port);
346  host = tmp_host;
347  port = tmp_port;
348 }
349 
350 /** Parse argument as integer.
351  * Converts the value of the given argument to an integer.
352  * @param argn argument name to retrieve
353  * @return value of string as long int
354  * @exception IllegalArgumentException thrown if the value cannot be properly
355  * converted to an integer
356  * @exception Exception thrown if the argument has not been supplied
357  */
358 long int
359 ArgumentParser::parse_int(const char *argn)
360 {
361  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
362  char * endptr;
363  long int rv = strtol(opts_[argn], &endptr, 10);
364  if (endptr[0] != 0) {
365  throw IllegalArgumentException("Supplied argument is not of type int");
366  }
367  return rv;
368  } else {
369  throw Exception("Value for '%s' not available", argn);
370  }
371 }
372 
373 /** Parse argument as double.
374  * Converts the value of the given argument to a double.
375  * @param argn argument name to retrieve
376  * @return value of string as double
377  * @exception IllegalArgumentException thrown if the value cannot be properly
378  * converted to a double
379  * @exception Exception thrown if the argument has not been supplied
380  */
381 double
383 {
384  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
385  char * endptr;
386  double rv = strtod(opts_[argn], &endptr);
387  if (endptr[0] != 0) {
388  throw IllegalArgumentException("Supplied argument is not of type double");
389  }
390  return rv;
391  } else {
392  throw Exception("Value for '%s' not available", argn);
393  }
394 }
395 
396 /** Parse item as integer.
397  * Converts the value of the given item to an integer.
398  * @param index item index
399  * @return value of string as long int
400  * @exception IllegalArgumentException thrown if the value cannot be properly
401  * converted to an integer
402  * @exception Exception thrown if the argument has not been supplied
403  */
404 long int
406 {
407  if (index < items_.size()) {
408  char * endptr;
409  long int rv = strtol(items_[index], &endptr, 10);
410  if (endptr[0] != 0) {
411  throw IllegalArgumentException("Supplied argument is not of type int");
412  }
413  return rv;
414  } else {
415  throw Exception("Value for item %u not available", index);
416  }
417 }
418 
419 /** Parse item as double.
420  * Converts the value of the given item to a double.
421  * @param index item index
422  * @return value of string as double
423  * @exception IllegalArgumentException thrown if the value cannot be properly
424  * converted to a double
425  * @exception Exception thrown if the argument has not been supplied
426  */
427 double
429 {
430  if (index < items_.size()) {
431  char * endptr;
432  double rv = strtod(items_[index], &endptr);
433  if (endptr[0] != 0) {
434  throw IllegalArgumentException("Supplied argument is not of type double");
435  }
436  return rv;
437  } else {
438  throw Exception("Value for item %u not available", index);
439  }
440 }
441 
442 /** Get non-option items.
443  * @return pointer to vector of pointer to non-argument values. Handled internally,
444  * do not free or delete!
445  */
446 const std::vector<const char *> &
448 {
449  return items_;
450 }
451 
452 /** Get number of non-option items.
453  * @return number of non-opt items.
454  */
455 std::vector<const char *>::size_type
457 {
458  return items_.size();
459 }
460 
461 /** Get number of arguments.
462  * @return number of arguments
463  */
464 int
466 {
467  return argc_;
468 }
469 
470 /** Program argument array as supplied to constructor.
471  * @return argument array.
472  */
473 const char **
475 {
476  return (const char **)argv_;
477 }
478 
479 /** Get name of program.
480  * @return the name of the program (argv[0] of argument vector supplied to constructor).
481  */
482 const char *
484 {
485  return program_name_;
486 }
487 
488 } // end namespace fawkes
int argc() const
Get number of arguments.
Definition: argparser.cpp:465
const char * program_name() const
Get name of program.
Definition: argparser.cpp:483
~ArgumentParser()
Destructor.
Definition: argparser.cpp:154
double parse_item_float(unsigned int index)
Parse item as double.
Definition: argparser.cpp:428
ArgumentParser(int argc, char **argv, const char *opt_string, option *long_options=NULL)
Constructor.
Definition: argparser.cpp:89
const std::vector< const char * > & items() const
Get non-option items.
Definition: argparser.cpp:447
static void parse_hostport_s(const char *s, char **host, unsigned short int *port)
Parse host:port string.
Definition: argparser.cpp:251
const char * arg(const char *argn)
Get argument value.
Definition: argparser.cpp:177
bool parse_hostport(const char *argn, char **host, unsigned short int *port)
Parse host:port string.
Definition: argparser.cpp:224
std::vector< const char * >::size_type num_items() const
Get number of non-option items.
Definition: argparser.cpp:456
const char ** argv() const
Program argument array as supplied to constructor.
Definition: argparser.cpp:474
double parse_float(const char *argn)
Parse argument as double.
Definition: argparser.cpp:382
long int parse_int(const char *argn)
Parse argument as integer.
Definition: argparser.cpp:359
long int parse_item_int(unsigned int index)
Parse item as integer.
Definition: argparser.cpp:405
bool has_arg(const char *argn)
Check if argument has been supplied.
Definition: argparser.cpp:165
Base class for exceptions in Fawkes.
Definition: exception.h:36
Expected parameter is missing.
Definition: software.h:80
Thrown if required argument was missing.
Definition: argparser.h:52
Thrown if unknown argument was supplied.
Definition: argparser.h:39
Fawkes library namespace.