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