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  * @return true, if the argument was supplied, false otherwise
249  * @exception Exception thrown on parsing error
250  */
251 void
252 ArgumentParser::parse_hostport_s(const char *s, char **host, unsigned short int *port)
253 {
254  std::string tmp = s;
255  size_t num_colons = 0;
256  std::string::size_type idx = 0;
257  while ((idx = tmp.find(':', idx)) != std::string::npos) {
258  idx += 1;
259  num_colons += 1;
260  }
261 
262  if (num_colons == 1) {
263  idx = tmp.find(':');
264  *host = strdup(tmp.substr(0, idx).c_str());
265  if (!tmp.substr(idx + 1).empty()) {
266  *port = atoi(tmp.substr(idx + 1).c_str());
267  }
268  } else if (num_colons > 1) {
269  // IPv6
270  if (tmp[0] == '[') {
271  // notation that actually contains a port
272  std::string::size_type closing_idx = tmp.find(']');
273  if (closing_idx == std::string::npos) {
274  throw Exception("No closing bracket for IPv6 address");
275  } else if (closing_idx < (tmp.length() - 1)) {
276  // there might be a port
277  if (tmp[closing_idx + 1] != ':') {
278  throw Exception("Expected colon after closing IPv6 address bracket");
279  } else if (closing_idx > tmp.length() - 3) {
280  throw Exception(
281  "Malformed IPv6 address with port, not enough characters after closing bracket");
282  } else {
283  *host = strdup(tmp.substr(1, closing_idx - 1).c_str());
284  *port = atoi(tmp.substr(closing_idx + 2).c_str());
285  }
286  } else {
287  // Just an IPv6 in bracket notation
288  *host = strdup(tmp.substr(1, closing_idx - 2).c_str());
289  }
290  } else {
291  // no port, just an IPv6 address
292  *host = strdup(tmp.c_str());
293  }
294  } else {
295  // no port given
296  *host = strdup(tmp.c_str());
297  }
298 }
299 
300 /** Parse host:port string.
301  * The value referenced by the given argn is parsed for the pattern "host:port". If the
302  * string does not match this pattern an exception is thrown.
303  * If no port is supplied in the string (plain
304  * hostname string) the port argument is left unchanged. If the argument has not
305  * been supplied at all both values are left unchanged. Thus it is safe to put the default
306  * values into the variables before passing them to this method.
307  * @param argn argument name to retrieve
308  * @param host Upon successful return contains the hostname part
309  * @param port upon successful return contains the port part (unchanged if not supplied)
310  * @return true, if the argument was supplied, false otherwise
311  * @exception OutOfBoundsException thrown if port is not in the range [0..65535]
312  */
313 bool
314 ArgumentParser::parse_hostport(const char *argn, std::string &host, unsigned short int &port)
315 {
316  if ((opts_.count(argn) == 0) || (opts_[argn] == NULL))
317  return false;
318 
319  char * tmp_host = NULL;
320  unsigned short int tmp_port = port;
321  if (parse_hostport(argn, &tmp_host, &tmp_port)) {
322  host = tmp_host;
323  port = tmp_port;
324  return true;
325  }
326  return false;
327 }
328 
329 /** Parse host:port string.
330  * The value referenced by the given argn is parsed for the pattern "host:port". If the
331  * string does not match this pattern an exception is thrown.
332  * If no port is supplied in the string (plain
333  * hostname string) the port argument is left unchanged. If the argument has not
334  * been supplied at all both values are left unchanged. Thus it is safe to put the default
335  * values into the variables before passing them to this method.
336  * @param s string to parse
337  * @param host Upon successful return contains the hostname part
338  * @param port upon successful return contains the port part (unchanged if not supplied)
339  * @return true, if the argument was supplied, false otherwise
340  * @exception OutOfBoundsException thrown if port is not in the range [0..65535]
341  */
342 void
343 ArgumentParser::parse_hostport_s(const char *s, std::string &host, unsigned short int &port)
344 {
345  char * tmp_host = NULL;
346  unsigned short int tmp_port = port;
347  parse_hostport_s(s, &tmp_host, &tmp_port);
348  host = tmp_host;
349  port = tmp_port;
350 }
351 
352 /** Parse argument as integer.
353  * Converts the value of the given argument to an integer.
354  * @param argn argument name to retrieve
355  * @return value of string as long int
356  * @exception IllegalArgumentException thrown if the value cannot be properly
357  * converted to an integer
358  * @exception Exception thrown if the argument has not been supplied
359  */
360 long int
361 ArgumentParser::parse_int(const char *argn)
362 {
363  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
364  char * endptr;
365  long int rv = strtol(opts_[argn], &endptr, 10);
366  if (endptr[0] != 0) {
367  throw IllegalArgumentException("Supplied argument is not of type int");
368  }
369  return rv;
370  } else {
371  throw Exception("Value for '%s' not available", argn);
372  }
373 }
374 
375 /** Parse argument as double.
376  * Converts the value of the given argument to a double.
377  * @param argn argument name to retrieve
378  * @return value of string as double
379  * @exception IllegalArgumentException thrown if the value cannot be properly
380  * converted to a double
381  * @exception Exception thrown if the argument has not been supplied
382  */
383 double
384 ArgumentParser::parse_float(const char *argn)
385 {
386  if ((opts_.count(argn) > 0) && (opts_[argn] != NULL)) {
387  char * endptr;
388  double rv = strtod(opts_[argn], &endptr);
389  if (endptr[0] != 0) {
390  throw IllegalArgumentException("Supplied argument is not of type double");
391  }
392  return rv;
393  } else {
394  throw Exception("Value for '%s' not available", argn);
395  }
396 }
397 
398 /** Parse item as integer.
399  * Converts the value of the given item to an integer.
400  * @param index item index
401  * @return value of string as long int
402  * @exception IllegalArgumentException thrown if the value cannot be properly
403  * converted to an integer
404  * @exception Exception thrown if the argument has not been supplied
405  */
406 long int
407 ArgumentParser::parse_item_int(unsigned int index)
408 {
409  if (index < items_.size()) {
410  char * endptr;
411  long int rv = strtol(items_[index], &endptr, 10);
412  if (endptr[0] != 0) {
413  throw IllegalArgumentException("Supplied argument is not of type int");
414  }
415  return rv;
416  } else {
417  throw Exception("Value for item %u not available", index);
418  }
419 }
420 
421 /** Parse item as double.
422  * Converts the value of the given item to a double.
423  * @param index item index
424  * @return value of string as double
425  * @exception IllegalArgumentException thrown if the value cannot be properly
426  * converted to a double
427  * @exception Exception thrown if the argument has not been supplied
428  */
429 double
430 ArgumentParser::parse_item_float(unsigned int index)
431 {
432  if (index < items_.size()) {
433  char * endptr;
434  double rv = strtod(items_[index], &endptr);
435  if (endptr[0] != 0) {
436  throw IllegalArgumentException("Supplied argument is not of type double");
437  }
438  return rv;
439  } else {
440  throw Exception("Value for item %u not available", index);
441  }
442 }
443 
444 /** Get non-option items.
445  * @return pointer to vector of pointer to non-argument values. Handled internally,
446  * do not free or delete!
447  */
448 const std::vector<const char *> &
449 ArgumentParser::items() const
450 {
451  return items_;
452 }
453 
454 /** Get number of non-option items.
455  * @return number of non-opt items.
456  */
457 std::vector<const char *>::size_type
459 {
460  return items_.size();
461 }
462 
463 /** Get number of arguments.
464  * @return number of arguments
465  */
466 int
467 ArgumentParser::argc() const
468 {
469  return argc_;
470 }
471 
472 /** Program argument array as supplied to constructor.
473  * @return argument array.
474  */
475 const char **
476 ArgumentParser::argv() const
477 {
478  return (const char **)argv_;
479 }
480 
481 /** Get name of program.
482  * @return the name of the program (argv[0] of argument vector supplied to constructor).
483  */
484 const char *
486 {
487  return program_name_;
488 }
489 
490 } // end namespace fawkes
fawkes::IllegalArgumentException
Definition: software.h:85
fawkes::ArgumentParser::parse_hostport
bool parse_hostport(const char *argn, char **host, unsigned short int *port)
Parse host:port string.
Definition: argparser.cpp:230
fawkes::ArgumentParser::argc
int argc() const
Get number of arguments.
Definition: argparser.cpp:473
fawkes::ArgumentParser::parse_item_float
double parse_item_float(unsigned int index)
Parse item as double.
Definition: argparser.cpp:436
fawkes::ArgumentParser::~ArgumentParser
~ArgumentParser()
Destructor.
Definition: argparser.cpp:160
fawkes::UnknownArgumentException
Thrown if unknown argument was supplied.
Definition: argparser.h:44
fawkes::ArgumentParser::has_arg
bool has_arg(const char *argn)
Check if argument has been supplied.
Definition: argparser.cpp:171
fawkes::ArgumentParser::argv
const char ** argv() const
Program argument array as supplied to constructor.
Definition: argparser.cpp:482
fawkes::ArgumentParser::parse_hostport_s
static void parse_hostport_s(const char *s, char **host, unsigned short int *port)
Parse host:port string.
Definition: argparser.cpp:258
fawkes::ArgumentParser::parse_int
long int parse_int(const char *argn)
Parse argument as integer.
Definition: argparser.cpp:367
fawkes::ArgumentParser::items
const std::vector< const char * > & items() const
Get non-option items.
Definition: argparser.cpp:455
fawkes::ArgumentParser::program_name
const char * program_name() const
Get name of program.
Definition: argparser.cpp:491
fawkes::ArgumentParser::ArgumentParser
ArgumentParser(int argc, char **argv, const char *opt_string, option *long_options=NULL)
Constructor.
Definition: argparser.cpp:95
fawkes
fawkes::ArgumentParser::arg
const char * arg(const char *argn)
Get argument value.
Definition: argparser.cpp:183
fawkes::ArgumentParser::parse_float
double parse_float(const char *argn)
Parse argument as double.
Definition: argparser.cpp:390
fawkes::ArgumentParser::num_items
std::vector< const char * >::size_type num_items() const
Get number of non-option items.
Definition: argparser.cpp:464
fawkes::ArgumentParser::parse_item_int
long int parse_item_int(unsigned int index)
Parse item as integer.
Definition: argparser.cpp:413