cprover
cmdline.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module:
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
9 #include "cmdline.h"
10 
11 #include <util/edit_distance.h>
12 #include <util/exception_utils.h>
13 #include <util/invariant.h>
14 
16 {
17 }
18 
20 {
21 }
22 
24 {
25  options.clear();
26  args.clear();
27 }
28 
29 bool cmdlinet::isset(char option) const
30 {
31  auto i=getoptnr(option);
32  if(i.has_value())
33  return options[*i].isset;
34  else
35  return false;
36 }
37 
38 bool cmdlinet::isset(const char *option) const
39 {
40  auto i=getoptnr(option);
41  if(i.has_value())
42  return options[*i].isset;
43  else
44  return false;
45 }
46 
47 std::string cmdlinet::get_value(char option) const
48 {
49  auto i=getoptnr(option);
50 
51  if(i.has_value() && !options[*i].values.empty())
52  return options[*i].values.front();
53  else
54  return "";
55 }
56 
57 void cmdlinet::set(const std::string &option, bool value)
58 {
59  auto i=getoptnr(option);
60 
61  if(i.has_value())
62  options[*i].isset = value;
63  else
64  {
66  "unknown command line option", option);
67  }
68 }
69 
70 void cmdlinet::set(const std::string &option, const std::string &value)
71 {
72  auto i=getoptnr(option);
73 
74  if(i.has_value())
75  {
76  options[*i].isset=true;
77  options[*i].values.push_back(value);
78  }
79  else
80  {
82  "unknown command line option", option);
83  }
84 }
85 
86 static std::list<std::string> immutable_empty_list;
87 
88 const std::list<std::string> &cmdlinet::get_values(char option) const
89 {
90  auto i=getoptnr(option);
91 
92  if(i.has_value())
93  return options[*i].values;
94  else
95  return immutable_empty_list;
96 }
97 
98 std::string cmdlinet::get_value(const char *option) const
99 {
100  auto i=getoptnr(option);
101 
102  if(i.has_value() && !options[*i].values.empty())
103  return options[*i].values.front();
104  else
105  return "";
106 }
107 
108 const std::list<std::string> &cmdlinet::get_values(
109  const std::string &option) const
110 {
111  auto i=getoptnr(option);
112 
113  if(i.has_value())
114  return options[*i].values;
115  else
116  return immutable_empty_list;
117 }
118 
119 std::list<std::string>
120 cmdlinet::get_comma_separated_values(const char *option) const
121 {
122  std::list<std::string> separated_values;
123  auto i = getoptnr(option);
124  if(i.has_value() && !options[*i].values.empty())
125  {
126  std::istringstream values_stream(options[*i].values.front());
127  std::string single_value;
128  while(std::getline(values_stream, single_value, ','))
129  {
130  separated_values.push_back(single_value);
131  }
132  }
133  return separated_values;
134 }
135 
137 {
138  for(std::size_t i=0; i<options.size(); i++)
139  if(options[i].optchar==option)
140  return i;
141 
142  return optionalt<std::size_t>();
143 }
144 
145 optionalt<std::size_t> cmdlinet::getoptnr(const std::string &option) const
146 {
147  for(std::size_t i=0; i<options.size(); i++)
148  if(options[i].optstring==option)
149  return i;
150 
151  return optionalt<std::size_t>();
152 }
153 
154 bool cmdlinet::parse(int argc, const char **argv, const char *optstring)
155 {
156  clear();
157 
158  parse_optstring(optstring);
159  return parse_arguments(argc, argv);
160 }
161 
163 {
164  return option_namest{*this};
165 }
166 void cmdlinet::parse_optstring(const char *optstring)
167 {
168  while(optstring[0] != 0)
169  {
170  optiont option;
171 
173  optstring[0] != ':', "cmdlinet::parse: Invalid option string\n");
174 
175  if(optstring[0] == '(')
176  {
177  option.islong = true;
178  option.optchar = 0;
179  option.isset = false;
180  option.optstring.clear();
181 
182  for(optstring++; optstring[0] != ')' && optstring[0] != 0; optstring++)
183  option.optstring += optstring[0];
184 
185  if(optstring[0] == ')')
186  optstring++;
187  }
188  else
189  {
190  option.islong = false;
191  option.optchar = optstring[0];
192  option.optstring.clear();
193  option.isset = false;
194 
195  optstring++;
196  }
197 
198  if(optstring[0] == ':')
199  {
200  option.hasval = true;
201  optstring++;
202  }
203  else
204  option.hasval = false;
205 
206  options.push_back(option);
207  }
208 }
209 
210 std::vector<std::string>
211 cmdlinet::get_argument_suggestions(const std::string &unknown_argument)
212 {
213  struct suggestiont
214  {
215  std::size_t distance;
216  std::string suggestion;
217 
218  bool operator<(const suggestiont &other) const
219  {
220  return distance < other.distance;
221  }
222  };
223 
224  auto argument_suggestions = std::vector<suggestiont>{};
225  // We allow 3 errors here. This can lead to the output being a bit chatty,
226  // which we mitigate by reducing suggestions to those with the minimum
227  // distance further down below
228  const auto argument_matcher = levenshtein_automatont{unknown_argument, 3};
229  for(const auto &option : options)
230  {
231  if(option.islong)
232  {
233  const auto long_name = "--" + option.optstring;
234  if(auto distance = argument_matcher.get_edit_distance(long_name))
235  {
236  argument_suggestions.push_back({distance.value(), long_name});
237  }
238  }
239  if(!option.islong)
240  {
241  const auto short_name = std::string{"-"} + option.optchar;
242  if(auto distance = argument_matcher.get_edit_distance(short_name))
243  {
244  argument_suggestions.push_back({distance.value(), short_name});
245  }
246  }
247  }
248 
249  auto final_suggestions = std::vector<std::string>{};
250  if(!argument_suggestions.empty())
251  {
252  // we only want to keep suggestions with the minimum distance
253  // because otherwise they become quickly too noisy to be useful
254  auto min = std::min_element(
255  argument_suggestions.begin(), argument_suggestions.end());
256  INVARIANT(
257  min != argument_suggestions.end(),
258  "there is a minimum because it's not empty");
259  for(auto const &suggestion : argument_suggestions)
260  {
261  if(suggestion.distance == min->distance)
262  {
263  final_suggestions.push_back(suggestion.suggestion);
264  }
265  }
266  }
267  return final_suggestions;
268 }
269 
270 bool cmdlinet::parse_arguments(int argc, const char **argv)
271 {
272  for(int i = 1; i < argc; i++)
273  {
274  if(argv[i][0] != '-')
275  args.push_back(argv[i]);
276  else
277  {
279 
280  if(argv[i][1] != 0 && argv[i][2] == 0)
281  optnr = getoptnr(argv[i][1]); // single-letter option -X
282  else if(argv[i][1] == '-')
283  optnr = getoptnr(argv[i] + 2); // multi-letter option with --XXX
284  else
285  {
286  // Multi-letter option -XXX, or single-letter with argument -Xval
287  // We first try single-letter.
288  optnr = getoptnr(argv[i][1]);
289 
290  if(!optnr.has_value()) // try multi-letter
291  optnr = getoptnr(argv[i] + 1);
292  }
293 
294  if(!optnr.has_value())
295  {
296  unknown_arg = argv[i];
297  return true;
298  }
299 
300  options[*optnr].isset = true;
301 
302  if(options[*optnr].hasval)
303  {
304  if(argv[i][2] == 0 || options[*optnr].islong)
305  {
306  i++;
307  if(i == argc)
308  return true;
309  if(argv[i][0] == '-' && argv[i][1] != 0)
310  return true;
311  options[*optnr].values.push_back(argv[i]);
312  }
313  else
314  options[*optnr].values.push_back(argv[i] + 2);
315  }
316  }
317  }
318  return false;
319 }
320 
322  const cmdlinet *command_line,
323  std::size_t index)
324  : command_line(command_line), index(index)
325 {
327 }
328 
331 {
332  PRECONDITION(command_line != nullptr);
333  ++index;
334  goto_next_valid_index();
335  return *this;
336 }
338 {
339  PRECONDITION(command_line != nullptr);
340  auto const &options = command_line->options;
341  return index < options.size() && options[index].isset &&
342  options[index].islong;
343 }
344 
346 {
347  PRECONDITION(command_line != nullptr);
348  while(index < command_line->options.size() && !is_valid_index())
349  {
350  ++index;
351  }
352 }
353 
356 {
357  return ++option_names_iteratort(*this);
358 }
359 
361 {
362  PRECONDITION(command_line != nullptr);
363  return command_line->options.at(index).optstring;
364 }
365 
368 {
369  PRECONDITION(command_line != nullptr && command_line == other.command_line);
370  return index == other.index;
371 }
372 
375 {
376  PRECONDITION(command_line != nullptr && command_line == other.command_line);
377  return index != other.index;
378 }
379 
382 {
383 }
384 
386 {
387  return option_names_iteratort(&command_line, 0);
388 }
389 
391 {
392  return option_names_iteratort(&command_line, command_line.options.size());
393 }
cmdlinet::args
argst args
Definition: cmdline.h:143
levenshtein_automatont
Simple automaton that can detect whether a string can be transformed into another with a limited numb...
Definition: edit_distance.h:26
exception_utils.h
cmdlinet::option_namest::option_names_iteratort::operator*
const std::string & operator*()
Definition: cmdline.cpp:360
cmdlinet::parse
virtual bool parse(int argc, const char **argv, const char *optstring)
Parses a commandline according to a specification given in optstring.
Definition: cmdline.cpp:154
cmdlinet::option_namest::begin
option_names_iteratort begin()
Definition: cmdline.cpp:385
cmdlinet::option_namest
Definition: cmdline.h:103
cmdlinet::isset
virtual bool isset(char option) const
Definition: cmdline.cpp:29
cmdlinet::parse_arguments
bool parse_arguments(int argc, const char **argv)
Parses a commandline according to a previously parsed optstring and writes the result to cmdlinet::op...
Definition: cmdline.cpp:270
cmdlinet::option_namest::option_names_iteratort::goto_next_valid_index
void goto_next_valid_index()
Definition: cmdline.cpp:345
invariant.h
cmdlinet::options
std::vector< optiont > options
Definition: cmdline.h:182
edit_distance.h
cmdlinet::option_namest::option_names_iteratort::option_names_iteratort
option_names_iteratort()=default
cmdlinet::option_namest::option_names_iteratort::index
std::size_t index
Definition: cmdline.h:127
cmdlinet::parse_optstring
void parse_optstring(const char *optstring)
Parses an optstring and writes the result to cmdlinet::options.
Definition: cmdline.cpp:166
cmdlinet::get_comma_separated_values
std::list< std::string > get_comma_separated_values(const char *option) const
Definition: cmdline.cpp:120
cmdlinet::set
virtual void set(const std::string &option, bool value=true)
Set option option to value, or true if the value is omitted.
Definition: cmdline.cpp:57
cmdlinet::optiont::islong
bool islong
Definition: cmdline.h:157
immutable_empty_list
static std::list< std::string > immutable_empty_list
Definition: cmdline.cpp:86
cmdlinet::option_namest::option_names_iteratort::operator!=
bool operator!=(const option_names_iteratort &other)
Definition: cmdline.cpp:374
cmdlinet::option_namest::option_namest
option_namest(const cmdlinet &command_line)
Definition: cmdline.cpp:380
cmdlinet::optiont::optstring
std::string optstring
Definition: cmdline.h:159
cmdlinet
Definition: cmdline.h:21
cmdlinet::optiont::isset
bool isset
Definition: cmdline.h:155
cmdlinet::~cmdlinet
virtual ~cmdlinet()
Definition: cmdline.cpp:19
DATA_INVARIANT
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:511
cmdlinet::option_namest::command_line
const cmdlinet & command_line
Definition: cmdline.h:135
PRECONDITION
#define PRECONDITION(CONDITION)
Definition: invariant.h:464
cmdlinet::get_value
std::string get_value(char option) const
Definition: cmdline.cpp:47
cmdlinet::unknown_arg
std::string unknown_arg
Definition: cmdline.h:144
cmdlinet::option_namest::option_names_iteratort::operator++
option_names_iteratort & operator++()
Definition: cmdline.cpp:330
cmdlinet::option_namest::option_names_iteratort
Definition: cmdline.h:107
operator<
bool operator<(const reaching_definitiont &a, const reaching_definitiont &b)
In order to use instances of this structure as keys in ordered containers, such as std::map,...
Definition: reaching_definitions.h:104
cmdlinet::option_names
option_namest option_names() const
Pseudo-object that can be used to iterate over options in this cmdlinet (should not outlive this)
Definition: cmdline.cpp:162
optionalt
nonstd::optional< T > optionalt
Definition: optional.h:35
cmdlinet::clear
virtual void clear()
Definition: cmdline.cpp:23
cmdlinet::cmdlinet
cmdlinet()
Definition: cmdline.cpp:15
cmdline.h
cmdlinet::getoptnr
optionalt< std::size_t > getoptnr(char option) const
Definition: cmdline.cpp:136
cmdlinet::optiont::optchar
char optchar
Definition: cmdline.h:158
cmdlinet::option_namest::option_names_iteratort::is_valid_index
bool is_valid_index() const
Definition: cmdline.cpp:337
cmdlinet::option_namest::option_names_iteratort::command_line
const cmdlinet * command_line
Definition: cmdline.h:126
cmdlinet::optiont
Definition: cmdline.h:154
cmdlinet::option_namest::option_names_iteratort::operator==
bool operator==(const option_names_iteratort &other)
Definition: cmdline.cpp:367
cmdlinet::get_argument_suggestions
std::vector< std::string > get_argument_suggestions(const std::string &unknown_argument)
Definition: cmdline.cpp:211
cmdlinet::option_namest::end
option_names_iteratort end()
Definition: cmdline.cpp:390
INVARIANT
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition: invariant.h:424
cmdlinet::optiont::hasval
bool hasval
Definition: cmdline.h:156
invalid_command_line_argument_exceptiont
Thrown when users pass incorrect command line arguments, for example passing no files to analysis or ...
Definition: exception_utils.h:38
cmdlinet::get_values
const std::list< std::string > & get_values(const std::string &option) const
Definition: cmdline.cpp:108