Line data Source code
1 : /*
2 : * License:
3 : * Copyright (c) 2006-2019 Made to Order Software Corp. All Rights Reserved
4 : *
5 : * https://snapwebsites.org/
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 1039 : bool getopt::is_defined(std::string const & name) const
98 : {
99 2078 : option_info::pointer_t opt(get_option(name));
100 1039 : if(opt != nullptr)
101 : {
102 727 : return opt->is_defined();
103 : }
104 :
105 312 : return false;
106 : }
107 :
108 :
109 : /** \brief Retrieve the number of arguments.
110 : *
111 : * This function returns the number of arguments that were specified after
112 : * the named option.
113 : *
114 : * The function returns zero if the argument was never specified on the
115 : * command line. If the option accepts exactly one parameter (i.e. not
116 : * marked as a multiple arguments option: GETOPT_FLAG_MULTIPLE) then
117 : * the function returns either zero (not specified) or one (specified
118 : * at least once.)
119 : *
120 : * \param[in] name The name of the option to check.
121 : *
122 : * \return The number of arguments specified on the command line or zero.
123 : */
124 635 : size_t getopt::size(std::string const & name) const
125 : {
126 1270 : option_info::pointer_t opt(get_option(name));
127 635 : if(opt != nullptr)
128 : {
129 408 : return opt->size();
130 : }
131 227 : return 0;
132 : }
133 :
134 :
135 : /** \brief Check whether an option has a default value.
136 : *
137 : * Some parameters may be given a default. This function is used to
138 : * detect whether such a default value is defined.
139 : *
140 : * \note
141 : * This function is particularly useful in the event the default value
142 : * may be an empty string.
143 : *
144 : * \exception getopt_exception_undefined
145 : * The getopt_exception_undefined exception is raised if this function is
146 : * called with an empty \p name.
147 : *
148 : * \param[in] name The name of the parameter of which you want to know
149 : * whether it has a default value or not.
150 : *
151 : * \return true if the default value was defined (even if an empty string.)
152 : */
153 71 : bool getopt::has_default(std::string const & name) const
154 : {
155 71 : if(name.empty())
156 : {
157 2 : throw getopt_logic_error("argument name cannot be empty.");
158 : }
159 :
160 138 : option_info::pointer_t opt(get_option(name));
161 69 : if(opt != nullptr)
162 : {
163 64 : return opt->has_default();
164 : }
165 :
166 5 : return false;
167 : }
168 :
169 :
170 : /** \brief Get the default value for this option.
171 : *
172 : * When an option is not defined, you may use this function to retrieve its
173 : * default instead. This is actually done automatically when you call the
174 : * get_string() or get_long() functions.
175 : *
176 : * An option without a default has this function returning nullptr.
177 : *
178 : * \note
179 : * Whether an option has a default value should be checked with the
180 : * has_default() function which returns true when the default value
181 : * was defined. An option with an empty string as the default is
182 : * a valid case which cannot be detected otherwise.
183 : *
184 : * \exception getopt_exception_undefined
185 : * The getopt_exception_undefined exception is raised if this function is
186 : * called with an empty \p name.
187 : *
188 : * \param[in] name The name of the parameter of which you want to retrieve
189 : * the default value.
190 : *
191 : * \return The default value or an empty string if no value is defined.
192 : */
193 537 : std::string getopt::get_default(std::string const & name) const
194 : {
195 537 : if(name.empty())
196 : {
197 2 : throw getopt_logic_error("argument name cannot be empty.");
198 : }
199 :
200 1070 : option_info::pointer_t opt(get_option(name));
201 535 : if(opt != nullptr)
202 : {
203 310 : return opt->get_default();
204 : }
205 :
206 225 : return std::string();
207 : }
208 :
209 :
210 : /** \brief This function retrieves an argument as a long value.
211 : *
212 : * This function reads the specified argument from the named option and
213 : * transforms it to a long value. It then checks the result against the
214 : * specified minimum and maximum range.
215 : *
216 : * The function name represents an argument that needs to be defined. You
217 : * can test whether it was defined on the command line with the is_defined()
218 : * function. The index must be between 0 and 'size() - 1' inclusive. If
219 : * the item was not defined, then size() returns zero and you cannot call
220 : * this function.
221 : *
222 : * The function does not check the validity of the minimum and maximum
223 : * parameters. If \p min \> \p max is true then the function will always
224 : * fail with a call to usage() as no value can be defined between \p min
225 : * and \p max in that case. The minimum and maximum values are inclusive,
226 : * so a range of 1 to 9 is defined with exactly 1 and 9 in min and max.
227 : * For example, the z library compression could be retrieved with:
228 : *
229 : * \code
230 : * int level(6); // default to 6
231 : * if(opt.is_defined("zlevel"))
232 : * {
233 : * zlevel = opt.get_long("zlevel", 0, 1, 9);
234 : * }
235 : * \endcode
236 : *
237 : * Note that the function can be used to read unsigned numbers, however
238 : * at this point getopt does not really support negative numbers (i.e. because
239 : * -\<number> is viewed as an option.)
240 : *
241 : * \exception getopt_exception_undefined
242 : * The getopt_exception_undefined exception is raised if \p name was not
243 : * found on the command line and it has no default, or if \p idx is
244 : * out of bounds.
245 : *
246 : * \param[in] name The name of the option to retrieve.
247 : * \param[in] idx The index of the argument to retrieve.
248 : * \param[in] min The minimum value that will be returned (inclusive).
249 : * \param[in] max The maximum value that will be returned (inclusive).
250 : *
251 : * \return The argument as a long.
252 : */
253 122 : long getopt::get_long(std::string const & name, int idx, long min, long max)
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 : 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 6 : + "\" 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 2 : << 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 1042 : option_info::pointer_t opt(get_option(name));
339 521 : if(opt == nullptr)
340 : {
341 : throw getopt_logic_error(
342 : "there is no --"
343 6 : + name
344 9 : + " option defined.");
345 : }
346 :
347 518 : if(!opt->is_defined())
348 : {
349 41 : if(opt->has_default())
350 : {
351 36 : 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 477 : 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 : if(name.empty())
380 : {
381 2 : throw getopt_logic_error("argument name cannot be empty.");
382 : }
383 :
384 382 : option_info::pointer_t opt(get_option(name));
385 191 : if(opt == nullptr)
386 : {
387 1 : return std::string();
388 : }
389 :
390 190 : if(!opt->is_defined())
391 : {
392 6 : if(opt->has_default())
393 : {
394 5 : return opt->get_default();
395 : }
396 1 : return std::string();
397 : }
398 :
399 184 : return opt->get_value(0);
400 : }
401 :
402 :
403 : /** \brief Access a parameter in read and write mode.
404 : *
405 : * This function allows you to access an argument which may or may not
406 : * yet exist.
407 : *
408 : * The return value is a reference to that parameter. You can read
409 : * and write to the reference.
410 : *
411 : * A non-existant argument is created only if necessary. That is,
412 : * only if you actually use an assignment operator as follow:
413 : *
414 : * \code
415 : * // straight assignment:
416 : * opt["my-var"] = "123";
417 : *
418 : * // or concatenation:
419 : * opt["my-var"] += "append";
420 : * \endcode
421 : *
422 : * In read mode and unless you defined a default, a non-existant argument
423 : * is viewed as an empty string or 0 if retrieved as a long:
424 : *
425 : * \code
426 : * // if non-existant you get an empty string:
427 : * std::string value = opt["non-existant"];
428 : *
429 : * // if non-existant you get zero:
430 : * long value = opt["non-existant"].get_long();
431 : * \endcode
432 : *
433 : * The get_long() function may generate an error if the parameter is not
434 : * a valid integer. Also when a default is defined, it tries to convert
435 : * the default value to a number and if that fails an error is generated.
436 : *
437 : * \note
438 : * This operator only allows you to access the very first value of
439 : * this option. If the option is marked with GETOPT_FLAG_MULTIPLE,
440 : * you may want to use the get_option() function and then handle
441 : * the option multiple values manually with the option_info::get_value()
442 : * and option_info::set_value().
443 : *
444 : * \warning
445 : * If the option is an alias and the destination is not defined you
446 : * can still get an exception raised.
447 : *
448 : * \param[in] name The name of the option to access.
449 : *
450 : * \return A reference to this option with support for many std::string like
451 : * operators.
452 : */
453 163 : option_info_ref getopt::operator [] (std::string const & name)
454 : {
455 163 : if(name.empty())
456 : {
457 2 : throw getopt_logic_error("argument name cannot be empty.");
458 : }
459 :
460 322 : option_info::pointer_t opt(get_option(name));
461 161 : if(opt == nullptr)
462 : {
463 54 : if(name.length() == 1)
464 : {
465 2 : throw getopt_logic_error("argument name cannot be one letter if it does not exist in operator [].");
466 : }
467 :
468 : // The option doesn't exist yet, create it
469 : //
470 52 : opt = std::make_shared<option_info>(name);
471 52 : f_options_by_name[name] = opt;
472 : }
473 :
474 318 : return option_info_ref(opt);
475 : }
476 :
477 :
478 : /** \brief Process the system options.
479 : *
480 : * If you have the GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS flag turned on,
481 : * then several options are automatically added to your list of supported
482 : * options, such as `--version`.
483 : *
484 : * This function processes these options if any were used by the client.
485 : *
486 : * If the function finds one or more system flags as being defined, it
487 : * returns a non-zero set of SYSTEM_OPTION_... flags. This can be useful
488 : * to decide whether to continue processing or not.
489 : *
490 : * We define a set of flags that can help you decide whether to continue
491 : * or exit. In most cases, we propose that you exit your program if any
492 : * one of the options was a command. This is done like so:
493 : *
494 : * \code
495 : * if(process_system_options & SYSTEM_OPTION_COMMANDS_MASK) != 0)
496 : * {
497 : * exit(1);
498 : * }
499 : * \endcode
500 : *
501 : * You may still want to continue, though, if other flags where set,
502 : * even if some commands were used. For example, some tools will print
503 : * their version and move forward with there work (i.e. compilers often do
504 : * that to help with logging all the information about a build process,
505 : * including the version of the compiler.)
506 : *
507 : * \param[in] out The stream where output is sent if required.
508 : *
509 : * \return non-zero set of flags if any of the system parameters were processed.
510 : */
511 29 : flag_t getopt::process_system_options(std::basic_ostream<char> & out)
512 : {
513 29 : flag_t result(SYSTEM_OPTION_NONE);
514 :
515 : // --version
516 29 : if(is_defined("version"))
517 : {
518 2 : out << f_options_environment.f_version << std::endl;
519 2 : result |= SYSTEM_OPTION_VERSION;
520 : }
521 :
522 : // --help
523 29 : if(is_defined("help"))
524 : {
525 1 : out << usage() << std::endl;
526 1 : result |= SYSTEM_OPTION_HELP;
527 : }
528 :
529 : // --long-help
530 29 : if(is_defined("long-help"))
531 : {
532 1 : out << usage(GETOPT_FLAG_SHOW_ALL) << std::endl;
533 1 : result |= SYSTEM_OPTION_HELP;
534 : }
535 :
536 29 : if(f_options_environment.f_groups != nullptr)
537 : {
538 6 : for(group_description const * grp = f_options_environment.f_groups
539 6 : ; grp->f_group != GETOPT_FLAG_GROUP_NONE
540 : ; ++grp)
541 : {
542 : // the name is not mandatory, without it you do not get the command
543 : // line option but still get the group description
544 : //
545 4 : if(grp->f_name != nullptr
546 4 : && *grp->f_name != '\0')
547 : {
548 8 : std::string const name(grp->f_name);
549 8 : std::string const option_name(name + "-help");
550 4 : if(is_defined(option_name))
551 : {
552 2 : out << usage(grp->f_group) << std::endl;
553 2 : result |= SYSTEM_OPTION_HELP;
554 : }
555 : }
556 : }
557 : }
558 :
559 : // --copyright
560 29 : if(is_defined("copyright"))
561 : {
562 2 : out << f_options_environment.f_copyright << std::endl;
563 2 : result |= SYSTEM_OPTION_COPYRIGHT;
564 : }
565 :
566 : // --license
567 29 : if(is_defined("license"))
568 : {
569 2 : out << f_options_environment.f_license << std::endl;
570 2 : result |= SYSTEM_OPTION_LICENSE;
571 : }
572 :
573 : // --build-date
574 29 : if(is_defined("build-date"))
575 : {
576 : out << "Built on "
577 : << f_options_environment.f_build_date
578 : << " at "
579 2 : << f_options_environment.f_build_time
580 2 : << std::endl;
581 2 : result |= SYSTEM_OPTION_BUILD_DATE;
582 : }
583 :
584 : // --environment-variable-name
585 29 : if(is_defined("environment-variable-name"))
586 : {
587 3 : if(f_options_environment.f_environment_variable_name == nullptr
588 2 : || *f_options_environment.f_environment_variable_name == '\0')
589 : {
590 : out << f_options_environment.f_project_name
591 2 : << " does not support an environment variable."
592 2 : << std::endl;
593 : }
594 : else
595 : {
596 1 : out << f_options_environment.f_environment_variable_name << std::endl;
597 : }
598 3 : result |= SYSTEM_OPTION_ENVIRONMENT_VARIABLE_NAME;
599 : }
600 :
601 : // --configuration-filenames
602 29 : if(is_defined("configuration-filenames"))
603 : {
604 6 : string_list_t list(get_configuration_filenames(false, false));
605 3 : if(list.empty())
606 : {
607 : out << f_options_environment.f_project_name
608 1 : << " does not support configuration files."
609 1 : << std::endl;
610 : }
611 : else
612 : {
613 2 : out << "Configuration filenames:" << std::endl;
614 26 : for(auto n : list)
615 : {
616 24 : out << " . " << n << std::endl;
617 : }
618 : }
619 3 : result |= SYSTEM_OPTION_CONFIGURATION_FILENAMES;
620 : }
621 :
622 : // --path-to-option-definitions
623 29 : if(is_defined("path-to-option-definitions"))
624 : {
625 2 : if(f_options_environment.f_options_files_directory == nullptr
626 1 : || *f_options_environment.f_options_files_directory == '\0')
627 : {
628 1 : out << "/usr/share/advgetopt/options/" << std::endl;
629 : }
630 : else
631 : {
632 1 : out << f_options_environment.f_options_files_directory;
633 1 : if(f_options_environment.f_options_files_directory[strlen(f_options_environment.f_options_files_directory) - 1] != '/')
634 : {
635 1 : out << '/';
636 : }
637 1 : out << std::endl;
638 : }
639 2 : result |= SYSTEM_OPTION_PATH_TO_OPTION_DEFINITIONS;
640 : }
641 :
642 : // --config-dir
643 29 : if(is_defined("config-dir"))
644 : {
645 : // these are automatically used in the get_configuration_filenames()
646 : // function, there is nothing for us to do here
647 : //
648 1 : result |= SYSTEM_OPTION_CONFIG_DIR;
649 : }
650 :
651 29 : return result;
652 : }
653 :
654 :
655 :
656 :
657 :
658 6 : } // namespace advgetopt
659 : // vim: ts=4 sw=4 et
|