HPCToolkit
CmdLineParser.hpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*-
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //***************************************************************************
48 //
49 // File:
50 // $HeadURL$
51 //
52 // Nathan Tallent
53 //
54 // Purpose:
55 // [The purpose of this file]
56 //
57 // Description:
58 // [The set of functions, macros, etc. defined in the file]
59 //
60 //***************************************************************************
61 
62 #ifndef support_CmdLineParser_hpp
63 #define support_CmdLineParser_hpp
64 
65 //************************* System Include Files ****************************
66 
67 #include <iostream>
68 #include <map>
69 #include <vector>
70 #include <string>
71 
72 #include <stdint.h>
73 
74 //*************************** User Include Files ****************************
75 
76 #include <include/gcc-attr.h>
77 
78 #include "diagnostics.h"
79 
80 //*************************** Forward Declarations ***************************
81 
82 //****************************************************************************
83 
84 //****************************************************************************
85 // CmdLineParser
86 //****************************************************************************
87 
88 // CmdLineParser: Parses arguments on the command line, argc and argv.
89 // Provides easy access to both optional arguments (with short or long
90 // specifiers) and regular arguments. Provides functionality similar
91 // to getopt() and GNU's getopt_long(), but in an easier to use -- and
92 // in the case of getopt_long(), more portable -- package. In
93 // addition, the package provides configurable handling of duplicate
94 // options/arguments and routines to convert string arguments into
95 // numerical types.
96 //
97 // A user creates a NULL-terminated array of option descriptors
98 // (OptArgDesc) indicating switch names and arguments, if any. He
99 // then instantiates a CmdLineParser and parses argc/argv. Errors are
100 // delivered with the exception Exception. The parser object provides
101 // access to all optional and regular arguments using the interface
102 // routines belows.
103 //
104 // More details:
105 // The command line generally has the form (but note qualifications below):
106 // <command> [arguments]
107 //
108 // <argument> ::= | <switch> | <switch_arg> | <switch_optarg> | <word>
109 //
110 // <switch> ::= -f | --foo (<short_switch> | <long_switch>)
111 //
112 // <switch_arg> ::= -f arg | -farg | --foo arg | --foo=arg
113 //
114 // <switch_optarg> ::= -f [arg] | -f[arg] | --foo [arg] | --foo[=arg]
115 //
116 // An element of argv that starts with '-' or '--' (and is not exactly
117 // '--') signifies an option. The option switch is the text without
118 // the initial dashes. As the above shows, we support a variety of
119 // option styles.
120 //
121 // Features:
122 // - The '--' token forces the end of optional argument scanning and
123 // iterprets everything following as a list of regular arguments.
124 // This is useful for non-option arguments that begin with dashes.
125 // - Unlike getopt() we do not support the deprecated use of the single
126 // '-'; this is an error.
127 // - Long argument switches may be abbreviated if the abbreviation
128 // is unique.
129 // - Configurable handling of duplicate options and arguments.
130 //
131 // Limitations:
132 // - Unlike getopt(), we do not currently support short switch grouping,
133 // e.g. using -abc instead of -a -b -c. [FIXME: we can assume that
134 // only options without arguments are allowed to be grouped.]
135 //
136 // Warnings:
137 // *** NOTE: the following should now be resolved with IsOptArg_fn ***
138 //
139 // - Switches that take optional arguments can be confusing. For
140 // example, assume a command 'foo' takes a filename and on option,
141 // --debug, which itself takes an optional debug level. The
142 // following commands pose no difficulties:
143 // foo myfile
144 // foo --debug=3 myfile
145 // foo --debug 3 myfile
146 // foo myfile --debug
147 // However, in the following
148 // foo --debug myfile
149 // 'myfile' is erroneously assumed to be the optional argument to
150 // --debug. While the '--' token solves this, it remains awkward.
151 //
153 public:
154 
155  // ---------------------------------------------------------
156  // Structure used to describe command line options
157  // ---------------------------------------------------------
158 
159  // Describes if an option switch takes an argument
160  enum OptKind {
161  ARG_NULL = 0,
162  ARG_NONE, // switch does not take argument
163  ARG_REQ, // switch must take an argument
164  ARG_OPT // switch takes an (optional!) argument
165  };
166 
167  // Describes how to handle duplicate options and option arguments
168  enum DupOptKind {
170  DUPOPT_ERR, // throw an exception for duplicate option or argument
171  DUPOPT_CLOB, // clobber any previous argument
172  DUPOPT_CAT // concat all available arguments using 'dupArgSep'
173  };
174 
175 
176  // A callback for disambiguating between a switch's potential
177  // optional argument and an actual argument; cf. Warnings above.
178  // INVARIANT: 'str' is non-NULL
179  typedef bool (*IsOptArg_fn_t)(const char* str);
180 
181  struct OptArgDesc {
182 
183  bool operator==(const OptArgDesc& x) const
184  {
185  return (swShort == x.swShort && swLong == x.swLong
186  && kind == x.kind && dupKind == x.dupKind
187  && dupArgSep == x.dupArgSep);
188  }
189 
190  bool
191  operator!=(const OptArgDesc& x) const
192  { return !(*this == x); }
193 
194  // Data
195  char swShort; // 0 if n/a
196  const char* swLong; // NULL if n/a
199  const char* dupArgSep; // separator for 'DUPARG_CONCAT'
200  IsOptArg_fn_t isOptArgFn; // NULL for default handler
201  };
202 
203 
204  // The NULL terminator (two versions). The use of the first version
205  // is preferable, but some older compilers won't support it.
207 # define CmdLineParser_OptArgDesc_NULL_MACRO \
208  { 0, NULL, CmdLineParser::ARG_NULL, CmdLineParser::DUPOPT_NULL, NULL }
209 
210 
211  // ---------------------------------------------------------
212  // Exception thrown when errors are encountered
213  // ---------------------------------------------------------
214 
216  public:
217  Exception(const char* x,
218  const char* filenm = NULL, unsigned int lineno = 0)
219  : Diagnostics::Exception(x, filenm, lineno)
220  { }
221 
222  Exception(std::string x,
223  const char* filenm = NULL, unsigned int lineno = 0)
224  : Diagnostics::Exception(x, filenm, lineno)
225  { }
226 
228  };
229 
230  class ParseError : public Exception {
231  public:
232  ParseError(const char* x,
233  const char* filenm = NULL, unsigned int lineno = 0)
234  : Exception(x, filenm, lineno)
235  { }
236 
237  ParseError(std::string x,
238  const char* filenm = NULL, unsigned int lineno = 0)
239  : Exception(x, filenm, lineno)
240  { }
241 
242  virtual std::string message() const {
243  return "[CmdLineParser::ParseError]: " + what();
244  }
245  };
246 
247  class InternalError : public Exception {
248  public:
249  InternalError(const char* x,
250  const char* filenm = NULL, unsigned int lineno = 0)
251  : Exception(x, filenm, lineno)
252  { }
253 
254  InternalError(std::string x,
255  const char* filenm = NULL, unsigned int lineno = 0)
256  : Exception(x, filenm, lineno)
257  { }
258 
259  virtual std::string message() const {
260  return "[CmdLineParser::InternalError]: " + what();
261  }
262  };
263 
264  // ---------------------------------------------------------
265 
266 public:
267  // ---------------------------------------------------------
268  // Constructor/Destructor
269  // ---------------------------------------------------------
270  CmdLineParser();
271  CmdLineParser(const OptArgDesc* optArgDescs,
272  int argc, const char* const argv[]);
273  ~CmdLineParser();
274 
275  // -------------------------------------------------------
276  // Parsing
277  // -------------------------------------------------------
278 
279  // Parse: Given a NULL terminated array of OptArgDesc describing
280  // command line arguments, parses the argv/argc into switches,
281  // optional and required arguments.
282  void
283  parse(const OptArgDesc* optArgDescs,
284  int argc, const char* const argv[]);
285 
286  // -------------------------------------------------------
287  // Parsed Data: Command
288  // -------------------------------------------------------
289 
290  // GetCmd: The command (will be valid even after a parse error)
291  const std::string&
292  getCmd() const;
293 
294  // -------------------------------------------------------
295  // Parsed Data: Optional arguments
296  // -------------------------------------------------------
297 
298  // isOpt: (isOption) Given a short or long switch, returns whether
299  // the switch has been seen.
300  bool
301  isOpt(const char swShort) const;
302 
303  bool
304  isOpt(const char* swLong) const;
305 
306  bool
307  isOpt(const std::string& sw) const;
308 
309  // isOptArg: (isOptionArgument) Given a short or long switch,
310  // returns whether an argument is associated with it. Designed for
311  // switches that optionally take arguments.
312  bool
313  isOptArg(const char swShort) const;
314 
315  bool
316  isOptArg(const char* swLong) const;
317 
318  bool
319  isOptArg(const std::string& sw) const;
320 
321  // getOptArg: (GetOptionArgument) Given a short or long switch, get
322  // the argument associated with it. Assumes user has verified that
323  // an argument *exists*.
324  const std::string&
325  getOptArg(const char swShort) const;
326 
327  const std::string&
328  getOptArg(const char* swLong) const;
329 
330  const std::string&
331  getOptArg(const std::string& sw) const;
332 
333  // -------------------------------------------------------
334  // Parsed Data: Arguments
335  // -------------------------------------------------------
336  unsigned int
337  getNumArgs() const;
338 
339  const std::string&
340  getArg(unsigned int i) const;
341 
342  // -------------------------------------------------------
343  // Convert strings into other formats
344  // -------------------------------------------------------
345  // The input should be non-empty
346  static long
347  toLong(const std::string& str);
348 
349  static uint64_t
350  toUInt64(const std::string& str);
351 
352  static double
353  toDbl(const std::string& str);
354 
355  // ---------------------------------------------------------
356  // Disambiguate optional arguments to a switch
357  // ---------------------------------------------------------
358  static bool
359  isOptArg_long(const char* option);
360 
361  // ---------------------------------------------------------
362  // Convenience routines for interpreting the value of an option
363  // ---------------------------------------------------------
364  static bool
365  parseArg_bool(const std::string& value, const char* errTag);
366 
367  // -------------------------------------------------------
368  // Misc
369  // -------------------------------------------------------
370  void
371  dump(std::ostream& os = std::cerr) const;
372 
373  void
374  ddump() const;
375 
376 private:
377  // Should not be used
379  { }
380 
383  { return *this; }
384 
385  typedef std::map<std::string, std::string*> SwitchToArgMap;
386  typedef std::vector<std::string> ArgVec;
387 
388  // Switch descriptor (Because of limited use, we allow this to be
389  // returned as an object)
390  class SwDesc {
391  public:
393  : isLong(false)
394  { }
395 
396  SwDesc(const char* sw_, bool isLong_, const char* arg_)
397  : sw(sw_), isLong(isLong_), arg(arg_)
398  { }
399 
400  SwDesc(const std::string& sw_, bool isLong_, const std::string& arg_)
401  : sw(sw_), isLong(isLong_), arg(arg_)
402  { }
403 
404  ~SwDesc() { }
405 
406  // use default copy constructor if necessary
407 
408  std::string sw; // switch text without dashes
409  bool isLong; // long style
410  std::string arg; // any argument
411  };
412 
413 private:
414  void
415  Ctor();
416 
417  void
418  reset();
419 
420  void
421  checkForErrors(const OptArgDesc* optArgDescs);
422 
423  const OptArgDesc*
424  createSortedCopy(const OptArgDesc* optArgDescs);
425 
426  // Parsing helpers
427  SwDesc
428  makeSwitchDesc(const char* str);
429 
430  const OptArgDesc*
431  findOptDesc(const OptArgDesc* optArgDescs, const SwDesc& swdesc,
432  bool errOnMultipleMatches = true);
433 
434  void
435  addOption(const OptArgDesc& odesc, const SwDesc& swdesc);
436 
437  void
438  addOption(const OptArgDesc& odesc,
439  const std::string& sw, const std::string& arg);
440 
441 private:
442  std::string command; // comand name
443  SwitchToArgMap switchToArgMap; // optional arguments
444  ArgVec arguments; // regular arguments
445 };
446 
447 //****************************************************************************
448 
449 #endif // support_CmdLineParser_hpp
450 
bool(* IsOptArg_fn_t)(const char *str)
bool isOpt(const char swShort) const
static OptArgDesc OptArgDesc_NULL
virtual std::string message() const
const OptArgDesc * createSortedCopy(const OptArgDesc *optArgDescs)
const std::string & getOptArg(const char swShort) const
const std::string & getCmd() const
static uint64_t toUInt64(const std::string &str)
CmdLineParser & operator=(const CmdLineParser &GCC_ATTR_UNUSED x)
static double toDbl(const std::string &str)
bool isOptArg(const char swShort) const
CmdLineParser(const CmdLineParser &GCC_ATTR_UNUSED x)
void parse(const OptArgDesc *optArgDescs, int argc, const char *const argv[])
std::map< std::string, std::string * > SwitchToArgMap
bool operator==(const OptArgDesc &x) const
bool operator!=(const OptArgDesc &x) const
unsigned int getNumArgs() const
static bool isOptArg_long(const char *option)
ParseError(std::string x, const char *filenm=NULL, unsigned int lineno=0)
ParseError(const char *x, const char *filenm=NULL, unsigned int lineno=0)
void dump(std::ostream &os=std::cerr) const
InternalError(const char *x, const char *filenm=NULL, unsigned int lineno=0)
void checkForErrors(const OptArgDesc *optArgDescs)
SwDesc(const char *sw_, bool isLong_, const char *arg_)
std::vector< std::string > ArgVec
const OptArgDesc * findOptDesc(const OptArgDesc *optArgDescs, const SwDesc &swdesc, bool errOnMultipleMatches=true)
SwDesc makeSwitchDesc(const char *str)
SwitchToArgMap switchToArgMap
const std::string & getArg(unsigned int i) const
Exception(const char *x, const char *filenm=NULL, unsigned int lineno=0)
#define NULL
Definition: ElfHelper.cpp:85
InternalError(std::string x, const char *filenm=NULL, unsigned int lineno=0)
Exception(std::string x, const char *filenm=NULL, unsigned int lineno=0)
std::string command
static bool parseArg_bool(const std::string &value, const char *errTag)
#define GCC_ATTR_UNUSED
Definition: gcc-attr.h:80
virtual std::string message() const
void ddump() const
static long toLong(const std::string &str)
void addOption(const OptArgDesc &odesc, const SwDesc &swdesc)
SwDesc(const std::string &sw_, bool isLong_, const std::string &arg_)