Line data Source code
1 : // Copyright (c) 2006-2021 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 Advanced getopt data access implementation.
22 : *
23 : * The advgetopt class has many function used to access the data in the
24 : * class. These functions are gathered here.
25 : */
26 :
27 : // self
28 : //
29 : #include "advgetopt/advgetopt.h"
30 :
31 : #include "advgetopt/conf_file.h"
32 : #include "advgetopt/exception.h"
33 : #include "advgetopt/version.h"
34 :
35 :
36 : // cppthread lib
37 : //
38 : #include <cppthread/log.h>
39 :
40 :
41 : // last include
42 : //
43 : #include <snapdev/poison.h>
44 :
45 :
46 :
47 :
48 : namespace advgetopt
49 : {
50 :
51 :
52 :
53 :
54 :
55 : /** \brief Check whether a parameter is defined.
56 : *
57 : * This function returns true if the specified parameter is found as part of
58 : * the command line options.
59 : *
60 : * You must specify the long name of the option. So a `--verbose` option can
61 : * be checked with:
62 : *
63 : * \code
64 : * if(is_defined("verbose")) ...
65 : * \endcode
66 : *
67 : * For options that come with a short name, you may also specify the short
68 : * name. This is done with a string in this case. It can be a UTF-8
69 : * character. The short name is used if the string represents exactly one
70 : * Unicode character. So the following is equivalent to the previous
71 : * example, assuming your verbose definition has `v` as the short name:
72 : *
73 : * \code
74 : * if(is_defined("v")) ...
75 : * \endcode
76 : *
77 : * \note
78 : * This function returns true when the option was found on the command line,
79 : * the environment variable, or a configuration file. It returns false if
80 : * the option is defined, but was not specified anywhere by the client using
81 : * your program. Also, specifying the option in one of those three locations
82 : * when not allowed at that location will not result in this flag being raised.
83 : *
84 : * \param[in] name The long name or short name of the option to check.
85 : *
86 : * \return true if the option was defined in a configuration file, the
87 : * environment variable, or the command line.
88 : */
89 1213 : bool getopt::is_defined(std::string const & name) const
90 : {
91 1213 : is_parsed();
92 :
93 2418 : option_info::pointer_t opt(get_option(name));
94 1209 : if(opt != nullptr)
95 : {
96 903 : return opt->is_defined();
97 : }
98 :
99 306 : return false;
100 : }
101 :
102 :
103 : /** \brief Retrieve the number of arguments.
104 : *
105 : * This function returns the number of arguments that were specified after
106 : * the named option.
107 : *
108 : * The function returns zero if the argument was never specified on the
109 : * command line. If the option accepts exactly one parameter (i.e. not
110 : * marked as a multiple arguments option: GETOPT_FLAG_MULTIPLE) then
111 : * the function returns either zero (not specified) or one (specified
112 : * at least once.)
113 : *
114 : * \param[in] name The name of the option to check.
115 : *
116 : * \return The number of arguments specified on the command line or zero.
117 : */
118 611 : size_t getopt::size(std::string const & name) const
119 : {
120 611 : is_parsed();
121 :
122 1216 : option_info::pointer_t opt(get_option(name));
123 608 : if(opt != nullptr)
124 : {
125 396 : return opt->size();
126 : }
127 212 : return 0;
128 : }
129 :
130 :
131 : /** \brief Check whether an option has a default value.
132 : *
133 : * Some parameters may be given a default. This function is used to
134 : * detect whether such a default value is defined.
135 : *
136 : * \note
137 : * This function is particularly useful in the event the default value
138 : * may be an empty string.
139 : *
140 : * \exception getopt_exception_undefined
141 : * The getopt_exception_undefined exception is raised if this function is
142 : * called with an empty \p name.
143 : *
144 : * \param[in] name The name of the parameter of which you want to know
145 : * whether it has a default value or not.
146 : *
147 : * \return true if the default value was defined (even if an empty string.)
148 : */
149 79 : bool getopt::has_default(std::string const & name) const
150 : {
151 79 : if(name.empty())
152 : {
153 2 : throw getopt_logic_error("argument name cannot be empty.");
154 : }
155 :
156 154 : option_info::pointer_t opt(get_option(name));
157 77 : if(opt != nullptr)
158 : {
159 72 : return opt->has_default();
160 : }
161 :
162 5 : return false;
163 : }
164 :
165 :
166 : /** \brief Get the default value for this option.
167 : *
168 : * When an option is not defined, you may use this function to retrieve its
169 : * default instead. This is actually done automatically when you call the
170 : * get_string() or get_long() functions.
171 : *
172 : * An option without a default has this function returning nullptr.
173 : *
174 : * \note
175 : * Whether an option has a default value should be checked with the
176 : * has_default() function which returns true when the default value
177 : * was defined. An option with an empty string as the default is
178 : * a valid case which cannot be detected otherwise.
179 : *
180 : * \exception getopt_exception_undefined
181 : * The getopt_exception_undefined exception is raised if this function is
182 : * called with an empty \p name.
183 : *
184 : * \param[in] name The name of the parameter of which you want to retrieve
185 : * the default value.
186 : *
187 : * \return The default value or an empty string if no value is defined.
188 : */
189 553 : std::string getopt::get_default(std::string const & name) const
190 : {
191 553 : if(name.empty())
192 : {
193 2 : throw getopt_logic_error("argument name cannot be empty.");
194 : }
195 :
196 1102 : option_info::pointer_t opt(get_option(name));
197 551 : if(opt != nullptr)
198 : {
199 318 : return opt->get_default();
200 : }
201 :
202 233 : return std::string();
203 : }
204 :
205 :
206 : /** \brief This function retrieves an argument as a long value.
207 : *
208 : * This function reads the specified argument from the named option and
209 : * transforms it to a long value. It then checks the result against the
210 : * specified minimum and maximum range.
211 : *
212 : * The function name represents an argument that needs to be defined. You
213 : * can test whether it was defined on the command line with the is_defined()
214 : * function. The index must be between 0 and 'size() - 1' inclusive. If
215 : * the item was not defined, then size() returns zero and you cannot call
216 : * this function.
217 : *
218 : * The function does not check the validity of the minimum and maximum
219 : * parameters. If \p min \> \p max is true then the function will always
220 : * fail with a call to usage() as no value can be defined between \p min
221 : * and \p max in that case. The minimum and maximum values are inclusive,
222 : * so a range of 1 to 9 is defined with exactly 1 and 9 in min and max.
223 : * For example, the z library compression could be retrieved with:
224 : *
225 : * \code
226 : * int level(6); // default to 6
227 : * if(opt.is_defined("zlevel"))
228 : * {
229 : * zlevel = opt.get_long("zlevel", 0, 1, 9);
230 : * }
231 : * \endcode
232 : *
233 : * Note that the function can be used to read unsigned numbers, however
234 : * at this point getopt does not really support negative numbers (i.e. because
235 : * -\<number> is viewed as an option.)
236 : *
237 : * \exception getopt_exception_undefined
238 : * The getopt_exception_undefined exception is raised if \p name was not
239 : * found on the command line and it has no default, or if \p idx is
240 : * out of bounds.
241 : *
242 : * \param[in] name The name of the option to retrieve.
243 : * \param[in] idx The index of the argument to retrieve.
244 : * \param[in] min The minimum value that will be returned (inclusive).
245 : * \param[in] max The maximum value that will be returned (inclusive).
246 : *
247 : * \return The argument as a long.
248 : */
249 130 : long getopt::get_long(std::string const & name, int idx, long min, long max) const
250 : {
251 130 : is_parsed();
252 :
253 260 : option_info::pointer_t opt(get_option(name));
254 130 : if(opt == nullptr)
255 : {
256 : throw getopt_logic_error(
257 : "there is no --"
258 6 : + name
259 9 : + " option defined.");
260 : }
261 :
262 127 : long result(0);
263 127 : if(!opt->is_defined())
264 : {
265 96 : std::string const d(opt->get_default());
266 48 : if(d.empty())
267 : {
268 : throw getopt_logic_error(
269 : "the --"
270 12 : + name
271 18 : + " option was not defined on the command line and it has no or an empty default.");
272 : }
273 42 : char * end;
274 42 : char const * str(d.c_str());
275 42 : result = strtol(str, &end, 10);
276 42 : if(end != str + d.length())
277 : {
278 : // here we throw because this default value is defined in the
279 : // options of the tool and not by the user
280 : //
281 : throw getopt_logic_error(
282 : "invalid default number \""
283 6 : + d
284 9 : + "\" for option --"
285 9 : + name);
286 : }
287 : }
288 : else
289 : {
290 79 : result = opt->get_long(idx);
291 : }
292 :
293 : // TODO: replace with validators
294 : //
295 118 : if(result < min || result > max)
296 : {
297 4 : cppthread::log << cppthread::log_level_t::error
298 2 : << result
299 2 : << " is out of bounds ("
300 2 : << min
301 2 : << ".."
302 2 : << max
303 2 : << " inclusive) in parameter --"
304 2 : << name
305 2 : << "."
306 4 : << cppthread::end;
307 2 : result = -1;
308 : }
309 :
310 236 : return result;
311 : }
312 :
313 :
314 : /** \brief Get the content of an option as a string.
315 : *
316 : * Get the content of the named parameter as a string. Command line options
317 : * that accept multiple arguments accept the \p idx parameter to
318 : * specify which item you are interested in.
319 : *
320 : * Note that the option must have been specified on the command line or have
321 : * a default value. For options that do not have a default value, you want
322 : * to call the is_defined() function first.
323 : *
324 : * \exception getopt_exception_undefined
325 : * The getopt_exception_undefined exception is raised if \p name was not
326 : * found on the command line and it has no default, or if \p idx is
327 : * out of bounds.
328 : *
329 : * \param[in] name The name of the option to read.
330 : * \param[in] idx The zero based index of a multi-argument command line option.
331 : *
332 : * \return The option argument as a string.
333 : */
334 533 : std::string getopt::get_string(std::string const & name, int idx) const
335 : {
336 533 : is_parsed();
337 :
338 1066 : option_info::pointer_t opt(get_option(name));
339 533 : if(opt == nullptr)
340 : {
341 : throw getopt_logic_error(
342 : "there is no --"
343 6 : + name
344 9 : + " option defined.");
345 : }
346 :
347 530 : if(!opt->is_defined())
348 : {
349 45 : if(opt->has_default())
350 : {
351 40 : return opt->get_default();
352 : }
353 : throw getopt_logic_error(
354 : "the --"
355 10 : + name
356 15 : + " option was not defined on the command line and it has no default.");
357 : }
358 :
359 485 : return opt->get_value(idx);
360 : }
361 :
362 :
363 : /** \brief Retrieve the value of an argument.
364 : *
365 : * This operator returns the value of an argument just like the get_string()
366 : * does when the argument is defined. When the argument is not defined and it
367 : * has no default, it returns an empty string instead of throwing.
368 : *
369 : * The function is only capable of returning the very first value. If this
370 : * argument has the GETOPT_FLAG_MULTIPLE flag set, you probably want to use
371 : * the get_string() instead.
372 : *
373 : * \param[in] name The name of the option to retrieve.
374 : *
375 : * \return The value of that option or an empty string if not defined.
376 : */
377 193 : std::string getopt::operator [] (std::string const & name) const
378 : {
379 193 : is_parsed();
380 :
381 193 : if(name.empty())
382 : {
383 2 : throw getopt_logic_error("argument name cannot be empty.");
384 : }
385 :
386 382 : option_info::pointer_t opt(get_option(name));
387 191 : if(opt == nullptr)
388 : {
389 1 : return std::string();
390 : }
391 :
392 190 : if(!opt->is_defined())
393 : {
394 6 : if(opt->has_default())
395 : {
396 5 : return opt->get_default();
397 : }
398 1 : return std::string();
399 : }
400 :
401 184 : return opt->get_value(0);
402 : }
403 :
404 :
405 : /** \brief Access a parameter in read and write mode.
406 : *
407 : * This function allows you to access an argument which may or may not
408 : * yet exist.
409 : *
410 : * The return value is a reference to that parameter. You can read
411 : * and write to the reference.
412 : *
413 : * A non-existant argument is created only if necessary. That is,
414 : * only if you actually use an assignment operator as follow:
415 : *
416 : * \code
417 : * // straight assignment:
418 : * opt["my-var"] = "123";
419 : *
420 : * // or concatenation:
421 : * opt["my-var"] += "append";
422 : * \endcode
423 : *
424 : * In read mode and unless you defined a default, a non-existant argument
425 : * is viewed as an empty string or 0 if retrieved as a long:
426 : *
427 : * \code
428 : * // if non-existant you get an empty string:
429 : * std::string value = opt["non-existant"];
430 : *
431 : * // if non-existant you get zero:
432 : * long value = opt["non-existant"].get_long();
433 : * \endcode
434 : *
435 : * The get_long() function may generate an error if the parameter is not
436 : * a valid integer. Also when a default is defined, it tries to convert
437 : * the default value to a number and if that fails an error is generated.
438 : *
439 : * \note
440 : * This operator only allows you to access the very first value of
441 : * this option. If the option is marked with GETOPT_FLAG_MULTIPLE,
442 : * you may want to use the get_option() function and then handle
443 : * the option multiple values manually with the option_info::get_value()
444 : * and option_info::set_value().
445 : *
446 : * \warning
447 : * If the option is an alias and the destination is not defined you
448 : * can still get an exception raised.
449 : *
450 : * \param[in] name The name of the option to access.
451 : *
452 : * \return A reference to this option with support for many std::string like
453 : * operators.
454 : */
455 169 : option_info_ref getopt::operator [] (std::string const & name)
456 : {
457 169 : is_parsed();
458 :
459 169 : if(name.empty())
460 : {
461 2 : throw getopt_logic_error("argument name cannot be empty.");
462 : }
463 :
464 334 : option_info::pointer_t opt(get_option(name));
465 167 : if(opt == nullptr)
466 : {
467 54 : if(name.length() == 1)
468 : {
469 2 : throw getopt_logic_error("argument name cannot be one letter if it does not exist in operator [].");
470 : }
471 :
472 : // The option doesn't exist yet, create it
473 : //
474 52 : opt = std::make_shared<option_info>(name);
475 52 : opt->add_flag(GETOPT_FLAG_DYNAMIC_CONFIGURATION);
476 52 : f_options_by_name[name] = opt;
477 : }
478 :
479 330 : return option_info_ref(opt);
480 : }
481 :
482 :
483 : /** \brief Generate a string describing whether we're using the sanitizer.
484 : *
485 : * This function determines whether this library was compiled with the
486 : * sanitizer extension. If so, then it will return detail about which
487 : * feature was compiled in.
488 : *
489 : * If no sanitizer options were compiled in, then it returns a
490 : * message saying so.
491 : *
492 : * \return A string with details about the sanitizer.
493 : */
494 2 : std::string getopt::sanitizer_details()
495 : {
496 2 : std::string result;
497 : #if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
498 : #if defined(__SANITIZE_ADDRESS__)
499 2 : result += "The address sanitizer is compiled in.\n";
500 : #endif
501 : #if defined(__SANITIZE_THREAD__)
502 : result += "The thread sanitizer is compiled in.\n";
503 : #endif
504 : #else
505 : result += "The address and thread sanitizers are not compiled in.\n";
506 : #endif
507 2 : return result;
508 : }
509 :
510 :
511 : /** \brief Process the system options.
512 : *
513 : * If you have the GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS flag turned on,
514 : * then several options are automatically added to your list of supported
515 : * options, such as `--version`.
516 : *
517 : * This function processes these options if any were used by the client.
518 : *
519 : * If the function finds one or more system flags as being defined, it
520 : * returns a non-zero set of SYSTEM_OPTION_... flags. This can be useful
521 : * to decide whether to continue processing or not.
522 : *
523 : * We define a set of flags that can help you decide whether to continue
524 : * or exit. In most cases, we propose that you exit your program if any
525 : * one of the options was a command. This is done like so:
526 : *
527 : * \code
528 : * if(process_system_options & SYSTEM_OPTION_COMMANDS_MASK) != 0)
529 : * {
530 : * exit(1);
531 : * }
532 : * \endcode
533 : *
534 : * You may still want to continue, though, if other flags where set,
535 : * even if some commands were used. For example, some tools will print
536 : * their version and move forward with there work (i.e. compilers often do
537 : * that to help with logging all the information about a build process,
538 : * including the version of the compiler.)
539 : *
540 : * \param[in] out The stream where output is sent if required.
541 : *
542 : * \return non-zero set of flags if any of the system parameters were processed.
543 : */
544 36 : flag_t getopt::process_system_options(std::basic_ostream<char> & out)
545 : {
546 36 : flag_t result(SYSTEM_OPTION_NONE);
547 :
548 : // --version
549 36 : if(is_defined("version"))
550 : {
551 4 : if(f_options_environment.f_version == nullptr)
552 : {
553 1 : out << "warning: no version found." << std::endl;
554 : }
555 : else
556 : {
557 3 : out << f_options_environment.f_version << std::endl;
558 : }
559 4 : result |= SYSTEM_OPTION_VERSION;
560 : }
561 :
562 : // --has-sanitizer
563 36 : if(is_defined("has-sanitizer"))
564 : {
565 1 : out << sanitizer_details() << std::flush;
566 1 : result |= SYSTEM_OPTION_HELP;
567 : }
568 :
569 : // --compiler-version
570 36 : if(is_defined("compiler-version"))
571 : {
572 1 : out << LIBADVGETOPT_COMPILER_VERSION << std::endl;
573 1 : result |= SYSTEM_OPTION_HELP;
574 : }
575 :
576 : // --help
577 36 : if(is_defined("help"))
578 : {
579 1 : out << usage() << std::endl;
580 1 : result |= SYSTEM_OPTION_HELP;
581 : }
582 :
583 : // --long-help
584 36 : if(is_defined("long-help"))
585 : {
586 1 : out << usage(GETOPT_FLAG_SHOW_ALL) << std::endl;
587 1 : result |= SYSTEM_OPTION_HELP;
588 : }
589 :
590 : // --<group-name>-help
591 : //
592 36 : if(f_options_environment.f_groups != nullptr)
593 : {
594 6 : for(group_description const * grp = f_options_environment.f_groups
595 6 : ; grp->f_group != GETOPT_FLAG_GROUP_NONE
596 : ; ++grp)
597 : {
598 : // the name is not mandatory, without it you do not get the command
599 : // line option but still get the group description
600 : //
601 4 : if(grp->f_name != nullptr
602 4 : && *grp->f_name != '\0')
603 : {
604 8 : std::string const name(grp->f_name);
605 8 : std::string const option_name(name + "-help");
606 4 : if(is_defined(option_name))
607 : {
608 2 : out << usage(grp->f_group) << std::endl;
609 2 : result |= SYSTEM_OPTION_HELP;
610 : }
611 : }
612 : }
613 : }
614 :
615 : // --copyright
616 36 : if(is_defined("copyright"))
617 : {
618 3 : if(f_options_environment.f_copyright == nullptr)
619 : {
620 1 : out << "warning: no copyright notice found." << std::endl;
621 : }
622 : else
623 : {
624 2 : out << f_options_environment.f_copyright << std::endl;
625 : }
626 3 : result |= SYSTEM_OPTION_COPYRIGHT;
627 : }
628 :
629 : // --license
630 36 : if(is_defined("license"))
631 : {
632 4 : if(f_options_environment.f_license == nullptr)
633 : {
634 1 : out << "warning: no license found." << std::endl;
635 : }
636 : else
637 : {
638 3 : out << f_options_environment.f_license << std::endl;
639 : }
640 4 : result |= SYSTEM_OPTION_LICENSE;
641 : }
642 :
643 : // --build-date
644 36 : if(is_defined("build-date"))
645 : {
646 : out << "Built on "
647 2 : << (f_options_environment.f_build_date == nullptr
648 : ? "<no-build-date>"
649 : : f_options_environment.f_build_date)
650 : << " at "
651 2 : << (f_options_environment.f_build_time == nullptr
652 : ? "<no-build-time>"
653 6 : : f_options_environment.f_build_time)
654 2 : << std::endl;
655 2 : result |= SYSTEM_OPTION_BUILD_DATE;
656 : }
657 :
658 : // --environment-variable-name
659 36 : if(is_defined("environment-variable-name"))
660 : {
661 3 : if(f_options_environment.f_environment_variable_name == nullptr
662 2 : || *f_options_environment.f_environment_variable_name == '\0')
663 : {
664 : out << f_options_environment.f_project_name
665 2 : << " does not support an environment variable."
666 2 : << std::endl;
667 : }
668 : else
669 : {
670 1 : out << f_options_environment.f_environment_variable_name << std::endl;
671 : }
672 3 : result |= SYSTEM_OPTION_ENVIRONMENT_VARIABLE_NAME;
673 : }
674 :
675 : // --configuration-filenames
676 36 : if(is_defined("configuration-filenames"))
677 : {
678 6 : string_list_t list(get_configuration_filenames(false, false));
679 3 : if(list.empty())
680 : {
681 : out << f_options_environment.f_project_name
682 1 : << " does not support configuration files."
683 1 : << std::endl;
684 : }
685 : else
686 : {
687 2 : out << "Configuration filenames:" << std::endl;
688 26 : for(auto n : list)
689 : {
690 24 : out << " . " << n << std::endl;
691 : }
692 : }
693 3 : result |= SYSTEM_OPTION_CONFIGURATION_FILENAMES;
694 : }
695 :
696 : // --path-to-option-definitions
697 36 : if(is_defined("path-to-option-definitions"))
698 : {
699 2 : if(f_options_environment.f_options_files_directory == nullptr
700 1 : || *f_options_environment.f_options_files_directory == '\0')
701 : {
702 1 : out << "/usr/share/advgetopt/options/" << std::endl;
703 : }
704 : else
705 : {
706 1 : out << f_options_environment.f_options_files_directory;
707 1 : if(f_options_environment.f_options_files_directory[strlen(f_options_environment.f_options_files_directory) - 1] != '/')
708 : {
709 1 : out << '/';
710 : }
711 1 : out << std::endl;
712 : }
713 2 : result |= SYSTEM_OPTION_PATH_TO_OPTION_DEFINITIONS;
714 : }
715 :
716 : // --config-dir
717 36 : if(is_defined("config-dir"))
718 : {
719 : // these are automatically used in the get_configuration_filenames()
720 : // function, there is nothing for us to do here
721 : //
722 3 : result |= SYSTEM_OPTION_CONFIG_DIR;
723 : }
724 :
725 : // --show-option-sources
726 36 : if(is_defined("show-option-sources"))
727 : {
728 3 : show_option_sources(out);
729 3 : result |= SYSTEM_OPTION_SHOW_OPTION_SOURCES;
730 : }
731 :
732 36 : return result;
733 : }
734 :
735 :
736 :
737 :
738 :
739 6 : } // namespace advgetopt
740 : // vim: ts=4 sw=4 et
|