cprover
Loading...
Searching...
No Matches
cmdline.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module:
4
5Author: Daniel Kroening, kroening@kroening.com
6
7\*******************************************************************/
8
9#include "cmdline.h"
10
11#include <util/edit_distance.h>
13#include <util/invariant.h>
14#include <util/string_utils.h>
15
19
23
25{
26 options.clear();
27 args.clear();
28}
29
30bool 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
39bool 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
48std::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
58void 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
71void 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
87static std::list<std::string> immutable_empty_list;
88
89const 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
97}
98
99std::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
109const 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
118}
119
120std::list<std::string>
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
136{
137 for(std::size_t i=0; i<options.size(); i++)
138 if(options[i].optchar==option)
139 return i;
140
141 return optionalt<std::size_t>();
142}
143
144optionalt<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 optionalt<std::size_t>();
151}
152
153bool 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
165void 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
209std::vector<std::string>
210cmdlinet::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
269bool 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 {
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 // Work around spurious GCC 12 warning about optnr being uninitialised.
300#pragma GCC diagnostic push
301#ifndef __clang__
302# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
303#endif
304 options[*optnr].isset = true;
305#pragma GCC diagnostic pop
306
307 if(options[*optnr].hasval)
308 {
309 if(argv[i][2] == 0 || options[*optnr].islong)
310 {
311 i++;
312 if(i == argc)
313 return true;
314 if(options[*optnr].optstring != "add-cmd-line-arg" && argv[i][0] == '-' && argv[i][1] != 0)
315 return true;
316 options[*optnr].values.push_back(argv[i]);
317 }
318 else
319 options[*optnr].values.push_back(argv[i] + 2);
320 }
321 }
322 }
323 return false;
324}
325
327 const cmdlinet *command_line,
328 std::size_t index)
329 : command_line(command_line), index(index)
330{
332}
333
336{
337 PRECONDITION(command_line != nullptr);
338 ++index;
339 goto_next_valid_index();
340 return *this;
341}
343{
344 PRECONDITION(command_line != nullptr);
345 auto const &options = command_line->options;
346 return index < options.size() && options[index].isset &&
347 options[index].islong;
348}
349
351{
352 PRECONDITION(command_line != nullptr);
353 while(index < command_line->options.size() && !is_valid_index())
354 {
355 ++index;
356 }
357}
358
364
366{
367 PRECONDITION(command_line != nullptr);
368 return command_line->options.at(index).optstring;
369}
370
377
384
389
394
396{
397 return option_names_iteratort(&command_line, command_line.options.size());
398}
std::string get_value(char option) const
Definition cmdline.cpp:48
std::string unknown_arg
Definition cmdline.h:146
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
argst args
Definition cmdline.h:145
optionalt< std::size_t > getoptnr(char option) const
Definition cmdline.cpp:135
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:184
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
nonstd::optional< T > optionalt
Definition optional.h:35
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
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
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:372
bool operator!=(const option_names_iteratort &other)
Definition cmdline.cpp:379
option_names_iteratort end()
Definition cmdline.cpp:395
option_names_iteratort begin()
Definition cmdline.cpp:390
option_namest(const cmdlinet &command_line)
Definition cmdline.cpp:385
const cmdlinet & command_line
Definition cmdline.h:137
std::string optstring
Definition cmdline.h:161
Simple automaton that can detect whether a string can be transformed into another with a limited numb...