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