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  while(optstring[0]!=0)
159  {
160  optiont option;
161 
163  optstring[0] != ':', "cmdlinet::parse: Invalid option string\n");
164 
165  if(optstring[0]=='(')
166  {
167  option.islong=true;
168  option.optchar=0;
169  option.isset=false;
170  option.optstring.clear();
171 
172  for(optstring++; optstring[0]!=')' && optstring[0]!=0; optstring++)
173  option.optstring+=optstring[0];
174 
175  if(optstring[0]==')')
176  optstring++;
177  }
178  else
179  {
180  option.islong=false;
181  option.optchar=optstring[0];
182  option.optstring.clear();
183  option.isset=false;
184 
185  optstring++;
186  }
187 
188  if(optstring[0]==':')
189  {
190  option.hasval=true;
191  optstring++;
192  }
193  else
194  option.hasval=false;
195 
196  options.push_back(option);
197  }
198 
199  for(int i=1; i<argc; i++)
200  {
201  if(argv[i][0]!='-')
202  args.push_back(argv[i]);
203  else
204  {
206 
207  if(argv[i][1]!=0 && argv[i][2]==0)
208  optnr=getoptnr(argv[i][1]); // single-letter option -X
209  else if(argv[i][1]=='-')
210  optnr=getoptnr(argv[i]+2); // multi-letter option with --XXX
211  else
212  {
213  // Multi-letter option -XXX, or single-letter with argument -Xval
214  // We first try single-letter.
215  optnr=getoptnr(argv[i][1]);
216 
217  if(!optnr.has_value()) // try multi-letter
218  optnr=getoptnr(argv[i]+1);
219  }
220 
221  if(!optnr.has_value())
222  {
223  unknown_arg=argv[i];
224  return true;
225  }
226 
227  options[*optnr].isset=true;
228 
229  if(options[*optnr].hasval)
230  {
231  if(argv[i][2]==0 || options[*optnr].islong)
232  {
233  i++;
234  if(i==argc)
235  return true;
236  if(argv[i][0]=='-' && argv[i][1]!=0)
237  return true;
238  options[*optnr].values.push_back(argv[i]);
239  }
240  else
241  options[*optnr].values.push_back(argv[i]+2);
242  }
243  }
244  }
245 
246  return false;
247 }
248 
250 {
251  return option_namest{*this};
252 }
253 
254 std::vector<std::string>
255 cmdlinet::get_argument_suggestions(const std::string &unknown_argument)
256 {
257  struct suggestiont
258  {
259  std::size_t distance;
260  std::string suggestion;
261 
262  bool operator<(const suggestiont &other) const
263  {
264  return distance < other.distance;
265  }
266  };
267 
268  auto argument_suggestions = std::vector<suggestiont>{};
269  // We allow 3 errors here. This can lead to the output being a bit chatty,
270  // which we mitigate by reducing suggestions to those with the minimum
271  // distance further down below
272  const auto argument_matcher = levenshtein_automatont{unknown_argument, 3};
273  for(const auto &option : options)
274  {
275  if(option.islong)
276  {
277  const auto long_name = "--" + option.optstring;
278  if(auto distance = argument_matcher.get_edit_distance(long_name))
279  {
280  argument_suggestions.push_back({distance.value(), long_name});
281  }
282  }
283  if(!option.islong)
284  {
285  const auto short_name = std::string{"-"} + option.optchar;
286  if(auto distance = argument_matcher.get_edit_distance(short_name))
287  {
288  argument_suggestions.push_back({distance.value(), short_name});
289  }
290  }
291  }
292 
293  auto final_suggestions = std::vector<std::string>{};
294  if(!argument_suggestions.empty())
295  {
296  // we only want to keep suggestions with the minimum distance
297  // because otherwise they become quickly too noisy to be useful
298  auto min = std::min_element(
299  argument_suggestions.begin(), argument_suggestions.end());
300  INVARIANT(
301  min != argument_suggestions.end(),
302  "there is a minimum because it's not empty");
303  for(auto const &suggestion : argument_suggestions)
304  {
305  if(suggestion.distance == min->distance)
306  {
307  final_suggestions.push_back(suggestion.suggestion);
308  }
309  }
310  }
311  return final_suggestions;
312 }
313 
315  const cmdlinet *command_line,
316  std::size_t index)
317  : command_line(command_line), index(index)
318 {
320 }
321 
324 {
325  PRECONDITION(command_line != nullptr);
326  ++index;
327  goto_next_valid_index();
328  return *this;
329 }
331 {
332  PRECONDITION(command_line != nullptr);
333  auto const &options = command_line->options;
334  return index < options.size() && options[index].isset &&
335  options[index].islong;
336 }
337 
339 {
340  PRECONDITION(command_line != nullptr);
341  while(index < command_line->options.size() && !is_valid_index())
342  {
343  ++index;
344  }
345 }
346 
349 {
350  return ++option_names_iteratort(*this);
351 }
352 
354 {
355  PRECONDITION(command_line != nullptr);
356  return command_line->options.at(index).optstring;
357 }
358 
361 {
362  PRECONDITION(command_line != nullptr && command_line == other.command_line);
363  return index == other.index;
364 }
365 
368 {
369  PRECONDITION(command_line != nullptr && command_line == other.command_line);
370  return index != other.index;
371 }
372 
375 {
376 }
377 
379 {
380  return option_names_iteratort(&command_line, 0);
381 }
382 
384 {
385  return option_names_iteratort(&command_line, command_line.options.size());
386 }
cmdlinet::args
argst args
Definition: cmdline.h:91
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:353
cmdlinet::parse
virtual bool parse(int argc, const char **argv, const char *optstring)
Definition: cmdline.cpp:154
cmdlinet::option_namest::begin
option_names_iteratort begin()
Definition: cmdline.cpp:378
cmdlinet::option_namest
Definition: cmdline.h:51
cmdlinet::isset
virtual bool isset(char option) const
Definition: cmdline.cpp:29
cmdlinet::option_namest::option_names_iteratort::goto_next_valid_index
void goto_next_valid_index()
Definition: cmdline.cpp:338
invariant.h
cmdlinet::options
std::vector< optiont > options
Definition: cmdline.h:118
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:75
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:105
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:367
cmdlinet::option_namest::option_namest
option_namest(const cmdlinet &command_line)
Definition: cmdline.cpp:373
cmdlinet::optiont::optstring
std::string optstring
Definition: cmdline.h:107
cmdlinet
Definition: cmdline.h:21
cmdlinet::optiont::isset
bool isset
Definition: cmdline.h:103
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:83
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:92
cmdlinet::option_namest::option_names_iteratort::operator++
option_names_iteratort & operator++()
Definition: cmdline.cpp:323
cmdlinet::option_namest::option_names_iteratort
Definition: cmdline.h:55
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:249
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:106
cmdlinet::option_namest::option_names_iteratort::is_valid_index
bool is_valid_index() const
Definition: cmdline.cpp:330
cmdlinet::option_namest::option_names_iteratort::command_line
const cmdlinet * command_line
Definition: cmdline.h:74
cmdlinet::optiont
Definition: cmdline.h:102
cmdlinet::option_namest::option_names_iteratort::operator==
bool operator==(const option_names_iteratort &other)
Definition: cmdline.cpp:360
cmdlinet::get_argument_suggestions
std::vector< std::string > get_argument_suggestions(const std::string &unknown_argument)
Definition: cmdline.cpp:255
cmdlinet::option_namest::end
option_names_iteratort end()
Definition: cmdline.cpp:383
cmdlinet::optiont::hasval
bool hasval
Definition: cmdline.h:104
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
validation_modet::INVARIANT
@ INVARIANT