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