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