LCOV - code coverage report
Current view: top level - advgetopt - validator.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 179 179 100.0 %
Date: 2020-11-13 17:54:34 Functions: 22 26 84.6 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13