LCOV - code coverage report
Current view: top level - advgetopt - validator.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 177 177 100.0 %
Date: 2019-09-16 03:06:47 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             : #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

Generated by: LCOV version 1.12