Line data Source code
1 : // Copyright (c) 2006-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/advgetopt
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Implementation of the integer validator.
22 : *
23 : * This validator is used to verify that a parameter represents a valid
24 : * integer.
25 : *
26 : * Note that the validator supports 64 bits integers by default. You can
27 : * reduce the size by defining your parameter with a range as required
28 : * by your application.
29 : *
30 : * The value is checked for overflows on a signed 64 bits value.
31 : */
32 :
33 : // self
34 : //
35 : #include "advgetopt/validator_integer.h"
36 :
37 :
38 : //// advgetopt lib
39 : ////
40 : //#include "advgetopt/exception.h"
41 :
42 :
43 : // cppthread lib
44 : //
45 : #include <cppthread/log.h>
46 :
47 :
48 : // snapdev lib
49 : //
50 : #include <snapdev/not_used.h>
51 :
52 :
53 : // boost lib
54 : //
55 : #include <boost/algorithm/string/trim.hpp>
56 :
57 :
58 : // last include
59 : //
60 : #include <snapdev/poison.h>
61 :
62 :
63 :
64 :
65 : namespace advgetopt
66 : {
67 :
68 :
69 :
70 : namespace
71 : {
72 :
73 :
74 :
75 2 : class validator_integer_factory
76 : : public validator_factory
77 : {
78 : public:
79 2 : validator_integer_factory()
80 2 : {
81 2 : validator::register_validator(*this);
82 2 : }
83 :
84 4 : virtual std::string get_name() const override
85 : {
86 4 : return std::string("integer");
87 : }
88 :
89 48 : virtual std::shared_ptr<validator> create(string_list_t const & data) const override
90 : {
91 48 : snapdev::NOT_USED(data); // ignore `data`
92 48 : return std::make_shared<validator_integer>(data);
93 : }
94 : };
95 :
96 2 : validator_integer_factory g_validator_integer_factory;
97 :
98 :
99 :
100 : } // no name namespace
101 :
102 :
103 :
104 :
105 :
106 : /** \brief Initialize the integer validator.
107 : *
108 : * The constructor accepts a string with values and ranges which are
109 : * used to limit the values that can be used with this parameter.
110 : *
111 : * Remember that the default does not have to be included in these
112 : * values. It will still be viewed as \em valid.
113 : *
114 : * The string uses the following format:
115 : *
116 : * \code
117 : * start: range
118 : * | start ',' range
119 : *
120 : * range: number
121 : * | number '...' number
122 : *
123 : * number: [-+]?[0-9]+
124 : * \endcode
125 : *
126 : * Note that a single number is considered to be a range and is managed
127 : * the exact same way. A value which matches any of the ranges is considered
128 : * valid.
129 : *
130 : * Example:
131 : *
132 : * \code
133 : * "-100...100,-1000"
134 : * \endcode
135 : *
136 : * This example allows all values between -100 and +100 inclusive and also
137 : * allows the value -1000.
138 : *
139 : * \param[in] ranges The ranges used to limit the integer.
140 : */
141 48 : validator_integer::validator_integer(string_list_t const & range_list)
142 : {
143 48 : range_t range;
144 271 : for(auto r : range_list)
145 : {
146 227 : std::string::size_type const pos(r.find("..."));
147 227 : if(pos == std::string::npos)
148 : {
149 204 : if(!convert_string(r, range.f_minimum))
150 : {
151 2 : cppthread::log << cppthread::log_level_t::error
152 1 : << r
153 : << " is not a valid value for your ranges;"
154 : " it must only be digits, optionally preceeded by a sign (+ or -)"
155 1 : " and not overflow an int64_t value."
156 2 : << cppthread::end;
157 1 : continue;
158 : }
159 202 : range.f_maximum = range.f_minimum;
160 : }
161 : else
162 : {
163 45 : std::string min_value(r.substr(0, pos));
164 24 : boost::trim(min_value);
165 25 : if(!convert_string(min_value, range.f_minimum))
166 : {
167 2 : cppthread::log << cppthread::log_level_t::error
168 1 : << min_value
169 : << " is not a valid value for your ranges;"
170 : " it must only be digits, optionally preceeded by a sign (+ or -)"
171 1 : " and not overflow an int64_t value."
172 2 : << cppthread::end;
173 1 : continue;
174 : }
175 :
176 44 : std::string max_value(r.substr(pos + 3));
177 23 : boost::trim(max_value);
178 24 : if(!convert_string(max_value, range.f_maximum))
179 : {
180 2 : cppthread::log << cppthread::log_level_t::error
181 1 : << max_value
182 : << " is not a valid value for your ranges;"
183 : " it must only be digits, optionally preceeded by a sign (+ or -)"
184 1 : " and not overflow an int64_t value."
185 2 : << cppthread::end;
186 1 : continue;
187 : }
188 :
189 23 : if(range.f_minimum > range.f_maximum)
190 : {
191 2 : cppthread::log << cppthread::log_level_t::error
192 1 : << min_value
193 1 : << " has to be smaller or equal to "
194 1 : << max_value
195 1 : << "; you have an invalid range."
196 2 : << cppthread::end;
197 1 : continue;
198 : }
199 : }
200 223 : f_allowed_values.push_back(range);
201 : }
202 48 : }
203 :
204 :
205 : /** \brief Return the name of this validator.
206 : *
207 : * This function returns "integer".
208 : *
209 : * \return "integer".
210 : */
211 42 : std::string validator_integer::name() const
212 : {
213 42 : return std::string("integer");
214 : }
215 :
216 :
217 : /** \brief Determine whether value is an integer.
218 : *
219 : * This function verifies that the specified value is a valid integer.
220 : *
221 : * It makes sures that the value is only composed of digits (`[0-9]+`).
222 : * It may also start with a sign (`[-+]?`).
223 : *
224 : * The function also makes sure that the value fits in an `int64_t` value.
225 : *
226 : * If ranges were defined, then the function also verifies that the
227 : * value is within at least one of the ranges.
228 : *
229 : * \todo
230 : * Add support for binary, octal, hexadecimal.
231 : *
232 : * \param[in] value The value to validate.
233 : *
234 : * \return true if the value validates.
235 : */
236 135722 : bool validator_integer::validate(std::string const & value) const
237 : {
238 135722 : std::int64_t result(0);
239 135722 : if(convert_string(value, result))
240 : {
241 51712 : if(f_allowed_values.empty())
242 : {
243 1494 : return true;
244 : }
245 :
246 262136 : for(auto f : f_allowed_values)
247 : {
248 220196 : if(result >= f.f_minimum
249 112565 : && result <= f.f_maximum)
250 : {
251 8278 : return true;
252 : }
253 : }
254 41940 : return false;
255 : }
256 :
257 84010 : return false;
258 : }
259 :
260 :
261 : /** \brief Convert a string to an std::int64_t value.
262 : *
263 : * This function is used to convert a string to an integer with full
264 : * boundary verification.
265 : *
266 : * \warning
267 : * There is no range checks in this function since it does not have access
268 : * to the ranges (i.e. it is static).
269 : *
270 : * \param[in] value The value to be converted to an integer.
271 : * \param[out] result The resulting integer.
272 : *
273 : * \return true if the conversion succeeded.
274 : */
275 136118 : bool validator_integer::convert_string(std::string const & value, std::int64_t & result)
276 : {
277 136118 : std::uint64_t integer(0);
278 136118 : char const * s(value.c_str());
279 :
280 136118 : char sign('\0');
281 136118 : if(*s == '-' || *s == '+')
282 : {
283 52040 : sign = *s;
284 52040 : ++s;
285 : }
286 :
287 136118 : if(*s == '\0')
288 : {
289 : // empty string, not considered valid
290 : //
291 8 : return false;
292 : }
293 :
294 : for(;;)
295 : {
296 1910182 : char const c(*s++);
297 1910182 : if(c == '\0')
298 : {
299 : // valid
300 : //
301 52096 : if(sign == '-')
302 : {
303 20541 : if(integer > 0x8000000000000000ULL)
304 : {
305 2 : return false;
306 : }
307 20539 : result = -integer;
308 : }
309 : else
310 : {
311 31555 : if(integer > 0x7FFFFFFFFFFFFFFFULL)
312 : {
313 3 : return false;
314 : }
315 31552 : result = integer;
316 : }
317 52091 : return true;
318 : }
319 1858086 : if(c < '0' || c > '9')
320 : {
321 : // invalid digit
322 : //
323 84010 : return false;
324 : }
325 :
326 1774076 : std::uint64_t const old(integer);
327 1774076 : integer = integer * 10 + c - '0';
328 1774076 : if(integer < old)
329 : {
330 : // overflow
331 : //
332 4 : return false;
333 : }
334 1774072 : }
335 : }
336 :
337 :
338 :
339 6 : } // namespace advgetopt
340 : // vim: ts=4 sw=4 et
|