Line data Source code
1 : /*
2 : * License:
3 : * Copyright (c) 2006-2019 Made to Order Software Corp. All Rights Reserved
4 : *
5 : * https://snapwebsites.org/
6 : * contact@m2osw.com
7 : *
8 : * This program is free software; you can redistribute it and/or modify
9 : * it under the terms of the GNU General Public License as published by
10 : * the Free Software Foundation; either version 2 of the License, or
11 : * (at your option) any later version.
12 : *
13 : * This program is distributed in the hope that it will be useful,
14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : * GNU General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU General Public License along
19 : * with this program; if not, write to the Free Software Foundation, Inc.,
20 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 : *
22 : * Authors:
23 : * Alexis Wilke alexis@m2osw.com
24 : * Doug Barbieri doug@m2osw.com
25 : */
26 :
27 : /** \file
28 : * \brief Advanced getopt version functions.
29 : *
30 : * The advgetopt environment is versioned. The functions available here
31 : * give you access to the version, in case you wanted to make sure you
32 : * had a minimum version or had some special case options when you
33 : * want to be able to support various versions.
34 : */
35 :
36 : // self
37 : //
38 : #include "advgetopt/validator.h"
39 :
40 :
41 : // advgetopt lib
42 : //
43 : #include "advgetopt/exception.h"
44 :
45 :
46 : // cppthread lib
47 : //
48 : #include <cppthread/log.h>
49 :
50 :
51 : // snapdev lib
52 : //
53 : #include <snapdev/not_used.h>
54 :
55 :
56 : // boost lib
57 : //
58 : #include <boost/algorithm/string/trim.hpp>
59 :
60 :
61 : // last include
62 : //
63 : #include <snapdev/poison.h>
64 :
65 :
66 :
67 :
68 : namespace advgetopt
69 : {
70 :
71 :
72 :
73 : namespace
74 : {
75 :
76 :
77 2 : std::map<std::string, validator_factory const *> g_validator_factories;
78 :
79 :
80 2 : class validator_integer_factory
81 : : public validator_factory
82 : {
83 : public:
84 2 : validator_integer_factory()
85 2 : {
86 2 : validator::register_validator(*this);
87 2 : }
88 :
89 4 : virtual std::string get_name() const override
90 : {
91 4 : return std::string("integer");
92 : }
93 :
94 48 : virtual std::shared_ptr<validator> create(string_list_t const & data) const override
95 : {
96 48 : snap::NOTUSED(data); // ignore `data`
97 48 : return std::make_shared<validator_integer>(data);
98 : }
99 : };
100 :
101 2 : validator_integer_factory g_validator_integer_factory;
102 :
103 :
104 :
105 2 : class validator_regex_factory
106 : : public validator_factory
107 : {
108 : public:
109 2 : validator_regex_factory()
110 2 : {
111 2 : validator::register_validator(*this);
112 2 : }
113 :
114 4 : virtual std::string get_name() const override
115 : {
116 4 : return std::string("regex");
117 : }
118 :
119 18 : virtual std::shared_ptr<validator> create(string_list_t const & data) const override
120 : {
121 18 : return std::make_shared<validator_regex>(data);
122 : }
123 : };
124 :
125 2 : validator_regex_factory g_validator_regex_factory;
126 :
127 :
128 :
129 : } // no name namespace
130 :
131 :
132 :
133 : /** \brief The destructor to ease derived classes.
134 : *
135 : * At this point this destructor does nothing more than help with the
136 : * virtual table.
137 : */
138 5 : validator_factory::~validator_factory()
139 : {
140 5 : }
141 :
142 :
143 :
144 :
145 :
146 :
147 : /** \brief The validator destructor to support virtuals.
148 : *
149 : * This destructor is defined so virtual functions work as expected including
150 : * the deleter.
151 : */
152 66 : validator::~validator()
153 : {
154 66 : }
155 :
156 :
157 : /** \fn std::string const & validator::name() const;
158 : * \brief Return the name of the validator.
159 : *
160 : * The name() function is used to get the name of the validator.
161 : * Validators are recognized by name and added to your options
162 : * using their name.
163 : *
164 : * Note that when an option specifies a validator which it can't find,
165 : * then an error occurs.
166 : *
167 : * \return The name of the validator.
168 : */
169 :
170 :
171 : /** \fn bool validator::validate(std::string const & value) const;
172 : * \brief Return true if \p value validates agains this validator.
173 : *
174 : * The function parses the \p value parameter and if it matches the
175 : * allowed parameters, then it returns true.
176 : *
177 : * \param[in] value The value to validate.
178 : *
179 : * \return true if the value validates.
180 : */
181 :
182 :
183 5 : void validator::register_validator(validator_factory const & factory)
184 : {
185 5 : auto it(g_validator_factories.find(factory.get_name()));
186 5 : if(it != g_validator_factories.end())
187 : {
188 : throw getopt_logic_error(
189 : "you have two or more validator factories named \""
190 2 : + factory.get_name()
191 3 : + "\".");
192 : }
193 4 : g_validator_factories[factory.get_name()] = &factory;
194 4 : }
195 :
196 :
197 67 : validator::pointer_t validator::create(std::string const & name, string_list_t const & data)
198 : {
199 67 : auto it(g_validator_factories.find(name));
200 67 : if(it == g_validator_factories.end())
201 : {
202 1 : return validator::pointer_t();
203 : }
204 :
205 66 : return it->second->create(data);
206 : }
207 :
208 :
209 : /** \brief Set the validator for this option.
210 : *
211 : * This function parses the specified name and optional parameters and
212 : * create a corresponding validator for this option.
213 : *
214 : * The \p name_and_params string can be defined as:
215 : *
216 : * \code
217 : * <validator-name>(<param1>, <param2>, ...)
218 : * \endcode
219 : *
220 : * The list of parameters is optional. There may be an empty, just one,
221 : * or any number of parameters. How the parameters are parsed is left
222 : * to the validator to decide.
223 : *
224 : * If the input string is empty, the current validator, if one is
225 : * installed, gets removed.
226 : *
227 : * \param[in] name_and_params The validator name and parameters.
228 : */
229 34 : validator::pointer_t validator::create(std::string const & name_and_params)
230 : {
231 34 : if(name_and_params.empty())
232 : {
233 15 : return validator::pointer_t();
234 : }
235 :
236 38 : if(name_and_params.length() >= 2
237 19 : && name_and_params[0] == '/')
238 : {
239 : // for the regex we have a special case
240 : //
241 14 : string_list_t data{name_and_params};
242 7 : return create("regex", data);
243 : }
244 : else
245 : {
246 12 : std::string::size_type const params(name_and_params.find('('));
247 24 : std::string name(name_and_params);
248 24 : string_list_t data;
249 12 : if(params != std::string::npos)
250 : {
251 10 : if(name_and_params.back() != ')')
252 : {
253 : throw getopt_logic_error(
254 : "invalid validator parameter definition: \""
255 8 : + name_and_params
256 12 : + "\", the ')' is missing.");
257 : }
258 6 : name = name_and_params.substr(0, params);
259 12 : split_string(name_and_params.substr(params + 1, name_and_params.length() - params - 2)
260 : , data
261 6 : , {","});
262 : }
263 8 : return create(name, data);
264 : }
265 : }
266 :
267 :
268 :
269 :
270 :
271 :
272 : /** \brief Initialize the integer validator.
273 : *
274 : * The constructor accepts a string with values and ranges which are
275 : * used to limit the values that can be used with this parameter.
276 : *
277 : * Remember that the default does not have to be included in these
278 : * values. It will still be viewed as \em valid.
279 : *
280 : * The string uses the following format:
281 : *
282 : * \code
283 : * start: range
284 : * | start ',' range
285 : *
286 : * range: number
287 : * | number '...' number
288 : *
289 : * number: [-+]?[0-9]+
290 : * \endcode
291 : *
292 : * Note that a single number is considered to be a range and is managed
293 : * the exact same way. A value which matches any of the ranges is considered
294 : * valid.
295 : *
296 : * Example:
297 : *
298 : * \code
299 : * "-100...100,-1000"
300 : * \endcode
301 : *
302 : * This example allows all values between -100 and +100 inclusive and also
303 : * allows the value -1000.
304 : *
305 : * \param[in] ranges The ranges used to limit the integer.
306 : */
307 48 : validator_integer::validator_integer(string_list_t const & range_list)
308 : {
309 48 : range_t range;
310 268 : for(auto r : range_list)
311 : {
312 224 : std::string::size_type const pos(r.find("..."));
313 224 : if(pos == std::string::npos)
314 : {
315 201 : if(!convert_string(r, range.f_minimum))
316 : {
317 2 : cppthread::log << cppthread::log_level_t::error
318 1 : << r
319 : << " is not a valid value for your ranges;"
320 : " it must only be digits, optionally preceeded by a sign (+ or -)"
321 1 : " and not overflow an int64_t value."
322 1 : << cppthread::end;
323 1 : continue;
324 : }
325 199 : range.f_maximum = range.f_minimum;
326 : }
327 : else
328 : {
329 45 : std::string min_value(r.substr(0, pos));
330 24 : boost::trim(min_value);
331 25 : if(!convert_string(min_value, range.f_minimum))
332 : {
333 2 : cppthread::log << cppthread::log_level_t::error
334 1 : << min_value
335 : << " is not a valid value for your ranges;"
336 : " it must only be digits, optionally preceeded by a sign (+ or -)"
337 1 : " and not overflow an int64_t value."
338 1 : << cppthread::end;
339 1 : continue;
340 : }
341 :
342 44 : std::string max_value(r.substr(pos + 3));
343 23 : boost::trim(max_value);
344 24 : if(!convert_string(max_value, range.f_maximum))
345 : {
346 2 : cppthread::log << cppthread::log_level_t::error
347 1 : << max_value
348 : << " is not a valid value for your ranges;"
349 : " it must only be digits, optionally preceeded by a sign (+ or -)"
350 1 : " and not overflow an int64_t value."
351 1 : << cppthread::end;
352 1 : continue;
353 : }
354 :
355 23 : if(range.f_minimum > range.f_maximum)
356 : {
357 2 : cppthread::log << cppthread::log_level_t::error
358 1 : << min_value
359 1 : << " has to be smaller or equal to "
360 1 : << max_value
361 1 : << "; you have an invalid range."
362 1 : << cppthread::end;
363 1 : continue;
364 : }
365 : }
366 220 : f_allowed_values.push_back(range);
367 : }
368 48 : }
369 :
370 :
371 : /** \brief Return the name of this validator.
372 : *
373 : * This function returns "integer".
374 : *
375 : * \return "integer".
376 : */
377 42 : std::string const validator_integer::name() const
378 : {
379 42 : return std::string("integer");
380 : }
381 :
382 :
383 : /** \brief Determine whether value is an integer.
384 : *
385 : * This function verifies that the specified value is a valid integer.
386 : *
387 : * It makes sures that the value is only composed of digits (`[0-9]+`).
388 : * It may also start with a sign (`[-+]?`).
389 : *
390 : * The function also makes sure that the value fits in an `int64_t` value.
391 : *
392 : * \todo
393 : * Add support for binary, octal, hexadecimal.
394 : *
395 : * \param[in] value The value to validate.
396 : *
397 : * \return true if the value validates.
398 : */
399 135673 : bool validator_integer::validate(std::string const & value) const
400 : {
401 135673 : std::int64_t result(0);
402 135673 : if(convert_string(value, result))
403 : {
404 51663 : if(f_allowed_values.empty())
405 : {
406 1543 : return true;
407 : }
408 :
409 252610 : for(auto f : f_allowed_values)
410 : {
411 215443 : if(result >= f.f_minimum
412 119969 : && result <= f.f_maximum)
413 : {
414 12953 : return true;
415 : }
416 : }
417 37167 : return false;
418 : }
419 :
420 84010 : return false;
421 : }
422 :
423 :
424 : /** \brief Convert a string to an std::int64_t value.
425 : *
426 : * This function is used to convert a string to an integer with full
427 : * boundary verification.
428 : *
429 : * The result can also get checked against ranges as defined in the
430 : * constructor.
431 : *
432 : * \return true if the conversion succeeded.
433 : */
434 136022 : bool validator_integer::convert_string(std::string const & value, std::int64_t & result)
435 : {
436 136022 : std::uint64_t integer(0);
437 136022 : char const * s(value.c_str());
438 :
439 136022 : char sign('\0');
440 136022 : if(*s == '-' || *s == '+')
441 : {
442 52283 : sign = *s;
443 52283 : ++s;
444 : }
445 :
446 136022 : if(*s == '\0')
447 : {
448 : // empty string, not considered valid
449 : //
450 8 : return false;
451 : }
452 :
453 : for(;;)
454 : {
455 1909076 : char const c(*s++);
456 1909076 : if(c == '\0')
457 : {
458 : // valid
459 : //
460 52003 : if(sign == '-')
461 : {
462 20736 : if(integer > 0x8000000000000000ULL)
463 : {
464 2 : return false;
465 : }
466 20734 : result = -integer;
467 : }
468 : else
469 : {
470 31267 : if(integer > 0x7FFFFFFFFFFFFFFFULL)
471 : {
472 3 : return false;
473 : }
474 31264 : result = integer;
475 : }
476 51998 : return true;
477 : }
478 1857073 : if(c < '0' || c > '9')
479 : {
480 : // invalid digit
481 : //
482 84007 : return false;
483 : }
484 :
485 1773066 : std::uint64_t const old(integer);
486 1773066 : integer = integer * 10 + c - '0';
487 1773066 : if(integer < old)
488 : {
489 : // overflow
490 : //
491 4 : return false;
492 : }
493 1773062 : }
494 : }
495 :
496 :
497 :
498 :
499 :
500 :
501 :
502 :
503 :
504 :
505 18 : validator_regex::validator_regex(string_list_t const & regex_list)
506 : {
507 18 : if(regex_list.size() > 1)
508 : {
509 8 : cppthread::log << cppthread::log_level_t::error
510 4 : << "validator_regex() only supports one parameter; "
511 8 : << regex_list.size()
512 4 : << " were supplied; single or double quotation may be required?"
513 4 : << cppthread::end;
514 4 : return;
515 : }
516 :
517 28 : std::string regex;
518 14 : if(!regex_list.empty())
519 : {
520 14 : regex = regex_list[0];
521 : }
522 14 : std::regex::flag_type flags = std::regex_constants::extended;
523 28 : if(regex.length() >= 2
524 14 : && regex[0] == '/')
525 : {
526 11 : auto it(regex.end());
527 30 : for(--it; it != regex.begin(); --it)
528 : {
529 29 : if(*it == '/')
530 : {
531 10 : break;
532 : }
533 19 : switch(*it)
534 : {
535 4 : case 'i':
536 4 : flags |= std::regex_constants::icase;
537 4 : break;
538 :
539 15 : default:
540 30 : cppthread::log << cppthread::log_level_t::error
541 15 : << "unsupported regex flag "
542 15 : << *it
543 15 : << " in regular expression \""
544 15 : << regex
545 15 : << "\"."
546 15 : << cppthread::end;
547 15 : break;
548 :
549 : }
550 : }
551 11 : if(it == regex.begin())
552 : {
553 2 : cppthread::log << cppthread::log_level_t::error
554 1 : << "invalid regex definition, ending / is missing in \""
555 1 : << regex
556 1 : << "\"."
557 1 : << cppthread::end;
558 :
559 1 : f_regex = std::regex(std::string(regex.begin() + 1, regex.end()), flags);
560 : }
561 : else
562 : {
563 10 : f_regex = std::regex(std::string(regex.begin() + 1, it), flags);
564 : }
565 : }
566 : else
567 : {
568 3 : f_regex = std::regex(regex, flags);
569 : }
570 : }
571 :
572 :
573 : /** \brief Return the name of this validator.
574 : *
575 : * This function returns "regex".
576 : *
577 : * \return "regex".
578 : */
579 7 : std::string const validator_regex::name() const
580 : {
581 7 : return std::string("regex");
582 : }
583 :
584 :
585 : /** \brief Check the value against a regular expression.
586 : *
587 : * This function is used to match the value of an argument against a
588 : * regular expression. It returns true when it does match.
589 : *
590 : * \param[in] value The value to be validated.
591 : *
592 : * \return true on a match.
593 : */
594 75 : bool validator_regex::validate(std::string const & value) const
595 : {
596 150 : std::smatch info;
597 150 : return std::regex_match(value, info, f_regex);
598 : }
599 :
600 :
601 :
602 :
603 :
604 6 : } // namespace advgetopt
605 : // vim: ts=4 sw=4 et
|