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