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