MVE - Multi-View Environment mve-devel
Loading...
Searching...
No Matches
arguments.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2015, Simon Fuhrmann
3 * TU Darmstadt - Graphics, Capture and Massively Parallel Computing
4 * All rights reserved.
5 *
6 * This software may be modified and distributed under the terms
7 * of the BSD 3-Clause license. See the LICENSE.txt file for details.
8 */
9
10#include <iostream>
11#include <cstdlib>
12#include <sstream>
13#include <iomanip>
14#include <limits>
15
16#include "util/strings.h"
17#include "util/tokenizer.h"
18#include "util/arguments.h"
19
21
22Arguments::Arguments (void)
23 : nonopt_min(0)
24 , nonopt_max(std::numeric_limits<std::size_t>::max())
25 , auto_exit(false)
26 , helptext_indent(16)
27 , descrtext_width(75)
28 , cur_result(std::numeric_limits<std::size_t>::max())
29{
30}
31
32/* ---------------------------------------------------------------- */
33
34void
35Arguments::set_usage (char const* argv0, std::string const& usage)
36{
37 std::stringstream ss;
38 ss << "Usage: " << argv0 << " " << usage;
39 this->usage_str = ss.str();
40}
41
42/* ---------------------------------------------------------------- */
43
44void
45Arguments::add_option (char shortname, std::string const& longname,
46 bool has_argument, std::string const& description)
47{
48 if (shortname == '\0' && longname.empty())
49 throw std::invalid_argument("Neither short nor long name given");
50
51 /* Check if option already exists. */
52 for (std::size_t i = 0; i < this->options.size(); ++i)
53 if ((shortname != '\0' && this->options[i].sopt == shortname)
54 || (!longname.empty() && this->options[i].lopt == longname))
55 throw std::runtime_error("Option already exists");
56
57 ArgOption opt;
58 opt.sopt = shortname;
59 opt.lopt = longname;
60 opt.argument = has_argument;
61 opt.desc = description;
62 this->options.push_back(opt);
63}
64
65/* ---------------------------------------------------------------- */
66
67void
68Arguments::parse (int argc, char const* const* argv)
69{
70 std::vector<std::string> args;
71 for (int i = 0; i < argc; ++i)
72 args.push_back(argv[i]);
73 this->parse(args);
74}
75
76/* ---------------------------------------------------------------- */
77
78void
79Arguments::parse (std::vector<std::string> const& args)
80{
81 try
82 {
83 this->parse_intern(args);
84 }
85 catch (util::Exception const& e)
86 {
87 if (!this->auto_exit)
88 throw;
89
90 std::cerr << std::endl;
91 this->generate_helptext(std::cerr);
92 std::cerr << std::endl << "Error: " << e.what() << std::endl;
93 std::exit(1);
94 }
95}
96
97/* ---------------------------------------------------------------- */
98
99void
100Arguments::parse_long_opt (std::string const& tok)
101{
102 std::size_t pos = tok.find_first_of('=');
103 std::string opt = tok.substr(2, pos - 2);
104 std::string arg;
105 if (pos != std::string::npos)
106 arg = tok.substr(pos + 1);
107
108 if (opt.empty())
109 throw util::Exception("Invalid option: ", tok);
110
111 ArgOption const* option = this->find_opt(opt);
112 if (option == nullptr)
113 throw util::Exception("Invalid option: ", tok);
114
115 if (option->argument && arg.empty())
116 throw util::Exception("Option missing argument: ", tok);
117
118 if (!option->argument && !arg.empty())
119 throw util::Exception("Option with unexpected argument: ", tok);
120
121 ArgResult result;
122 result.arg = arg;
123 result.opt = option;
124 this->results.push_back(result);
125}
126
127/* ---------------------------------------------------------------- */
128
129bool
130Arguments::parse_short_opt (std::string const& tok1, std::string const& tok2)
131{
132 if (tok1.size() < 2)
133 throw std::runtime_error("Short option with too few chars");
134
135 char opt = tok1[1];
136 std::string arg;
137 bool retval = false;
138 ArgOption const* option = this->find_opt(opt);
139
140 if (option == nullptr)
141 throw util::Exception("Invalid option: ", tok1);
142
143 /* If multiple options are given, recurse. */
144 if (!option->argument && tok1.size() > 2)
145 {
146 for (std::size_t i = 1; i < tok1.size(); ++i)
147 {
148 std::string sopt = "-";
149 sopt.append(1, tok1[i]);
150 this->parse_short_opt(sopt, "");
151 }
152 return false;
153 }
154
155 if (option->argument && tok1.size() == 2)
156 {
157 if (tok2.empty() || tok2[0] == '-')
158 throw util::Exception("Option missing argument: ", tok1);
159 arg = tok2;
160 retval = true;
161 }
162 else if (option->argument)
163 {
164 arg = tok1.substr(2);
165 }
166
167 if (!option->argument && tok1.size() != 2)
168 throw util::Exception("Option with unexpected argument: ", tok1);
169
170 ArgResult result;
171 result.arg = arg;
172 result.opt = option;
173 this->results.push_back(result);
174
175 return retval;
176}
177
178/* ---------------------------------------------------------------- */
179
180ArgOption const*
181Arguments::find_opt (char sopt)
182{
183 for (std::size_t i = 0; i < this->options.size(); ++i)
184 if (this->options[i].sopt == sopt)
185 return &this->options[i];
186
187 return nullptr;
188}
189
190/* ---------------------------------------------------------------- */
191
192ArgOption const*
193Arguments::find_opt (std::string const& lopt)
194{
195 for (std::size_t i = 0; i < this->options.size(); ++i)
196 if (this->options[i].lopt == lopt)
197 return &this->options[i];
198
199 return nullptr;
200}
201
202/* ---------------------------------------------------------------- */
203
204void
205Arguments::parse_intern (std::vector<std::string> const& args)
206{
207 this->results.clear();
208 this->command_name.clear();
209 this->cur_result = std::numeric_limits<std::size_t>::max();
210
211 /* Iterate over all tokens and buils result structure. */
212 bool parseopt = true;
213 for (std::size_t i = 0; i < args.size(); ++i)
214 {
215 if (i == 0)
216 {
217 this->command_name = args[i];
218 continue;
219 }
220
221 std::string tok = args[i];
224
225 /* Skip empty tokens. */
226 if (tok.empty())
227 continue;
228
229 if (parseopt && tok.size() == 2 && tok == "--")
230 {
231 /* Option terminator. */
232 parseopt = false;
233 continue;
234 }
235 else if (parseopt && tok.size() >= 3 && tok[0] == '-' && tok[1] == '-')
236 {
237 /* Long option. Requires only one token. */
238 this->parse_long_opt(tok);
239 }
240 else if (parseopt && tok.size() >= 2 && tok[0] == '-')
241 {
242 /* Short option, possibly requiring the next token. */
243 std::string next_tok;
244 if (i + 1 < args.size())
245 {
246 next_tok = args[i + 1];
247 string::clip_newlines(&next_tok);
248 string::clip_whitespaces(&next_tok);
249 }
250 bool used_next = this->parse_short_opt(tok, next_tok);
251 if (used_next)
252 i += 1;
253 }
254 else
255 {
256 /* Regular non-option. */
257 ArgResult res;
258 res.opt = nullptr;
259 res.arg = tok;
260 this->results.push_back(res);
261 }
262 }
263
264 /* Check non-option limit. */
265 std::size_t num_nonopt = 0;
266 for (std::size_t i = 0; i < this->results.size(); ++i)
267 {
268 if (this->results[i].opt == nullptr)
269 num_nonopt += 1;
270 }
271
272 if (num_nonopt > this->nonopt_max)
273 throw util::Exception("Too many non-option arguments");
274 if (num_nonopt < this->nonopt_min)
275 throw util::Exception("Too few non-option arguments");
276}
277
278/* ---------------------------------------------------------------- */
279
280ArgResult const*
282{
283 this->cur_result += 1;
284 if (this->cur_result >= this->results.size())
285 {
286 this->cur_result = (std::size_t)(-1);
287 return nullptr;
288 }
289
290 return &this->results[this->cur_result];
291}
292
293/* ---------------------------------------------------------------- */
294
295ArgResult const*
297{
298 while (true)
299 {
300 this->cur_result += 1;
301 if (this->cur_result >= this->results.size())
302 break;
303 ArgResult const* ret = &this->results[this->cur_result];
304 if (ret->opt == nullptr)
305 continue;
306 return ret;
307 }
308 this->cur_result = (std::size_t)(-1); // Reset iterator
309 return nullptr;
310}
311
312/* ---------------------------------------------------------------- */
313
314std::string
315Arguments::get_nth_nonopt (std::size_t index)
316{
317 std::size_t cnt = 0;
318 for (std::size_t i = 0; i < this->results.size(); ++i)
319 {
320 if (this->results[i].opt)
321 continue;
322 if (index == cnt)
323 return this->results[i].arg;
324 cnt += 1;
325 }
326
327 return std::string();
328}
329
330/* ---------------------------------------------------------------- */
331
332void
333Arguments::get_ids_from_string (std::string const& str, std::vector<int>* ids)
334{
335 ids->clear();
336 if (str.empty() || util::string::lowercase(str) == "all")
337 return;
338
340 t.split(str, ',');
341 for (std::size_t i = 0; i < t.size(); ++i)
342 {
343 std::size_t pos = t[i].find_first_of('-');
344 if (pos != std::string::npos)
345 {
346 int const first = util::string::convert<int>(t[i].substr(0, pos));
347 int const last = util::string::convert<int>(t[i].substr(pos + 1));
348 int const inc = (first > last ? -1 : 1);
349 for (int j = first; j != last + inc; j += inc)
350 ids->push_back(j);
351 }
352 else
353 {
354 ids->push_back(util::string::convert<int>(t[i]));
355 }
356 }
357}
358
359/* ---------------------------------------------------------------- */
360
361void
362Arguments::generate_helptext (std::ostream& stream) const
363{
364 std::string descr = util::string::wordwrap
365 (this->descr_str.c_str(), this->descrtext_width);
366
367 if (!descr.empty())
368 stream << descr << std::endl << std::endl;
369
370 if (!this->usage_str.empty())
371 stream << this->usage_str << std::endl;
372
373 if (!this->options.empty() && (!this->usage_str.empty() || !descr.empty()))
374 stream << "Available options: " << std::endl;
375
376 for (std::size_t i = 0; i < this->options.size(); ++i)
377 {
378 ArgOption const& opt(this->options[i]);
379 std::stringstream optstr;
380 if (opt.sopt != '\0')
381 {
382 optstr << '-' << opt.sopt;
383 if (opt.argument && opt.lopt.empty())
384 optstr << " ARG";
385 if (!opt.lopt.empty())
386 optstr << ", ";
387
388 }
389 if (!opt.lopt.empty())
390 {
391 optstr << "--" << opt.lopt;
392 if (opt.argument)
393 optstr << "=ARG";
394 }
395 optstr << " ";
396
397 stream << " " << std::setiosflags(std::ios::left)
398 << std::setw(this->helptext_indent) << optstr.str()
399 << opt.desc << std::endl;
400 }
401}
402
void add_option(char shortname, std::string const &longname, bool has_argument, std::string const &description="")
Adds an option to the list of possible arguments.
Definition arguments.cc:45
void get_ids_from_string(std::string const &str, std::vector< int > *ids)
Returns a numeric list of IDs by parsing the given string.
Definition arguments.cc:333
void parse(std::vector< std::string > const &args)
Parses arguments from a vector.
Definition arguments.cc:79
void generate_helptext(std::ostream &stream) const
Generates a help text that lists all arguments.
Definition arguments.cc:362
ArgResult const * next_option(void)
Iterator for the options.
Definition arguments.cc:296
std::string get_nth_nonopt(std::size_t index)
Returns the Nth non-option argument, or an empty string.
Definition arguments.cc:315
ArgResult const * next_result(void)
Iterator for the results.
Definition arguments.cc:281
void set_usage(std::string const &str)
Sets the usage string for printing the help text.
Definition arguments.h:199
Universal, simple exception class.
Definition exception.h:24
virtual const char * what(void) const
Definition exception.h:43
Simple tokenizer.
Definition tokenizer.h:29
void split(std::string const &str, char delim=' ', bool keep_empty=false)
Very simple tokenziation at a given delimiter characater.
Definition tokenizer.h:62
std::size_t first
Definition mesh_info.cc:46
STL namespace.
std::string wordwrap(char const *str, int width)
Inserts line breaks on word boundaries to limit lines to 'width' chars.
Definition strings.h:333
void clip_newlines(std::string *str)
Clips newlines from the end of the string, in-place.
Definition strings.h:317
void clip_whitespaces(std::string *str)
Clips whitespaces from the front and end of the string, in-place.
Definition strings.h:299
std::string lowercase(std::string const &str)
Returns a lower-case version of the string.
Definition strings.h:458
A single argument option.
Definition arguments.h:28
std::string lopt
Long option name.
Definition arguments.h:30
std::string desc
Description.
Definition arguments.h:31
bool argument
Requires argument?
Definition arguments.h:32
char sopt
Short option name.
Definition arguments.h:29
An argument which can either be an option or a non-option.
Definition arguments.h:39
ArgOption const * opt
Null for non-options.
Definition arguments.h:40
std::string arg
Empty for options without arguments.
Definition arguments.h:41
#define UTIL_NAMESPACE_BEGIN
Definition defines.h:13
#define UTIL_NAMESPACE_END
Definition defines.h:14