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 : #include "advgetopt/log.h"
43 :
44 :
45 : // last include
46 : //
47 : #include <snapdev/poison.h>
48 :
49 :
50 :
51 :
52 : namespace advgetopt
53 : {
54 :
55 :
56 :
57 :
58 :
59 : /** \brief Check whether a parameter is defined.
60 : *
61 : * This function returns true if the specified parameter is found as part of
62 : * the command line options.
63 : *
64 : * You must specify the long name of the option. So a `--verbose` option can
65 : * be checked with:
66 : *
67 : * \code
68 : * if(is_defined("verbose")) ...
69 : * \endcode
70 : *
71 : * For options that come with a short name, you may also specify the short
72 : * name. This is done with a string in this case. It can be a UTF-8
73 : * character. The short name is used if the string represents exactly one
74 : * Unicode character. So the following is equivalent to the previous
75 : * example, assuming your verbose definition has `v` as the short name:
76 : *
77 : * \code
78 : * if(is_defined("v")) ...
79 : * \endcode
80 : *
81 : * \note
82 : * This function returns true when the option was found on the command line,
83 : * the environment variable, or a configuration file. It returns false if
84 : * the option is defined, but was not specified anywhere by the client using
85 : * your program. Also, specifying the option in one of those three locations
86 : * when not allowed at that location will not result in this flag being raised.
87 : *
88 : * \param[in] name The long name or short name of the option to check.
89 : *
90 : * \return true if the option was defined in a configuration file, the
91 : * environment variable, or the command line.
92 : */
93 1028 : bool getopt::is_defined(std::string const & name) const
94 : {
95 2056 : option_info::pointer_t opt(get_option(name));
96 1028 : if(opt != nullptr)
97 : {
98 717 : return opt->is_defined();
99 : }
100 :
101 311 : 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 620 : size_t getopt::size(std::string const & name) const
121 : {
122 1240 : option_info::pointer_t opt(get_option(name));
123 620 : if(opt != nullptr)
124 : {
125 393 : return opt->size();
126 : }
127 227 : 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 71 : bool getopt::has_default(std::string const & name) const
150 : {
151 71 : if(name.empty())
152 : {
153 2 : throw getopt_exception_logic("argument name cannot be empty.");
154 : }
155 :
156 138 : option_info::pointer_t opt(get_option(name));
157 69 : if(opt != nullptr)
158 : {
159 64 : 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 537 : std::string getopt::get_default(std::string const & name) const
190 : {
191 537 : if(name.empty())
192 : {
193 2 : throw getopt_exception_logic("argument name cannot be empty.");
194 : }
195 :
196 1070 : option_info::pointer_t opt(get_option(name));
197 535 : if(opt != nullptr)
198 : {
199 310 : return opt->get_default();
200 : }
201 :
202 225 : 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 122 : long getopt::get_long(std::string const & name, int idx, long min, long max)
250 : {
251 244 : option_info::pointer_t opt(get_option(name));
252 122 : if(opt == nullptr)
253 : {
254 : throw getopt_exception_logic(
255 : "there is no --"
256 6 : + name
257 9 : + " option defined.");
258 : }
259 :
260 119 : long result(0);
261 119 : if(!opt->is_defined())
262 : {
263 88 : std::string const d(opt->get_default());
264 44 : if(d.empty())
265 : {
266 : throw getopt_exception_logic(
267 : "the --"
268 12 : + name
269 18 : + " option was not defined on the command line and it has no or an empty default.");
270 : }
271 : char * end;
272 38 : char const * str(d.c_str());
273 38 : result = strtol(str, &end, 10);
274 38 : if(end != str + d.length())
275 : {
276 : // here we throw because this default value is defined in the
277 : // options of the tool and not by the user
278 : //
279 : throw getopt_exception_logic(
280 : "invalid default number \""
281 6 : + d
282 6 : + "\" for option --"
283 9 : + name);
284 : }
285 : }
286 : else
287 : {
288 75 : result = opt->get_long(idx);
289 : }
290 :
291 : // TODO: replace with validators
292 : //
293 110 : if(result < min || result > max)
294 : {
295 4 : log << log_level_t::error
296 2 : << result
297 2 : << " is out of bounds ("
298 2 : << min
299 2 : << ".."
300 2 : << max
301 2 : << " inclusive) in parameter --"
302 2 : << name
303 2 : << "."
304 2 : << end;
305 2 : result = -1;
306 : }
307 :
308 220 : return result;
309 : }
310 :
311 :
312 : /** \brief Get the content of an option as a string.
313 : *
314 : * Get the content of the named parameter as a string. Command line options
315 : * that accept multiple arguments accept the \p idx parameter to
316 : * specify which item you are interested in.
317 : *
318 : * Note that the option must have been specified on the command line or have
319 : * a default value. For options that do not have a default value, you want
320 : * to call the is_defined() function first.
321 : *
322 : * \exception getopt_exception_undefined
323 : * The getopt_exception_undefined exception is raised if \p name was not
324 : * found on the command line and it has no default, or if \p idx is
325 : * out of bounds.
326 : *
327 : * \param[in] name The name of the option to read.
328 : * \param[in] idx The zero based index of a multi-argument command line option.
329 : *
330 : * \return The option argument as a string.
331 : */
332 498 : std::string getopt::get_string(std::string const & name, int idx) const
333 : {
334 996 : option_info::pointer_t opt(get_option(name));
335 498 : if(opt == nullptr)
336 : {
337 : throw getopt_exception_logic(
338 : "there is no --"
339 6 : + name
340 9 : + " option defined.");
341 : }
342 :
343 495 : if(!opt->is_defined())
344 : {
345 41 : if(opt->has_default())
346 : {
347 36 : return opt->get_default();
348 : }
349 : throw getopt_exception_logic(
350 : "the --"
351 10 : + name
352 15 : + " option was not defined on the command line and it has no default.");
353 : }
354 :
355 454 : return opt->get_value(idx);
356 : }
357 :
358 :
359 : /** \brief Retrieve the value of an argument.
360 : *
361 : * This operator returns the value of an argument just like the get_string()
362 : * does when the argument is defined. When the argument is not defined and it
363 : * has no default, it returns an empty string instead of throwing.
364 : *
365 : * The function is only capable of returning the very first value. If this
366 : * argument has the GETOPT_FLAG_MULTIPLE flag set, you probably want to use
367 : * the get_string() instead.
368 : *
369 : * \param[in] name The name of the option to retrieve.
370 : *
371 : * \return The value of that option or an empty string if not defined.
372 : */
373 193 : std::string getopt::operator [] (std::string const & name) const
374 : {
375 193 : if(name.empty())
376 : {
377 2 : throw getopt_exception_logic("argument name cannot be empty.");
378 : }
379 :
380 382 : option_info::pointer_t opt(get_option(name));
381 191 : if(opt == nullptr)
382 : {
383 1 : return std::string();
384 : }
385 :
386 190 : if(!opt->is_defined())
387 : {
388 6 : if(opt->has_default())
389 : {
390 5 : return opt->get_default();
391 : }
392 1 : return std::string();
393 : }
394 :
395 184 : return opt->get_value(0);
396 : }
397 :
398 :
399 : /** \brief Access a parameter in read and write mode.
400 : *
401 : * This function allows you to access an argument which may or may not
402 : * yet exist.
403 : *
404 : * The return value is a reference to that parameter. You can read
405 : * and write to the reference.
406 : *
407 : * A non-existant argument is created only if necessary. That is,
408 : * only if you actually use an assignment operator as follow:
409 : *
410 : * \code
411 : * // straight assignment:
412 : * opt["my-var"] = "123";
413 : *
414 : * // or concatenation:
415 : * opt["my-var"] += "append";
416 : * \endcode
417 : *
418 : * In read mode and unless you defined a default, a non-existant argument
419 : * is viewed as an empty string or 0 if retrieved as a long:
420 : *
421 : * \code
422 : * // if non-existant you get an empty string:
423 : * std::string value = opt["non-existant"];
424 : *
425 : * // if non-existant you get zero:
426 : * long value = opt["non-existant"].get_long();
427 : * \endcode
428 : *
429 : * The get_long() function may generate an error if the parameter is not
430 : * a valid integer. Also when a default is defined, it tries to convert
431 : * the default value to a number and if that fails an error is generated.
432 : *
433 : * \note
434 : * This operator only allows you to access the very first value of
435 : * this option. If the option is marked with GETOPT_FLAG_MULTIPLE,
436 : * you may want to use the get_option() function and then handle
437 : * the option multiple values manually with the option_info::get_value()
438 : * and option_info::set_value().
439 : *
440 : * \warning
441 : * If the option is an alias and the destination is not defined you
442 : * can still get an exception raised.
443 : *
444 : * \param[in] name The name of the option to access.
445 : *
446 : * \return A reference to this option with support for many std::string like
447 : * operators.
448 : */
449 163 : option_info_ref getopt::operator [] (std::string const & name)
450 : {
451 163 : if(name.empty())
452 : {
453 2 : throw getopt_exception_logic("argument name cannot be empty.");
454 : }
455 :
456 322 : option_info::pointer_t opt(get_option(name));
457 161 : if(opt == nullptr)
458 : {
459 54 : if(name.length() == 1)
460 : {
461 2 : throw getopt_exception_logic("argument name cannot be one letter if it does not exist in operator [].");
462 : }
463 :
464 : // The option doesn't exist yet, create it
465 : //
466 52 : opt = std::make_shared<option_info>(name);
467 52 : f_options_by_name[name] = opt;
468 : }
469 :
470 318 : return option_info_ref(opt);
471 : }
472 :
473 :
474 : /** \brief Process the system options.
475 : *
476 : * If you have the GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS flag turned on,
477 : * then several options are automatically added to your list of supported
478 : * options, such as `--version`.
479 : *
480 : * This function processes these options if any were used by the client.
481 : *
482 : * If the function finds one or more system flags as being defined, it
483 : * returns a non-zero set of SYSTEM_OPTION_... flags. This can be useful
484 : * to decide whether to continue processing or not.
485 : *
486 : * We define a set of flags that can help you decide whether to continue
487 : * or exit. In most cases, we propose that you exit your program if any
488 : * one of the options was a command. This is done like so:
489 : *
490 : * \code
491 : * if(process_system_options & SYSTEM_OPTION_COMMANDS_MASK) != 0)
492 : * {
493 : * exit(1);
494 : * }
495 : * \endcode
496 : *
497 : * You may still want to continue, though, if other flags where set,
498 : * even if some commands were used. For example, some tools will print
499 : * their version and move forward with there work (i.e. compilers often do
500 : * that to help with logging all the information about a build process,
501 : * including the version of the compiler.)
502 : *
503 : * \param[in] out The stream where output is sent if required.
504 : *
505 : * \return non-zero set of flags if any of the system parameters were processed.
506 : */
507 28 : flag_t getopt::process_system_options(std::basic_ostream<char> & out)
508 : {
509 28 : flag_t result(SYSTEM_OPTION_NONE);
510 :
511 : // --version
512 28 : if(is_defined("version"))
513 : {
514 2 : out << f_options_environment.f_version << std::endl;
515 2 : result |= SYSTEM_OPTION_VERSION;
516 : }
517 :
518 : // --help
519 28 : if(is_defined("help"))
520 : {
521 1 : out << usage() << std::endl;
522 1 : result |= SYSTEM_OPTION_HELP;
523 : }
524 :
525 : // --long-help
526 28 : if(is_defined("long-help"))
527 : {
528 1 : out << usage(GETOPT_FLAG_SHOW_ALL) << std::endl;
529 1 : result |= SYSTEM_OPTION_HELP;
530 : }
531 :
532 28 : if(f_options_environment.f_groups != nullptr)
533 : {
534 6 : for(group_description const * grp = f_options_environment.f_groups
535 6 : ; grp->f_group != GETOPT_FLAG_GROUP_NONE
536 : ; ++grp)
537 : {
538 : // the name is not mandatory, without it you do not get the command
539 : // line option but still get the group description
540 : //
541 4 : if(grp->f_name != nullptr
542 4 : && *grp->f_name != '\0')
543 : {
544 8 : std::string const name(grp->f_name);
545 8 : std::string const option_name(name + "-help");
546 4 : if(is_defined(option_name))
547 : {
548 2 : out << usage(grp->f_group) << std::endl;
549 2 : result |= SYSTEM_OPTION_HELP;
550 : }
551 : }
552 : }
553 : }
554 :
555 : // --copyright
556 28 : if(is_defined("copyright"))
557 : {
558 2 : out << f_options_environment.f_copyright << std::endl;
559 2 : result |= SYSTEM_OPTION_COPYRIGHT;
560 : }
561 :
562 : // --license
563 28 : if(is_defined("license"))
564 : {
565 2 : out << f_options_environment.f_license << std::endl;
566 2 : result |= SYSTEM_OPTION_LICENSE;
567 : }
568 :
569 : // --build-date
570 28 : if(is_defined("build-date"))
571 : {
572 2 : out << "Built on "
573 4 : << f_options_environment.f_build_date
574 2 : << " at "
575 4 : << f_options_environment.f_build_time
576 2 : << std::endl;
577 2 : result |= SYSTEM_OPTION_BUILD_DATE;
578 : }
579 :
580 : // --environment-variable-name
581 28 : if(is_defined("environment-variable-name"))
582 : {
583 3 : if(f_options_environment.f_environment_variable_name == nullptr
584 2 : || *f_options_environment.f_environment_variable_name == '\0')
585 : {
586 2 : out << f_options_environment.f_project_name
587 2 : << " does not support an environment variable."
588 2 : << std::endl;
589 : }
590 : else
591 : {
592 1 : out << f_options_environment.f_environment_variable_name << std::endl;
593 : }
594 3 : result |= SYSTEM_OPTION_ENVIRONMENT_VARIABLE_NAME;
595 : }
596 :
597 : // --configuration-filenames
598 28 : if(is_defined("configuration-filenames"))
599 : {
600 6 : string_list_t list(get_configuration_filenames(false, false));
601 3 : if(list.empty())
602 : {
603 1 : out << f_options_environment.f_project_name
604 1 : << " does not support configuration files."
605 1 : << std::endl;
606 : }
607 : else
608 : {
609 2 : out << "Configuration filenames:" << std::endl;
610 26 : for(auto n : list)
611 : {
612 24 : out << " . " << n << std::endl;
613 : }
614 : }
615 3 : result |= SYSTEM_OPTION_CONFIGURATION_FILENAMES;
616 : }
617 :
618 : // --path-to-option-definitions
619 28 : if(is_defined("path-to-option-definitions"))
620 : {
621 2 : if(f_options_environment.f_options_files_directory == nullptr
622 1 : || *f_options_environment.f_options_files_directory == '\0')
623 : {
624 1 : out << "/usr/share/advgetopt/options" << std::endl;
625 : }
626 : else
627 : {
628 1 : out << f_options_environment.f_options_files_directory << std::endl;
629 : }
630 2 : result |= SYSTEM_OPTION_PATH_TO_OPTION_DEFINITIONS;
631 : }
632 :
633 : // --config-dir
634 28 : if(is_defined("config-dir"))
635 : {
636 : // these are automatically used in the get_configuration_filenames()
637 : // function, there is nothing for us to do here
638 : //
639 1 : result |= SYSTEM_OPTION_CONFIG_DIR;
640 : }
641 :
642 28 : return result;
643 : }
644 :
645 :
646 :
647 :
648 :
649 6 : } // namespace advgetopt
650 : // vim: ts=4 sw=4 et
|