libassa  3.5.1
CmdLineOpts.cpp
Go to the documentation of this file.
1 // -*- c++ -*-
2 //------------------------------------------------------------------------------
3 // $Id: CmdLineOpts.cpp,v 1.7 2007/05/14 19:19:50 vlg Exp $
4 //------------------------------------------------------------------------------
5 // CmdLineOpts.cpp
6 //------------------------------------------------------------------------------
7 // Copyright (C) 2000,2005,2007 Vladislav Grinchenko
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Library General Public
11 // License as published by the Free Software Foundation; either
12 // version 2 of the License, or (at your option) any later version.
13 //------------------------------------------------------------------------------
14 #include <errno.h>
15 #include <string.h>
16 #include <stdlib.h>
17 
18 #include <sstream>
19 #include <iomanip>
20 
21 #include "assa/Logger.h"
22 #include "assa/CmdLineOpts.h"
23 #include "assa/CommonUtils.h"
24 #include "assa/IniFile.h"
25 
26 using namespace ASSA;
27 
28 void
30 dump () const
31 {
32  std::ostringstream msg;
33 
34  if (m_short_name != 0) {
35  msg << "-" << m_short_name << ", ";
36  }
37  else {
38  msg << " ";
39  }
40 
41  if (m_long_name.size ()) {
42  msg << "--" << std::setiosflags (std::ios::left)
43  << std::setw(14) << m_long_name.c_str () << ' ';
44  }
45  else {
46  msg << std::setiosflags (std::ios::left) << std::setw (14) << " ";
47  }
48  msg << '[';
49 
50  switch (m_type)
51  {
52  case Option::string_t:
53  msg << std::setiosflags (std::ios::left) << std::setw(7) << "string";
54  msg << "] = '" << *(string*) m_val << "'";
55  break;
56 
57  case Option::int_t:
58  msg << std::setiosflags(std::ios::left) << std::setw(7) << "int";
59  msg << "] = " << *(int*) m_val;
60  break;
61 
62  case Option::uint_t:
63  msg << std::setiosflags(std::ios::left) << std::setw(7) << "u_int";
64  msg << "] = " << *(int*) m_val;
65  break;
66 
67  case Option::long_t:
68  msg << std::setiosflags(std::ios::left) << std::setw(7) << "long";
69  msg << "] = " << *(long*) m_val;
70  break;
71 
72  case Option::ulong_t:
73  msg << std::setiosflags(std::ios::left) << std::setw(7) << "u_long";
74  msg << "] = " << *(long*) m_val;
75  break;
76 
77  case Option::double_t:
78  msg << std::setiosflags(std::ios::left) << std::setw(7) << "double";
79  msg << "] = " << *(double*) m_val;
80  break;
81 
82  case Option::float_t:
83  msg << std::setiosflags(std::ios::left) << std::setw(7) << "float";
84  msg << "] = " << *(float*) m_val;
85  break;
86 
87  case Option::flag_t:
88  msg << std::setiosflags(std::ios::left) << std::setw(7) << "bool";
89  msg << "] = " << *(bool*) m_val ? "true" : "false";
90  break;
91 
92  case Option::func_t:
93  msg << std::setiosflags(std::ios::left)
94  << std::setw(7) << "function ()";
95  msg << ']';
96  break;
97 
98  case Option::func_one_t:
99  msg << std::setiosflags(std::ios::left)
100  << std::setw(7) << "function (opt)";
101  msg << ']';
102  break;
103 
104  case Option::none_t:
105  msg << std::setiosflags(std::ios::left) << std::setw(7) << "none";
106  msg << ']';
107  break;
108 
109  default:
110  msg << std::setiosflags(std::ios::left)
111  << std::setw(7) << "--undef--";
112  msg << ']';
113  }
114  msg << std::ends;
115  DL((CMDLINEOPTS,"%s\n", msg.str ().c_str ()));
116 }
117 
118 const char*
120 type_c_str ()
121 {
122  const char* ret;
123 
124  switch (m_type)
125  {
126  case Option::string_t: ret = "string"; break;
127  case Option::int_t: ret = "int"; break;
128  case Option::uint_t: ret = "u_int"; break;
129  case Option::long_t: ret = "long"; break;
130  case Option::ulong_t: ret = "u_long"; break;
131  case Option::double_t: ret = "double"; break;
132  case Option::float_t: ret = "float"; break;
133  case Option::flag_t: ret = "bool"; break;
134  case Option::func_t: ret = "func()"; break;
135  case Option::func_one_t: ret = "func(opt)"; break;
136  case Option::none_t: ret = "none"; break;
137  default: ret = "--undef--";
138  }
139  return (ret);
140 }
141 
142 /*----------------------------------------------------------------------------*/
143 bool
145 is_valid (const char sopt_, const string& lopt_)
146 {
147  trace_with_mask ("CmdLineOpts::is_valid", CMDLINEOPTS);
148 
149  set_error_none ();
150  OptionSet::const_iterator i;
151 
152  for (i = m_opts_set.begin (); i != m_opts_set.end (); i++) {
153  if (sopt_ == '\0' && lopt_.empty ()) {
154  m_error = "Ignore empty option";
155  return (false);
156  }
157  else if (sopt_ != '\0' && i->m_short_name == sopt_) {
158  m_error = "Ignored multiple option '-";
159  m_error += sopt_ + string ("'");
160  return (false);
161  }
162  else if (!lopt_.empty () && i->m_long_name == lopt_) {
163  m_error = "Ignore multiple option '--";
164  m_error += lopt_ + string ("'");
165  return (false);
166  }
167  }
168  return (true);
169 }
170 
171 Option*
173 find_option (const char* str_)
174 {
175  trace_with_mask ("CmdLineOpts::find_option(char*)", CMDLINEOPTS);
176 
177  OptionSet::iterator i;
178 
179  for ( i = m_opts_set.begin (); i != m_opts_set.end (); i++)
180  {
181  if (i->m_long_name == str_) {
182  return &(*i);
183  }
184  }
185  return (NULL);
186 }
187 
188 Option*
190 find_option (const char letter_)
191 {
192  trace_with_mask ("CmdLineOpts::find_option(char)", CMDLINEOPTS);
193 
194  OptionSet::iterator i;
195 
196  for (i = m_opts_set.begin (); i != m_opts_set.end (); i++)
197  {
198  if (i->m_short_name == letter_)
199  return &(*i);
200  }
201  return (NULL);
202 }
203 
204 bool
206 add_flag_opt (const char sopt_, const string& lopt_, bool* v_)
207 {
208  trace_with_mask ("CmdLineOpts::add_flag_opt", CMDLINEOPTS);
209 
210  if (!is_valid (sopt_, lopt_))
211  return (false);
212 
213  Option o (sopt_, lopt_, Option::flag_t, (void*) v_);
214  m_opts_set.push_back (o);
215  return (true);
216 }
217 
218 bool
220 add_opt (const char sopt_, const string& lopt_, string* v_)
221 {
222  trace_with_mask ("CmdLineOpts::add_opt(string*)", CMDLINEOPTS);
223 
224  if (!is_valid (sopt_, lopt_))
225  return (false);
226 
227  Option o (sopt_, lopt_, Option::string_t, (void*) v_);
228  m_opts_set.push_back (o);
229  return (true);
230 }
231 
232 bool
234 add_opt (const char sopt_, const string& lopt_, int* v_)
235 {
236  trace_with_mask ("CmdLineOpts::add_opt(int*)", CMDLINEOPTS);
237 
238  if (!is_valid (sopt_, lopt_)) {
239  return (false);
240  }
241  Option o (sopt_, lopt_, Option::int_t, (void*) v_);
242  m_opts_set.push_back (o);
243  return (true);
244 }
245 
246 bool
248 add_opt (const char sopt_, const string& lopt_, unsigned int* v_)
249 {
250  trace_with_mask ("CmdLineOpts::add_opt(u_int*)", CMDLINEOPTS);
251 
252  if (!is_valid (sopt_, lopt_)) {
253  return (false);
254  }
255  Option o (sopt_, lopt_, Option::uint_t, (void*) v_);
256  m_opts_set.push_back (o);
257  return (true);
258 }
259 
260 bool
262 add_opt (const char sopt_, const string& lopt_, long* v_)
263 {
264  trace_with_mask ("CmdLineOpts::add_opt(long*)", CMDLINEOPTS);
265 
266  if (!is_valid (sopt_, lopt_)) {
267  return (false);
268  }
269  Option o (sopt_, lopt_, Option::long_t, (void*) v_);
270  m_opts_set.push_back (o);
271  return (true);
272 }
273 
274 bool
276 add_opt (const char sopt_, const string& lopt_, unsigned long* v_)
277 {
278  trace_with_mask ("CmdLineOpts::add_opt(u_long*)", CMDLINEOPTS);
279 
280  if (!is_valid (sopt_, lopt_)) {
281  return (false);
282  }
283  Option o (sopt_, lopt_, Option::long_t, (void*) v_);
284  m_opts_set.push_back (o);
285  return (true);
286 }
287 
288 bool
290 add_opt (const char sopt_, const string& lopt_, double* v_)
291 {
292  trace_with_mask ("CmdLineOpts::add_opt(double*)", CMDLINEOPTS);
293 
294  if (!is_valid (sopt_, lopt_)) {
295  return (false);
296  }
297  Option o (sopt_, lopt_, Option::double_t, (void*) v_);
298  m_opts_set.push_back (o);
299  return (true);
300 }
301 
302 bool
304 add_opt (const char sopt_, const string& lopt_, float* v_)
305 {
306  trace_with_mask ("CmdLineOpts::add_opt(float*)", CMDLINEOPTS);
307 
308  if (!is_valid (sopt_, lopt_)) {
309  return (false);
310  }
311  Option o (sopt_, lopt_, Option::float_t, (void*) v_);
312  m_opts_set.push_back (o);
313  return (true);
314 }
315 
316 bool
318 add_opt (const char sopt_, const string& lopt_, OPTS_FUNC v_)
319 {
320  trace_with_mask ("CmdLineOpts::add_opt(OPTS_FUNC)", CMDLINEOPTS);
321 
322  if (!is_valid (sopt_, lopt_)) {
323  return (false);
324  }
325  Option o (sopt_, lopt_, Option::func_t, (void*) v_);
326  m_opts_set.push_back (o);
327  return (true);
328 }
329 
330 bool
332 add_opt (const char sopt_, const string& lopt_, OPTS_FUNC_ONE v_)
333 {
334  trace_with_mask ("CmdLineOpts::add_opt(OPTS_FUNC_ONE)", CMDLINEOPTS);
335 
336  if (!is_valid (sopt_, lopt_)) {
337  return (false);
338  }
339  Option o (sopt_, lopt_, Option::func_one_t, (void*) v_);
340  m_opts_set.push_back (o);
341  return (true);
342 }
343 
344 bool
346 rm_opt (const char sopt_, const string& lopt_)
347 {
348  trace_with_mask ("CmdLineOpts::rm_opt(string&)", CMDLINEOPTS);
349 
350  OptionSet::iterator i;
351 
352  for (i = m_opts_set.begin (); i != m_opts_set.end (); i++)
353  {
354  if (i->m_short_name == sopt_ || i->m_long_name == lopt_)
355  {
356  m_opts_set.erase (i);
357  return (true);
358  }
359  }
360  return (false);
361 }
362 
363 bool
365 parse_args (const char* argv_[])
366 {
367  trace_with_mask ("CmdLineOpts::parse_args", CMDLINEOPTS);
368 
369  register int skip = 1;
370  bool pos_args_started = false;
371  string param ("");
372  string token ("");
373  set_error_none ();
374  Option* node = (Option*) NULL;
375 
376  for (argv_++; argv_[0]; argv_ += skip) {
377  if (skip != 0) {
378  token = argv_[0];
379  }
380 
381  DL((CMDLINEOPTS, "token: \"%s\"\n", token.c_str()));
382 
383  if (pos_args_started) {
384  DL((CMDLINEOPTS,"pos_args_started = true\n"));
385 
386  if (token[0] == '-' && token.size () != 1) {
387  m_error = "Invalid order of arguments: '";
388  m_error += token + "'.";
389  goto done;
390  }
391  pos_arg (token.c_str ());
392  continue;
393  }
394  skip = 1;
395 
396  if (token[0] == '-' && token.size () > 1 && token[1] != '-') {
397  if (token.size () == 1 && !pos_args_started) {
398  pos_arg (token.c_str ());
399  pos_args_started = true;
400  continue;
401  }
402 
403  if ((node = find_option (token[1])) != NULL) {
404  if (token.size () > 2) {
405  if (node->m_type == Option::flag_t ||
406  node->m_type == Option::func_t)
407  {
408  token.erase (1, 1);
409  skip = 0;
410  }
411  else {
412  param = token.substr (2);
413  }
414  } // if (token.size()>2)
415  } // if ((node = find_option ())
416  }
417  else {
418  if (token.size () > 1 && token[1] == '-') {
419  string op = token.substr (2);
420  size_t pos;
421 
422  if ((pos = op.find ("=")) != (size_t)-1) {
423  param = op.substr (pos+1, op.length ());
424  op.replace (pos, op.length() - pos, "");
425  }
426  node = find_option (op.c_str ());
427  }
428  else {
429  pos_arg (token.c_str ());
430  pos_args_started = true;
431  continue;
432  }
433  } // if (token[0] == '-' && token[1] != '-')
434 
435  if (!node) {
436  m_error = "Invalid option '" + token + "'.";
437  goto done;
438  }
439 
440  if (node->m_type != Option::flag_t &&
441  node->m_type != Option::func_t)
442  {
443  if (param.empty ()) {
444  if (!argv_[1]) {
445  m_error = "Expecting parameter after '"
446  + string (argv_[0]) + "'.";
447  goto done;
448  }
449  param = argv_[1];
450  skip = 2;
451  }
452  }
453  /*---
454  * if positional arguments only
455  ---*/
456  if (!node) {
457  goto done;
458  }
459 
460  if (param.empty ()) {
461  if (!assign (node, argv_[1])) {
462  return (false);
463  }
464  }
465  else {
466  const char* str = param.c_str ();
467  if (!assign (node, str)) {
468  return (false);
469  }
470  param = "";
471  }
472  } // for (argv_++; argv_[0]; argv_ += skip)
473 
474  done:
475  return !m_error.empty () ? false : true;
476 }
477 
490 int
492 parse_config_file (IniFile& inifile_)
493 {
494  trace_with_mask ("CmdLineOpts::parse_config_file", CMDLINEOPTS);
495 
496  unsigned int count = 0;
497  string v;
498  string s;
499  string optsect_name ("options");
500  OptionSet::iterator pos = m_opts_set.begin ();
501 
502  if (inifile_.find_section (optsect_name) == inifile_.sect_end ())
503  {
504  optsect_name = "Options";
505  if (inifile_.find_section (optsect_name) == inifile_.sect_end ())
506  {
507  optsect_name = "OPTIONS";
508  if (inifile_.find_section (optsect_name) == inifile_.sect_end ())
509  {
510  m_error = "Missing [options] section in INI file!";
511  return -1;
512  }
513  }
514  }
515 
516  while (pos != m_opts_set.end ()) {
517  if (pos->m_long_name.size ()) {
518  s = pos->m_long_name;
520  DL ((CMDLINEOPTS, "trying option \"%s\"\n", s.c_str ()));
521  v = inifile_.get_value (optsect_name, s);
522  if (v.size ()) {
523  if (assign (&(*pos), v.c_str ())) {
524  count++;
525  }
526  }
527  }
528  pos++;
529  }
530 
531  return (count);
532 }
533 
534 bool
536 assign (Option* node_, const char* op_)
537 {
538  trace_with_mask ("CmdLineOpts::assign", CMDLINEOPTS);
539 
540  long l;
541  double d;
542 
543  if (node_ && op_) {
544  DL ((CMDLINEOPTS, "Assign '%s' to {-%c, --%s, t=%s}\n",
545  op_, node_->m_short_name, node_->m_long_name.c_str (),
546  node_->type_c_str ()));
547  }
548 
549  /*---
550  From strtol(3C) man page:
551 
552  "Because 0 is returned on error and is also a valid return on
553  success, an application wishing to check for error situations
554  should set 'errno' to 0, then call strtol(3C), then check 'errno'
555  and if it is non-zero, assume an error has occured."
556  ---*/
557 
558  switch (node_->m_type) {
559  case Option::string_t:
560  *(string*) node_->m_val = op_;
561  break;
562 
563  case Option::int_t:
564  case Option::long_t:
565  errno = 0;
566  l = strtol (op_, NULL, 0);
567 
568  if (errno != 0) {
569  m_error = "Error: '" + string (strerror (errno)) + "',";
570  m_error += " in converting to integer from '";
571  m_error += string (op_) + "'.";
572  return (false);
573  }
574 
575  if (node_->m_type == Option::int_t) {
576  *(int*) node_->m_val = int (l);
577  }
578  else {
579  *(long*) node_->m_val = l;
580  }
581  break;
582 
583  case Option::uint_t:
584  case Option::ulong_t:
585  errno = 0;
586  l = strtol (op_, NULL, 0);
587 
588  if (errno != 0) {
589  m_error = "Error: '" + string (strerror (errno)) + "',";
590  m_error += " in converting to unsigned integer from '";
591  m_error += string (op_) + "'.";
592  return (false);
593  }
594 
595  if (node_->m_type == Option::uint_t) {
596  *(unsigned int*) node_->m_val = int (l);
597  }
598  else {
599  *(unsigned long*) node_->m_val = l;
600  }
601  break;
602 
603  case Option::double_t:
604  case Option::float_t:
605  errno = 0;
606  d = strtod (op_, NULL);
607 
608  if (errno != 0) {
609  m_error = "Error: '" + string (strerror (errno)) + "',";
610  m_error += " in converting to double/float from '";
611  m_error += string (op_) + "'.";
612  return (false);
613  }
614 
615  if (node_->m_type == Option::double_t) {
616  *(double*) node_->m_val = d;
617  }
618  else {
619  *(float*) node_->m_val = float (d);
620  }
621  break;
622 
623  case Option::flag_t:
624  *(bool*) node_->m_val = true; // no more flipping!
625  break;
626 
627  case Option::func_t:
628  (*(OPTS_FUNC)(node_->m_val)) ();
629  break;
630 
631  case Option::func_one_t:
632  (*(OPTS_FUNC_ONE)(node_->m_val)) (op_);
633  break;
634 
635  case Option::none_t:
636  default:
637  m_error = "Undefined type for option '"+string (op_)+"'.";
638  return (false);
639  } /*-- switch () --*/
640 
641  return (true);
642 }
643 
644 void
646 dump () const
647 {
648  OptionSet::const_iterator i;
649 
650  for (i = m_opts_set.begin (); i != m_opts_set.end (); i++) {
651  i->dump ();
652  }
653 
654  if (!m_error.empty ()) {
655  DL((CMDLINEOPTS, "Last error: '%s'\n", m_error.c_str ()));
656  }
657 }
658 
659 void
661 str_to_argv (const string& src_, int& argc_, char**& argv_)
662 {
663  trace_with_mask ("CmdLineOpts::str_to_argv", CMDLINEOPTS);
664 
665  std::vector<string> vs;
666  std::istringstream input (src_);
667  std::string token;
668 
669  while (input >> token) {
670  vs.push_back (token);
671  token = "";
672  }
673  int i = 0;
674  char* p;
675 
676  if (vs.size ()) {
677  argv_ = new char* [vs.size() + 1];
678  std::vector<string>::iterator it;
679 
680  for (it = vs.begin (); it != vs.end (); it++, i++) {
681  p = new char [it->size() + 1];
682  strcpy (p, it->c_str ());
683  p[it->size()] = '\0';
684  argv_[i] = p;
685  }
686  argv_[i] = NULL;
687  }
688  argc_ = i;
689 }
690 
691 void
693 free_argv (char**& argv_)
694 {
695  trace_with_mask ("CmdLineOpts::free_argv", CMDLINEOPTS);
696 
697  /* If argument is empty (which should never be the case),
698  * then freeing the memory would core dump application.
699  */
700  if (argv_ == NULL) {
701  return;
702  }
703 
704  for (int i = 0; argv_[i]; i++) {
705  delete [] argv_[i];
706  }
707  delete [] argv_;
708  argv_ = NULL;
709 }
710 
Class to handle processing command-line options.
A Windows-style INI configuration file management class.
An abstraction to message logging facility.
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition: Logger.h:437
Option * find_option(const char *str_)
Locate option in the options set.
string m_error
Last reported error.
Definition: CmdLineOpts.h:287
bool parse_args(const char *argv[])
Parse command line arguments based on installed options set.
void(* OPTS_FUNC)(void)
Definition: CmdLineOpts.h:116
void(* OPTS_FUNC_ONE)(const string &)
Definition: CmdLineOpts.h:117
void set_error_none()
Reset error message to an empty string.
Definition: CmdLineOpts.h:302
bool add_opt(const char c, const string &s, string *str)
Add an option with STL string argument.
bool rm_opt(const char c_, const string &s_)
Remove option for the option list.
OptionSet m_opts_set
Options set.
Definition: CmdLineOpts.h:284
bool add_flag_opt(const char c, const string &s, bool *f)
Add binary flag option.
bool assign(Option *node_, const char *op_)
Perform value assignment to the node.
int parse_config_file(IniFile &inifile_)
Parse configuration parameters found in [options] section of the INI file.
virtual void pos_arg(const char *arg_)
Process positional argument arg_.
Definition: CmdLineOpts.h:292
static void str_to_argv(const string &src_, int &argc_, char **&argv_)
Static function.
void dump() const
Write options set to the log file.
static void free_argv(char **&argv_)
Free up memory allocated by str_to_argv() function
bool is_valid(const char sopt_, const string &lopt_)
Detect if supplied option is valid.
config_iterator find_section(const string &section_)
Find section by its name.
config_iterator sect_end()
Return iterator past the last section.
Definition: IniFile.h:174
string get_value(const string &section_, const string &name_) const
Find and return a value of the name/value pair in the section section_.
Option class.
Definition: CmdLineOpts.h:39
@ func_one_t
Convert argument to function with one argument
Definition: CmdLineOpts.h:57
@ func_t
Convert argument to function
Definition: CmdLineOpts.h:56
@ string_t
Convert argument to STL string
Definition: CmdLineOpts.h:48
@ ulong_t
Convert argument to unsigned long
Definition: CmdLineOpts.h:52
@ float_t
Convert argument to float
Definition: CmdLineOpts.h:54
@ long_t
Convert argument to long
Definition: CmdLineOpts.h:51
@ double_t
Convert argument to double
Definition: CmdLineOpts.h:53
@ flag_t
No argument; bool value is flipped.
Definition: CmdLineOpts.h:55
@ int_t
Convert argument to int
Definition: CmdLineOpts.h:49
@ uint_t
Convert argument to unsigned int
Definition: CmdLineOpts.h:50
type_t m_type
Option type.
Definition: CmdLineOpts.h:82
string m_long_name
Long option name.
Definition: CmdLineOpts.h:79
void * m_val
Pointer to the option value.
Definition: CmdLineOpts.h:85
void dump() const
Write object state to the log file.
Definition: CmdLineOpts.cpp:30
const char * type_c_str()
Return the type of the Option object.
char m_short_name
One-letter option name.
Definition: CmdLineOpts.h:76
void find_and_replace_char(std::string &text_, char src_, char dest_)
Find and relpace all instances of src_ character with dest_ character in a string text_.
Definition: Acceptor.h:40
@ CMDLINEOPTS
Class CmdLineOpts messages
Definition: LogMask.h:36
Socket & ends(Socket &os_)
ends manipulator.
Definition: Socket.h:622