advgetopt 2.0.49
Parse complex command line arguments and configuration files in C++.
advgetopt_options.cpp
Go to the documentation of this file.
1// Copyright (c) 2006-2025 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
34// self
35//
36#include "advgetopt/advgetopt.h"
37
38#include "advgetopt/conf_file.h"
39#include "advgetopt/exception.h"
40
41
42// cppthread
43//
44#include <cppthread/log.h>
45
46
47// snapdev
48//
49#include <snapdev/glob_to_list.h>
50#include <snapdev/join_strings.h>
51#include <snapdev/tokenize_string.h>
52
53
54// C++
55//
56#include <list>
57
58
59// last include
60//
61#include <snapdev/poison.h>
62
63
64
65
66namespace advgetopt
67{
68
69
70
71
72
73
74
84{
85 for(auto & opt : f_options_by_name)
86 {
87 opt.second->reset();
88 }
89}
90
91
100void getopt::parse_options_info(option const * opts, bool ignore_duplicates)
101{
102 if(opts == nullptr)
103 {
104 return;
105 }
106
107 for(
108 ; (opts->f_flags & GETOPT_FLAG_END) == 0
109 ; ++opts)
110 {
111 if(opts->f_name == nullptr
112 || opts->f_name[0] == '\0')
113 {
114 throw getopt_logic_error("option long name missing or empty.");
115 }
116 short_name_t const one_char(string_to_short_name(opts->f_name));
117 if(one_char != NO_SHORT_NAME)
118 {
119 throw getopt_logic_error("a long name option must be at least 2 characters.");
120 }
121
122 short_name_t short_name(opts->f_short_name);
123
124 option_info::pointer_t o(std::make_shared<option_info>(
125 opts->f_name
126 , short_name));
127 o->set_variables(f_variables);
128
129 o->set_environment_variable_name(opts->f_environment_variable_name);
130 o->add_flag(opts->f_flags);
131 o->set_default(opts->f_default);
132 o->set_help(opts->f_help);
133 o->set_multiple_separators(opts->f_multiple_separators);
134
135 if(opts->f_validator != nullptr)
136 {
137 o->set_validator(opts->f_validator);
138 }
139
140 add_option(o, ignore_duplicates);
141 }
142}
143
144
161void getopt::add_option(option_info::pointer_t opt, bool ignore_duplicates)
162{
163 if(get_option(opt->get_name(), true) != nullptr)
164 {
165 if(ignore_duplicates)
166 {
167 return;
168 }
169 throw getopt_defined_twice(
170 std::string("option named \"")
171 + opt->get_name()
172 + "\" found twice.");
173 }
174
175 short_name_t short_name(opt->get_short_name());
176 if(get_option(short_name, true) != nullptr)
177 {
178 if(ignore_duplicates)
179 {
180 short_name = NO_SHORT_NAME;
181 opt->set_short_name(NO_SHORT_NAME);
182 }
183 else
184 {
185 throw getopt_defined_twice(
186 "option with short name \""
187 + short_name_to_string(short_name)
188 + "\" found twice.");
189 }
190 }
191
192 if(opt->is_default_option())
193 {
194 if(f_default_option != nullptr)
195 {
196 throw getopt_logic_error("two default options found.");
197 }
198 if(opt->has_flag(GETOPT_FLAG_FLAG))
199 {
200 throw getopt_logic_error("a default option must accept parameters, it can't be a GETOPT_FLAG_FLAG.");
201 }
202
203 f_default_option = opt;
204 }
205
206 f_options_by_name[opt->get_name()] = opt;
207
208 if(short_name != NO_SHORT_NAME)
209 {
210 f_options_by_short_name[short_name] = opt;
211 }
212}
213
214
216{
217 std::string path;
218
219 char const * const options_files_directory(getenv("ADVGETOPT_OPTIONS_FILES_DIRECTORY"));
220 if(options_files_directory != nullptr
221 && *options_files_directory != '\0')
222 {
223 // environment variable has priority
224 //
225 path = options_files_directory;
226 }
229 {
230 // next the tool option environment has priority
231 //
233 }
234 else
235 {
236 // finally, use a default
237 //
238 path = "/usr/share/advgetopt/options/";
239 }
240
241 if(path.back() != '/')
242 {
243 path += '/';
244 }
245
246 return path;
247}
248
249
301{
302 string_list_t result;
303
304 std::string const filename(get_group_or_project_name());
305 if(filename.empty())
306 {
307 return result;
308 }
309
310 std::string const path(get_path_to_option_files());
311
312 // add the plain filename
313 //
314 result.push_back(path + filename + ".ini");
315
316 // look for additional filenames
317 //
318 std::string pattern(path + filename + "-*.ini");
319 snapdev::glob_to_list<std::list<std::string>> list;
320 if(list.read_path<snapdev::glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS>(pattern))
321 {
322 for(auto const & l : list)
323 {
324 result.push_back(l);
325 }
326 }
327
328 return result;
329}
330
331
357{
359 for(auto const & l : list)
360 {
362 }
363}
364
365
415 std::string const & filename
416 , int min_sections
417 , int max_sections
418 , bool ignore_duplicates)
419{
420 if(filename.empty())
421 {
422 return;
423 }
424
426 if(min_sections == 1
427 && max_sections == 1)
428 {
429 operators |= SECTION_OPERATOR_ONE_SECTION;
430 }
431
432 conf_file_setup conf_setup(
433 filename
437 , operators);
438 if(!conf_setup.is_valid())
439 {
440 return; // LCOV_EXCL_LINE
441 }
442
443 // if the file includes a section named after the group or project
444 // we can remove it completely (this helps with sharing fluid settings)
445 //
446 std::string const section_to_ignore(get_group_or_project_name());
447 conf_setup.set_section_to_ignore(section_to_ignore);
448
450 conf_file::sections_t const & sections(conf->get_sections());
451 for(auto & section_names : sections)
452 {
453 string_list_t names;
454 split_string(section_names, names, {"::"});
455 std::string option_name;
456 if(names.size() > 1
457 && *names.begin() == section_to_ignore)
458 {
459 names.erase(names.begin());
460 option_name = snapdev::join_strings(names, "::");
461 }
462 else
463 {
464 option_name = section_names;
465 }
466
467 if(names.size() < static_cast<std::size_t>(min_sections)
468 || names.size() > static_cast<std::size_t>(max_sections))
469 {
470 if(min_sections == 1
471 && max_sections == 1) // LCOV_EXCL_LINE
472 {
473 // right now this case cannot happen because we set the
474 // SECTION_OPERATOR_ONE_SECTION flag so errors are caught
475 // directly inside the conf_file::get_conf_file() call
476 //
477 cppthread::log << cppthread::log_level_t::error // LCOV_EXCL_LINE
478 << "the name of a settings definition must include one namespace; \"" // LCOV_EXCL_LINE
479 << section_names // LCOV_EXCL_LINE
480 << "\" is not considered valid." // LCOV_EXCL_LINE
481 << cppthread::end; // LCOV_EXCL_LINE
482 }
483 else
484 {
485 cppthread::log << cppthread::log_level_t::error
486 << "the name of a settings definition must include between "
487 << min_sections
488 << " and "
489 << max_sections
490 << " namespaces; \""
491 << section_names
492 << "\" is not considered valid."
493 << cppthread::end;
494 }
495 continue;
496 }
497
498 std::string const parameter_name(option_name);
499 std::string const short_name(unquote(conf->get_parameter(parameter_name + "::shortname")));
500 if(short_name.length() > 1)
501 {
502 throw getopt_logic_error(
503 "option \""
504 + section_names
505 + "\" has an invalid short name in \""
506 + filename
507 + "\", it can't be more than one character.");
508 }
509 short_name_t const sn(short_name.length() == 1
510 ? short_name[0]
511 : NO_SHORT_NAME);
512
513 option_info::pointer_t opt(std::make_shared<option_info>(parameter_name, sn));
514 opt->set_variables(f_variables);
515
516 std::string const environment_variable_name(parameter_name + "::environment_variable_name");
517 if(conf->has_parameter(environment_variable_name))
518 {
519 opt->set_environment_variable_name(unquote(conf->get_parameter(environment_variable_name)));
520 }
521
522 std::string const default_name(parameter_name + "::default");
523 if(conf->has_parameter(default_name))
524 {
525 opt->set_default(unquote(conf->get_parameter(default_name)));
526 }
527
528 opt->set_help(unquote(conf->get_parameter(parameter_name + "::help")));
529
530 std::string const validator_name_and_params(conf->get_parameter(parameter_name + "::validator"));
531 opt->set_validator(validator_name_and_params);
532
533 std::string const alias_name(parameter_name + "::alias");
534 if(conf->has_parameter(alias_name))
535 {
536 if(!opt->get_help().empty())
537 {
538 throw getopt_logic_error(
539 "option \""
540 + section_names
541 + "\" is an alias and as such it can't include a help=... parameter in \""
542 + filename
543 + "\".");
544 }
545 opt->set_help(unquote(conf->get_parameter(alias_name)));
546 opt->add_flag(GETOPT_FLAG_ALIAS);
547 }
548
549 std::string const allowed_name(parameter_name + "::allowed");
550 if(conf->has_parameter(allowed_name))
551 {
552 std::string const allowed_list(conf->get_parameter(allowed_name));
553 string_list_t allowed;
554 split_string(allowed_list, allowed, {","});
555 for(auto const & a : allowed)
556 {
557 if(a == "command-line")
558 {
559 opt->add_flag(GETOPT_FLAG_COMMAND_LINE);
560 }
561 else if(a == "environment-variable")
562 {
564 }
565 else if(a == "configuration-file")
566 {
567 opt->add_flag(GETOPT_FLAG_CONFIGURATION_FILE);
568 }
569 else if(a == "dynamic-configuration")
570 {
572 }
573 }
574 }
575
576 std::string const group_name(parameter_name + "::group");
577 if(conf->has_parameter(group_name))
578 {
579 std::string const group(conf->get_parameter(group_name));
580 if(group == "commands")
581 {
582 opt->add_flag(GETOPT_FLAG_GROUP_COMMANDS);
583 }
584 else if(group == "options")
585 {
586 opt->add_flag(GETOPT_FLAG_GROUP_OPTIONS);
587 }
588 else if(group == "three")
589 {
590 opt->add_flag(GETOPT_FLAG_GROUP_THREE);
591 }
592 else if(group == "four")
593 {
594 opt->add_flag(GETOPT_FLAG_GROUP_FOUR);
595 }
596 else if(group == "five")
597 {
598 opt->add_flag(GETOPT_FLAG_GROUP_FIVE);
599 }
600 else if(group == "six")
601 {
602 opt->add_flag(GETOPT_FLAG_GROUP_SIX);
603 }
604 else if(group == "seven")
605 {
606 opt->add_flag(GETOPT_FLAG_GROUP_SEVEN);
607 }
608 }
609
610 if(conf->has_parameter(parameter_name + "::show-usage-on-error"))
611 {
612 opt->add_flag(GETOPT_FLAG_SHOW_USAGE_ON_ERROR);
613 }
614
615 if(conf->has_parameter(parameter_name + "::no-arguments"))
616 {
617 opt->add_flag(GETOPT_FLAG_FLAG);
618 }
619
620 if(conf->has_parameter(parameter_name + "::multiple"))
621 {
622 opt->add_flag(GETOPT_FLAG_MULTIPLE);
623 }
624
625 if(conf->has_parameter(parameter_name + "::required"))
626 {
627 opt->add_flag(GETOPT_FLAG_REQUIRED);
628 }
629
630 add_option(opt, ignore_duplicates);
631 }
632}
633
634
644{
645 for(auto & c : f_options_by_name)
646 {
647 if(c.second->has_flag(GETOPT_FLAG_ALIAS))
648 {
649 std::string const & alias_name(c.second->get_help());
650 if(alias_name.empty())
651 {
652 throw getopt_logic_error(
653 "the default value of your alias cannot be an empty string for \""
654 + c.first
655 + "\".");
656 }
657
658 // we have to use the `true` flag in this get_option() because
659 // aliases may not yet be defined
660 //
661 option_info::pointer_t alias(get_option(alias_name, true));
662 if(alias == nullptr)
663 {
664 throw getopt_logic_error(
665 "no option named \""
666 + alias_name
667 + "\" to satisfy the alias of \""
668 + c.first
669 + "\".");
670 }
671
672 flag_t const expected_flags(c.second->get_flags() & ~GETOPT_FLAG_ALIAS);
673 if(alias->get_flags() != expected_flags)
674 {
675 std::stringstream ss;
676 ss << std::hex
677 << "the flags of alias \""
678 << c.first
679 << "\" (0x"
680 << expected_flags
681 << ") are different than the flags of \""
682 << alias_name
683 << "\" (0x"
684 << alias->get_flags()
685 << ").";
686 throw getopt_logic_error(ss.str());
687 }
688
689 c.second->set_alias_destination(alias);
690 }
691 }
692}
693
694
726void getopt::set_short_name(std::string const & name, short_name_t short_name)
727{
728 auto opt(f_options_by_name.find(name));
729 if(opt == f_options_by_name.end())
730 {
731 throw getopt_logic_error(
732 "option with name \""
733 + name
734 + "\" not found.");
735 }
736
737 if(short_name != NO_SHORT_NAME)
738 {
739 auto it(f_options_by_short_name.find(short_name));
740 if(it != f_options_by_short_name.end())
741 {
742 if(it->second == opt->second)
743 {
744 // same option, already named 'short_name'
745 //
746 return;
747 }
748
749 throw getopt_logic_error(
750 "found another option (\""
751 + it->second->get_name()
752 + "\") with short name '"
753 + short_name_to_string(short_name)
754 + "'.");
755 }
756 }
757
758 short_name_t const old_short_name(opt->second->get_short_name());
759 if(old_short_name != NO_SHORT_NAME)
760 {
761 auto it(f_options_by_short_name.find(old_short_name));
762 if(it != f_options_by_short_name.end())
763 {
764 f_options_by_short_name.erase(it);
765 }
766 }
767
768 opt->second->set_short_name(short_name);
769
770 if(short_name != NO_SHORT_NAME)
771 {
772 f_options_by_short_name[short_name] = opt->second;
773 }
774}
775
776
788void getopt::show_option_sources(std::basic_ostream<char> & out)
789{
790 int idx(1);
791 out << "Option Sources:\n";
792 for(auto const & opt : f_options_by_name)
793 {
794 out << " " << idx << ". option \"" << opt.second->get_name() << "\"";
795 string_list_t sources(opt.second->trace_sources());
796 if(sources.empty())
797 {
798 out << " (undefined)\n";
799 }
800 else
801 {
802 out << "\n";
803 for(auto const & src : sources)
804 {
805 out << " " << src << "\n";
806 }
807 }
808 out << "\n";
809
810 ++idx;
811 }
812 out << std::flush;
813}
814
815
816
817} // namespace advgetopt
818// vim: ts=4 sw=4 et
Definitions of the advanced getopt class.
bool is_valid() const
Check whether the setup is considered valid.
void set_section_to_ignore(std::string const &section_name)
Set a section name to ignore.
std::shared_ptr< conf_file > pointer_t
Definition conf_file.h:186
string_set_t sections_t
Definition conf_file.h:187
static pointer_t get_conf_file(conf_file_setup const &setup)
Create and read a conf_file.
std::string get_group_or_project_name() const
Retrieve the group or project name.
options_environment f_options_environment
Definition advgetopt.h:225
option_info::map_by_name_t f_options_by_name
Definition advgetopt.h:226
option_info::pointer_t get_option(std::string const &name, bool exact_option=false) const
Retrieve an option by name.
void add_option(option_info::pointer_t opt, bool ignore_duplicates=false)
Add one option to the advgetopt object.
option_info::map_by_short_name_t f_options_by_short_name
Definition advgetopt.h:227
std::string get_path_to_option_files() const
void parse_options_info(option const *opts, bool ignore_duplicates=false)
Parse the options to option_info objects.
string_list_t get_filenames_of_option_definitions() const
Get the path and filename to options.
void parse_options_from_file()
Check for a file with option definitions.
void reset()
Reset all the options.
void link_aliases()
Link options marked as a GETOPT_FLAG_ALIAS.
void show_option_sources(std::basic_ostream< char > &out)
Output the source of each option.
variables::pointer_t f_variables
Definition advgetopt.h:230
option_info::pointer_t f_default_option
Definition advgetopt.h:228
void set_short_name(std::string const &name, short_name_t short_name)
Assign a short name to an option.
std::shared_ptr< option_info > pointer_t
Definition option_info.h:78
Declaration of the conf_file class used to read a configuration file.
Definitions of the advanced getopt exceptions.
The advgetopt environment to parse command line options.
Definition version.h:37
static constexpr flag_t GETOPT_FLAG_GROUP_SEVEN
Definition flags.h:77
void split_string(std::string const &str, string_list_t &result, string_list_t const &separators)
Split a string in sub-strings separated by separators.
Definition utils.cpp:347
constexpr section_operator_t SECTION_OPERATOR_INI_FILE
Definition conf_file.h:97
static constexpr flag_t GETOPT_FLAG_GROUP_OPTIONS
Definition flags.h:72
std::uint_fast16_t section_operator_t
Definition conf_file.h:91
static constexpr flag_t GETOPT_FLAG_SHOW_USAGE_ON_ERROR
Definition flags.h:60
static constexpr flag_t GETOPT_FLAG_END
Definition flags.h:82
constexpr section_operator_t SECTION_OPERATOR_ONE_SECTION
Definition conf_file.h:99
constexpr comment_t COMMENT_SHELL
Definition conf_file.h:83
static constexpr flag_t GETOPT_FLAG_DYNAMIC_CONFIGURATION
Definition flags.h:48
short_name_t string_to_short_name(std::string const &name)
Transform a string to a short name.
static constexpr flag_t GETOPT_FLAG_COMMAND_LINE
Definition flags.h:45
static constexpr flag_t GETOPT_FLAG_GROUP_FOUR
Definition flags.h:74
static constexpr flag_t GETOPT_FLAG_GROUP_SIX
Definition flags.h:76
constexpr assignment_operator_t ASSIGNMENT_OPERATOR_EQUAL
Definition conf_file.h:71
constexpr short_name_t NO_SHORT_NAME
Definition option_info.h:51
std::uint32_t flag_t
Definition flags.h:41
static constexpr flag_t GETOPT_FLAG_CONFIGURATION_FILE
Definition flags.h:47
char32_t short_name_t
Definition option_info.h:49
static constexpr flag_t GETOPT_FLAG_FLAG
Definition flags.h:51
static constexpr flag_t GETOPT_FLAG_GROUP_COMMANDS
Definition flags.h:71
static constexpr flag_t GETOPT_FLAG_GROUP_FIVE
Definition flags.h:75
std::string short_name_to_string(short_name_t short_name)
Convert a short name to a UTF-8 string.
constexpr comment_t COMMENT_INI
Definition conf_file.h:82
static constexpr flag_t GETOPT_FLAG_MULTIPLE
Definition flags.h:53
static constexpr flag_t GETOPT_FLAG_GROUP_THREE
Definition flags.h:73
std::string unquote(std::string const &s, std::string const &pairs)
Remove single (') or double (") quotes from a string.
Definition utils.cpp:168
static constexpr flag_t GETOPT_FLAG_REQUIRED
Definition flags.h:52
static constexpr flag_t GETOPT_FLAG_ENVIRONMENT_VARIABLE
Definition flags.h:46
std::vector< std::string > string_list_t
Definition utils.h:41
static constexpr flag_t GETOPT_FLAG_ALIAS
Definition flags.h:50
Structure representing an option.
Definition options.h:70
char const *const * f_multiple_separators
Definition options.h:78
short_name_t f_short_name
Definition options.h:71
char const * f_help
Definition options.h:76
char const * f_validator
Definition options.h:77
char const * f_environment_variable_name
Definition options.h:74
char const * f_name
Definition options.h:73
char const * f_default
Definition options.h:75
char const * f_options_files_directory
Definition options.h:445

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.