HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
program_options.cpp
Go to the documentation of this file.
2 
5 
6 #include <algorithm>
7 #include <unistd.h>
8 
9 #if __linux__ || __APPLE__
10 #include <sys/ioctl.h>
11 #endif
12 
13 namespace hal
14 {
15  const std::string ProgramOptions::A_REQUIRED_PARAMETER = "__A_REQUIRED_PARAMETER__";
16 
18  {
19  m_name = name;
20  }
21 
22  std::vector<std::string> ProgramOptions::get_unknown_arguments()
23  {
24  return m_unknown_options;
25  }
26 
27  ProgramArguments ProgramOptions::parse(int argc, const char* argv[])
28  {
29  const Option* current_option = nullptr;
30 
31  m_unknown_options.clear();
32 
33  auto all_options = get_all_options();
34 
35  std::string current_flag;
36  std::vector<std::string> current_parameters;
37  size_t param_pos = 0;
38 
39  ProgramArguments args(argc, argv);
40 
41  // i=1: ignore first argument as it is the program itself
42  for (int i = 1; i < argc; ++i)
43  {
44  std::string arg(argv[i]);
45 
46  // search through all options for a recognized flag
47  bool opt_found = false;
48  for (auto opt : all_options)
49  {
50  if (std::find(opt->flags.begin(), opt->flags.end(), arg) != opt->flags.end())
51  {
52  // flag found, set current option
53 
54  if (current_option != nullptr)
55  {
56  if (current_option->parameters[param_pos] == A_REQUIRED_PARAMETER)
57  {
58  die("core",
59  "the option with flags {}is missing a required parameter!",
60  utils::join(" ", std::vector<std::string>(current_option->flags.begin(), current_option->flags.end())));
61  }
62  else
63  {
64  args.set_option(current_flag, current_option->flags, current_parameters);
65  }
66  }
67 
68  opt_found = true;
69  current_option = opt;
70  current_parameters = opt->parameters;
71  current_flag = arg;
72  param_pos = 0;
73  break;
74  }
75  }
76 
77  // if the current string is not a flag, process it otherwise
78  if (!opt_found)
79  {
80  // if the current option expects parameters, add the current string
81  if (current_option != nullptr)
82  {
83  current_parameters[param_pos++] = arg;
84  }
85  else
86  {
87  m_unknown_options.push_back(arg);
88  }
89  }
90 
91  // if the current option got all expected parameters, it is finished
92  if (current_option != nullptr && param_pos >= current_option->parameters.size())
93  {
94  args.set_option(current_flag, current_option->flags, current_parameters);
95  current_option = nullptr;
96  current_parameters.clear();
97  current_flag = "";
98  param_pos = 0;
99  }
100  }
101 
102  if (current_option != nullptr)
103  {
104  if (param_pos < current_option->parameters.size() && current_option->parameters[param_pos] == A_REQUIRED_PARAMETER)
105  {
106  die("core",
107  "the option with flags {}is missing at least one required parameter!",
108  utils::join(" ", std::vector<std::string>(current_option->flags.begin(), current_option->flags.end())));
109  }
110 
111  args.set_option(current_flag, current_option->flags, current_parameters);
112  }
113 
114  return args;
115  }
116 
117  bool ProgramOptions::is_registered(const std::string& flag) const
118  {
119  for (auto opt : get_all_options())
120  {
121  if (std::find(opt->flags.begin(), opt->flags.end(), flag) != opt->flags.end())
122  {
123  return true;
124  }
125  }
126  return false;
127  }
128 
129  bool ProgramOptions::add(const std::string& flag, const std::string& description, const std::initializer_list<std::string>& parameters)
130  {
131  return add_flags({flag}, description, parameters);
132  }
133 
134  bool ProgramOptions::add(const std::initializer_list<std::string>& flags, const std::string& description, const std::initializer_list<std::string>& parameters)
135  {
136  return add_flags(flags, description, parameters);
137  }
138 
139  bool ProgramOptions::add_flags(const std::vector<std::string>& flags, const std::string& description, const std::vector<std::string>& parameters)
140  {
141  if (flags.size() == 0)
142  {
143  log_error("core", "can't add option with empty flags (description: '{}')!", utils::trim(description));
144  return false;
145  }
146 
147  if (description.empty())
148  {
149  std::string flags_string = "";
150  for (const auto& flag : flags)
151  {
152  flags_string += flag + " ";
153  }
154 
155  log_error("core", "can't add option with flags ({}). The description must not be empty!", utils::trim(flags_string));
156  return false;
157  }
158 
159  // look for already registered option, abort if found
160  for (auto opt : get_all_options())
161  {
162  if (opt->description == description)
163  {
164  std::string flags_string = "";
165  for (const auto& flag : flags)
166  {
167  flags_string += flag + " ";
168  }
169 
170  log_error("core", "can't add option with flags ({}). An option with description '{}' is already registered.", utils::trim(flags_string), description);
171  return false;
172  }
173  }
174 
175  for (const auto& flag : flags)
176  {
177  if (is_registered(flag))
178  {
179  log_error("core", "the flag '{}' is already registered.", flag);
180  return false;
181  }
182  }
183 
184  // only allow REQUIRED, REQUIRED, REQUIRED, [...], NON_REQUIRED, NON_REQUIRED, [...]
185  bool found_not_required = false;
186  for (const auto& param : parameters)
187  {
188  if (param == A_REQUIRED_PARAMETER)
189  {
190  if (found_not_required)
191  {
192  log_error("core", "a required parameter can't follow a non-required parameter.");
193  return false;
194  }
195  }
196  else if (param != A_REQUIRED_PARAMETER)
197  {
198  found_not_required = true;
199  }
200  }
201 
202  // create new option
203  Option opt;
204  opt.description = description;
205  opt.parameters = parameters;
206  opt.flags = std::set<std::string>(flags.begin(), flags.end());
207 
208  m_options.push_back(opt);
209 
210  return true;
211  }
212 
213  bool ProgramOptions::add(const ProgramOptions& other_options, const std::string& category)
214  {
215  // look for conflicts
216  for (auto other_opt : other_options.get_all_options())
217  {
218  for (auto opt : get_all_options())
219  {
220  if (opt->description == other_opt->description)
221  {
222  log_error("core", "an option with description '{}' is already registered.", opt->description);
223  return false;
224  }
225 
226  for (const auto& flag : other_opt->flags)
227  {
228  if (std::find(opt->flags.begin(), opt->flags.end(), flag) != opt->flags.end())
229  {
230  log_error("core", "the flag '{}' is already registered.", flag);
231  return false;
232  }
233  }
234  }
235  }
236 
237  // no conflict -> add
238  m_suboptions[category].push_back(other_options);
239  return true;
240  }
241 
242  bool ProgramOptions::remove(const std::string& flag)
243  {
244  for (auto it = m_options.begin(); it != m_options.end(); ++it)
245  {
246  if (it->flags.find(flag) != it->flags.end())
247  {
248  it->flags.erase(flag);
249  if (it->flags.empty())
250  {
251  m_options.erase(it);
252  }
253  return true;
254  }
255  }
256  for (auto& sub : m_suboptions)
257  {
258  for (auto& opt : sub.second)
259  {
260  auto success = opt.remove(flag);
261  if (success)
262  {
263  return true;
264  }
265  }
266  }
267  return false;
268  }
269 
271  {
272  size_t line_width = 80; // default magic number
273 
274 #if __linux__ || __APPLE__
275  struct winsize w;
276  ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
277  if (w.ws_col < 30)
278  {
279  line_width = 1000;
280  }
281  else
282  {
283  line_width = w.ws_col;
284  }
285 #endif
286 
287  return get_options_string_internal(get_flag_length() + 10, line_width);
288  }
289 
290  std::vector<std::tuple<std::set<std::string>, std::string>> ProgramOptions::get_options() const
291  {
292  std::vector<std::tuple<std::set<std::string>, std::string>> options;
293  for (auto opt : get_all_options())
294  {
295  options.push_back(std::make_tuple(opt->flags, opt->description));
296  }
297  return options;
298  }
299 
300  std::vector<const ProgramOptions::Option*> ProgramOptions::get_all_options() const
301  {
302  std::vector<const ProgramOptions::Option*> options;
303 
304  std::transform(m_options.begin(), m_options.end(), std::back_inserter(options), [](auto& opt) { return &opt; });
305 
306  for (const auto& it : m_suboptions)
307  {
308  for (const auto& opt : it.second)
309  {
310  auto insertion = opt.get_all_options();
311  options.insert(options.end(), insertion.begin(), insertion.end());
312  }
313  }
314  return options;
315  }
316 
317  size_t ProgramOptions::get_flag_length() const
318  {
319  size_t max_len = 0;
320  for (const auto& opt : m_options)
321  {
322  size_t opt_len = 0;
323  for (const auto& f : opt.flags)
324  {
325  opt_len += f.size() + 2;
326  }
327  for (size_t j = 0; j < opt.parameters.size(); ++j)
328  {
329  opt_len += 4; //" arg"
330  }
331  opt_len -= 2; // last option has no ", "
332 
333  if (opt_len > max_len)
334  {
335  max_len = opt_len;
336  }
337  }
338 
339  for (const auto& it : m_suboptions)
340  {
341  for (const auto& opt : it.second)
342  {
343  size_t subopt_len = opt.get_flag_length();
344  if (subopt_len > max_len)
345  {
346  max_len = subopt_len;
347  }
348  }
349  }
350 
351  return max_len;
352  }
353 
354  std::string ProgramOptions::get_options_string_internal(size_t fill_length, size_t max_line_width) const
355  {
356  std::string s = "";
357 
358  if (!m_name.empty())
359  {
360  s += m_name + "\n";
361  }
362 
363  for (size_t i = 0; i < m_options.size(); ++i)
364  {
365  auto& opt = m_options[i];
366 
367  std::string flags = " " + utils::join(", ", std::vector<std::string>(opt.flags.begin(), opt.flags.end()));
368 
369  for (const auto& flag : opt.parameters)
370  {
371  if (flag == A_REQUIRED_PARAMETER)
372  {
373  flags += " ARG";
374  }
375  else
376  {
377  flags += " arg";
378  }
379  }
380 
381  s += flags;
382 
383  for (size_t j = 0; j < fill_length - flags.size(); ++j)
384  {
385  s += " ";
386  }
387 
388  std::string description = opt.description;
389  bool first_description_line = true;
390 
391  while (!description.empty())
392  {
393  if (!first_description_line)
394  {
395  for (size_t j = 0; j < fill_length; ++j)
396  {
397  s += " ";
398  }
399  }
400  if (description.size() > max_line_width - fill_length)
401  {
402  auto index = description.substr(0, max_line_width - fill_length).rfind(' ');
403  if (index == std::string::npos)
404  {
405  index = max_line_width - fill_length;
406  }
407  s += description.substr(0, index) + "\n";
408  description = description.substr(index);
409  }
410  else
411  {
412  s += description;
413  description = "";
414  }
415  first_description_line = false;
416  }
417 
418  if (i != m_options.size() - 1)
419  {
420  s += "\n";
421  }
422  }
423 
424  if (!s.empty())
425  {
426  s += "\n";
427  }
428 
429  for (const auto& it : m_suboptions)
430  {
431  if (!it.first.empty())
432  {
433  s += it.first + "\n";
434  }
435  for (const auto& opt : it.second)
436  {
437  s += opt.get_options_string_internal(fill_length, max_line_width);
438  }
439  }
440 
441  if (s.length() > 1)
442  {
443  while (s.substr(s.length() - 2) != "\n\n")
444  {
445  s += "\n";
446  }
447  }
448  return s;
449  }
450 } // namespace hal
void set_option(const std::string &flag, const std::string &parameter)
std::string get_options_string() const
bool add_flags(const std::vector< std::string > &flags, const std::string &description, const std::vector< std::string > &parameters={})
bool is_registered(const std::string &flag) const
bool remove(const std::string &flag)
std::vector< std::tuple< std::set< std::string >, std::string > > get_options() const
ProgramArguments parse(int argc, const char *argv[])
bool add(const std::string &flag, const std::string &description, const std::initializer_list< std::string > &parameters={})
std::vector< std::string > get_unknown_arguments()
ProgramOptions(const std::string &name="")
static const std::string A_REQUIRED_PARAMETER
constant to specify that a parameter is required and does not have a default value.
#define die(channel,...)
Definition: log.h:94
#define log_error(channel,...)
Definition: log.h:78
std::string join(const std::string &joiner, const Iterator &begin, const Iterator &end, const Transform &transform)
Definition: utils.h:414
T trim(const T &s, const char *to_remove=" \t\r\n")
Definition: utils.h:360
Definition: defines.h:45
std::string name