advgetopt 2.0.49
Parse complex command line arguments and configuration files in C++.
validator_duration.cpp
Go to the documentation of this file.
1// Copyright (c) 2006-2025 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
31// self
32//
34
36
37
38// cppthread
39//
40#include <cppthread/log.h>
41
42
43// snapdev
44//
45#include <snapdev/trim_string.h>
46
47
48// last include
49//
50#include <snapdev/poison.h>
51
52
53
54namespace advgetopt
55{
56
57
58
59namespace
60{
61
62
63
65 : public validator_factory
66{
67public:
69 {
70 validator::register_validator(*this);
71 }
72
73 virtual std::string get_name() const override
74 {
75 return std::string("duration");
76 }
77
78 virtual std::shared_ptr<validator> create(string_list_t const & data) const override
79 {
80 return std::make_shared<validator_duration>(data);
81 }
82};
83
85
86
87
88} // no name namespace
89
90
91
92
93
149{
150 for(auto r : flag_list)
151 {
152 if(r == "small")
153 {
155 }
156 else if(r == "large")
157 {
159 }
160 else
161 {
163 std::string::size_type const pos(r.find("..."));
164 if(pos == std::string::npos)
165 {
166 if(!convert_string(r, f_flags, range.f_minimum))
167 {
168 cppthread::log << cppthread::log_level_t::error
169 << r
170 << " is not a valid duration or flag."
171 << cppthread::end;
172 continue;
173 }
174 range.f_maximum = range.f_minimum;
175 }
176 else
177 {
178 std::string const min_value(snapdev::trim_string(r.substr(0, pos)));
179 if(!min_value.empty())
180 {
181 if(!convert_string(min_value, f_flags, range.f_minimum))
182 {
183 cppthread::log << cppthread::log_level_t::error
184 << min_value
185 << " is not a valid value for your range's start;"
186 " it must be a valid duration,"
187 " optionally preceded by a sign (+ or -)."
188 << cppthread::end;
189 continue;
190 }
191 }
192
193 std::string const max_value(snapdev::trim_string(r.substr(pos + 3)));
194 if(!max_value.empty())
195 {
196 if(!convert_string(max_value, f_flags, range.f_maximum))
197 {
198 cppthread::log << cppthread::log_level_t::error
199 << max_value
200 << " is not a valid value for your range's end;"
201 " it must be a valid duration,"
202 " optionally preceded by a sign (+ or -)."
203 << cppthread::end;
204 continue;
205 }
206 }
207
208 if(range.f_minimum > range.f_maximum)
209 {
210 cppthread::log << cppthread::log_level_t::error
211 << min_value
212 << " has to be smaller or equal to "
213 << max_value
214 << "; you have an invalid duration range."
215 << cppthread::end;
216 continue;
217 }
218 }
219 f_allowed_values.push_back(range);
220 }
221 }
222}
223
224
231std::string validator_duration::name() const
232{
233 return std::string("duration");
234}
235
236
249bool validator_duration::validate(std::string const & value) const
250{
251 double result(0);
252 if(!convert_string(value, f_flags, result))
253 {
254 set_error("not a valid duration.");
255 return false;
256 }
257
258 if(f_allowed_values.empty())
259 {
260 return true;
261 }
262
263 for(auto f : f_allowed_values)
264 {
265 if(result >= f.f_minimum
266 && result <= f.f_maximum)
267 {
268 return true;
269 }
270 }
271
272 set_error("out of range.");
273 return false;
274}
275
276
315 std::string const & value
316 , flag_t flags
317 , double & result)
318{
319 result = 0.0;
320
321 // TODO: use libutf8 to walk the string and skip all UTF-8 spaces
322 //
323 char const * s(value.c_str());
324 while(isspace(*s))
325 {
326 ++s;
327 }
328 if(*s == '\0')
329 {
330 return false;
331 }
332
333 double r(0.0);
334 while(*s != '\0')
335 {
336 // get the number
337 //
338 char const * number(s);
339 if(*s == '+'
340 || *s == '-')
341 {
342 ++s;
343 }
344 while(*s >= '0' && *s <= '9')
345 {
346 ++s;
347 }
348 if(*s == '.')
349 {
350 do
351 {
352 ++s;
353 }
354 while(*s >= '0' && *s <= '9');
355 if(*s == '.')
356 {
357 // this would otherwise allow "2..8 year" which is probably
358 // a mistake (i.e. not "2.s" and ".8 year")
359 //
360 return false;
361 }
362 }
363 double n(0.0);
364#pragma GCC diagnostic push
365#pragma GCC diagnostic ignored "-Wrestrict"
367 (*number == '.' ? "0" : "") + std::string(number, s - number)
368 , n))
369 {
370 return false;
371 }
372#pragma GCC diagnostic pop
373
374 while(isspace(*s))
375 {
376 ++s;
377 }
378
379 // get the suffix
380 //
381 std::string suffix;
382 for(;; ++s)
383 {
384 if(*s >= 'A'
385 && *s <= 'Z')
386 {
387 // make lowercase as we're at it
388 //
389 suffix += *s + 0x20;
390 }
391 else if(*s >= 'a'
392 && *s <= 'z')
393 {
394 suffix += *s;
395 }
396 else
397 {
398 break;
399 }
400 }
401
402 double factor(1.0);
403 if(!suffix.empty()) // empty == "seconds"
404 {
405 switch(suffix[0])
406 {
407 case 'd':
408 if(suffix == "d"
409 || suffix == "day"
410 || suffix == "days")
411 {
412 factor = 86400.0;
413 }
414 else
415 {
416 return false;
417 }
418 break;
419
420 case 'h':
421 if(suffix == "h"
422 || suffix == "hour"
423 || suffix == "hours")
424 {
425 factor = 3600.0;
426 }
427 else
428 {
429 return false;
430 }
431 break;
432
433 case 'm':
434 if(suffix == "m")
435 {
436 if((flags & VALIDATOR_DURATION_LONG) != 0)
437 {
438 factor = 86400.0 * 30.0; // 1 month
439 }
440 else
441 {
442 factor = 60.0; // 1 minute
443 }
444 }
445 else if(suffix == "minute"
446 || suffix == "minutes")
447 {
448 factor = 60.0;
449 }
450 else if(suffix == "month"
451 || suffix == "months")
452 {
453 factor = 86400.0 * 30.0;
454 }
455 else
456 {
457 return false;
458 }
459 break;
460
461 case 's':
462 if(suffix != "s"
463 && suffix != "second"
464 && suffix != "seconds")
465 {
466 return false;
467 }
468 break;
469
470 case 'w':
471 if(suffix == "w"
472 || suffix == "week"
473 || suffix == "weeks")
474 {
475 factor = 86400.0 * 7.0;
476 }
477 else
478 {
479 return false;
480 }
481 break;
482
483 case 'y':
484 if(suffix == "y"
485 || suffix == "year"
486 || suffix == "years")
487 {
488 factor = 86400.0 * 365.0;
489 }
490 else
491 {
492 return false;
493 }
494 break;
495
496 default:
497 return false;
498
499 }
500 }
501
502 // TODO: catch ERANGE errors
503 //
504 r += n * factor;
505
506 while(isspace(*s))
507 {
508 ++s;
509 }
510 }
511
512 result = r;
513
514 return true;
515}
516
517
518
519} // namespace advgetopt
520// vim: ts=4 sw=4 et
virtual std::shared_ptr< validator > create(string_list_t const &data) const override
static bool convert_string(std::string const &number, double &result)
Convert a string to a double value.
static bool convert_string(std::string const &duration, flag_t flags, double &result)
Convert a string to a double value representing a duration.
virtual std::string name() const override
Return the name of this validator.
virtual bool validate(std::string const &value) const override
Determine whether value is a valid duration.
validator_duration(string_list_t const &data)
Initialize the duration validator.
static constexpr flag_t VALIDATOR_DURATION_LONG
void set_error(std::string const &msg) const
The advgetopt environment to parse command line options.
Definition version.h:37
constexpr flag_t option_flags_merge()
Definition flags.h:87
std::vector< std::string > string_list_t
Definition utils.h:41
Declaration of validators which can be used to verify the parameters.
Declaration of validators which can be used to verify the parameters.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.