advgetopt 2.0.47
Parse complex command line arguments and configuration files in C++.
advgetopt_options.cpp
Go to the documentation of this file.
1// Copyright (c) 2006-2024 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/join_strings.h>
50#include <snapdev/tokenize_string.h>
51
52
53// C++
54//
55#include <list>
56
57
58// last include
59//
60#include <snapdev/poison.h>
61
62
63
64
65namespace advgetopt
66{
67
68
69
70
71
72
73
83{
84 for(auto & opt : f_options_by_name)
85 {
86 opt.second->reset();
87 }
88}
89
90
99void getopt::parse_options_info(option const * opts, bool ignore_duplicates)
100{
101 if(opts == nullptr)
102 {
103 return;
104 }
105
106 for(
107 ; (opts->f_flags & GETOPT_FLAG_END) == 0
108 ; ++opts)
109 {
110 if(opts->f_name == nullptr
111 || opts->f_name[0] == '\0')
112 {
113 throw getopt_logic_error("option long name missing or empty.");
114 }
115 short_name_t const one_char(string_to_short_name(opts->f_name));
116 if(one_char != NO_SHORT_NAME)
117 {
118 throw getopt_logic_error("a long name option must be at least 2 characters.");
119 }
120
121 short_name_t short_name(opts->f_short_name);
122
123 option_info::pointer_t o(std::make_shared<option_info>(
124 opts->f_name
125 , short_name));
126 o->set_variables(f_variables);
127
128 o->set_environment_variable_name(opts->f_environment_variable_name);
129 o->add_flag(opts->f_flags);
130 o->set_default(opts->f_default);
131 o->set_help(opts->f_help);
132 o->set_multiple_separators(opts->f_multiple_separators);
133
134 if(opts->f_validator != nullptr)
135 {
136 o->set_validator(opts->f_validator);
137 }
138
139 add_option(o, ignore_duplicates);
140 }
141}
142
143
160void getopt::add_option(option_info::pointer_t opt, bool ignore_duplicates)
161{
162 if(get_option(opt->get_name(), true) != nullptr)
163 {
164 if(ignore_duplicates)
165 {
166 return;
167 }
168 throw getopt_defined_twice(
169 std::string("option named \"")
170 + opt->get_name()
171 + "\" found twice.");
172 }
173
174 short_name_t short_name(opt->get_short_name());
175 if(get_option(short_name, true) != nullptr)
176 {
177 if(ignore_duplicates)
178 {
179 short_name = NO_SHORT_NAME;
180 opt->set_short_name(NO_SHORT_NAME);
181 }
182 else
183 {
184 throw getopt_defined_twice(
185 "option with short name \""
186 + short_name_to_string(short_name)
187 + "\" found twice.");
188 }
189 }
190
191 if(opt->is_default_option())
192 {
193 if(f_default_option != nullptr)
194 {
195 throw getopt_logic_error("two default options found.");
196 }
197 if(opt->has_flag(GETOPT_FLAG_FLAG))
198 {
199 throw getopt_logic_error("a default option must accept parameters, it can't be a GETOPT_FLAG_FLAG.");
200 }
201
202 f_default_option = opt;
203 }
204
205 f_options_by_name[opt->get_name()] = opt;
206
207 if(short_name != NO_SHORT_NAME)
208 {
209 f_options_by_short_name[short_name] = opt;
210 }
211}
212
213
233{
234 std::string const filename(get_group_or_project_name());
235 if(filename.empty())
236 {
237 return std::string();
238 }
239
240 std::string path;
241 char const * const options_files_directory(getenv("ADVGETOPT_OPTIONS_FILES_DIRECTORY"));
242 if(options_files_directory != nullptr
243 && *options_files_directory != '\0')
244 {
245 path = options_files_directory;
246 }
249 {
251 }
252 else
253 {
254 path = "/usr/share/advgetopt/options/";
255 }
256 if(path.back() != '/')
257 {
258 path += '/';
259 }
260
261 return path + filename + ".ini";
262}
263
264
282
283
333 std::string const & filename
334 , int min_sections
335 , int max_sections
336 , bool ignore_duplicates)
337{
338 if(filename.empty())
339 {
340 return;
341 }
342
344 if(min_sections == 1
345 && max_sections == 1)
346 {
347 operators |= SECTION_OPERATOR_ONE_SECTION;
348 }
349
350 conf_file_setup conf_setup(
351 filename
355 , operators);
356 if(!conf_setup.is_valid())
357 {
358 return; // LCOV_EXCL_LINE
359 }
360
361 // if the file includes a section named after the group or project
362 // we can remove it completely (this helps with sharing fluid settings)
363 //
364 std::string const section_to_ignore(get_group_or_project_name());
365 conf_setup.set_section_to_ignore(section_to_ignore);
366
368 conf_file::sections_t const & sections(conf->get_sections());
369 for(auto & section_names : sections)
370 {
371 string_list_t names;
372 split_string(section_names, names, {"::"});
373 std::string option_name;
374 if(names.size() > 1
375 && *names.begin() == section_to_ignore)
376 {
377 names.erase(names.begin());
378 option_name = snapdev::join_strings(names, "::");
379 }
380 else
381 {
382 option_name = section_names;
383 }
384
385 if(names.size() < static_cast<std::size_t>(min_sections)
386 || names.size() > static_cast<std::size_t>(max_sections))
387 {
388 if(min_sections == 1
389 && max_sections == 1) // LCOV_EXCL_LINE
390 {
391 // right now this case cannot happen because we set the
392 // SECTION_OPERATOR_ONE_SECTION flag so errors are caught
393 // directly inside the conf_file::get_conf_file() call
394 //
395 cppthread::log << cppthread::log_level_t::error // LCOV_EXCL_LINE
396 << "the name of a settings definition must include one namespace; \"" // LCOV_EXCL_LINE
397 << section_names // LCOV_EXCL_LINE
398 << "\" is not considered valid." // LCOV_EXCL_LINE
399 << cppthread::end; // LCOV_EXCL_LINE
400 }
401 else
402 {
403 cppthread::log << cppthread::log_level_t::error
404 << "the name of a settings definition must include between "
405 << min_sections
406 << " and "
407 << max_sections
408 << " namespaces; \""
409 << section_names
410 << "\" is not considered valid."
411 << cppthread::end;
412 }
413 continue;
414 }
415
416 std::string const parameter_name(option_name);
417 std::string const short_name(unquote(conf->get_parameter(parameter_name + "::shortname")));
418 if(short_name.length() > 1)
419 {
420 throw getopt_logic_error(
421 "option \""
422 + section_names
423 + "\" has an invalid short name in \""
424 + filename
425 + "\", it can't be more than one character.");
426 }
427 short_name_t const sn(short_name.length() == 1
428 ? short_name[0]
429 : NO_SHORT_NAME);
430
431 option_info::pointer_t opt(std::make_shared<option_info>(parameter_name, sn));
432 opt->set_variables(f_variables);
433
434 std::string const environment_variable_name(parameter_name + "::environment_variable_name");
435 if(conf->has_parameter(environment_variable_name))
436 {
437 opt->set_environment_variable_name(unquote(conf->get_parameter(environment_variable_name)));
438 }
439
440 std::string const default_name(parameter_name + "::default");
441 if(conf->has_parameter(default_name))
442 {
443 opt->set_default(unquote(conf->get_parameter(default_name)));
444 }
445
446 opt->set_help(unquote(conf->get_parameter(parameter_name + "::help")));
447
448 std::string const validator_name_and_params(conf->get_parameter(parameter_name + "::validator"));
449 opt->set_validator(validator_name_and_params);
450
451 std::string const alias_name(parameter_name + "::alias");
452 if(conf->has_parameter(alias_name))
453 {
454 if(!opt->get_help().empty())
455 {
456 throw getopt_logic_error(
457 "option \""
458 + section_names
459 + "\" is an alias and as such it can't include a help=... parameter in \""
460 + filename
461 + "\".");
462 }
463 opt->set_help(unquote(conf->get_parameter(alias_name)));
464 opt->add_flag(GETOPT_FLAG_ALIAS);
465 }
466
467 std::string const allowed_name(parameter_name + "::allowed");
468 if(conf->has_parameter(allowed_name))
469 {
470 std::string const allowed_list(conf->get_parameter(allowed_name));
471 string_list_t allowed;
472 split_string(allowed_list, allowed, {","});
473 for(auto const & a : allowed)
474 {
475 if(a == "command-line")
476 {
477 opt->add_flag(GETOPT_FLAG_COMMAND_LINE);
478 }
479 else if(a == "environment-variable")
480 {
482 }
483 else if(a == "configuration-file")
484 {
485 opt->add_flag(GETOPT_FLAG_CONFIGURATION_FILE);
486 }
487 else if(a == "dynamic-configuration")
488 {
490 }
491 }
492 }
493
494 std::string const group_name(parameter_name + "::group");
495 if(conf->has_parameter(group_name))
496 {
497 std::string const group(conf->get_parameter(group_name));
498 if(group == "commands")
499 {
500 opt->add_flag(GETOPT_FLAG_GROUP_COMMANDS);
501 }
502 else if(group == "options")
503 {
504 opt->add_flag(GETOPT_FLAG_GROUP_OPTIONS);
505 }
506 else if(group == "three")
507 {
508 opt->add_flag(GETOPT_FLAG_GROUP_THREE);
509 }
510 else if(group == "four")
511 {
512 opt->add_flag(GETOPT_FLAG_GROUP_FOUR);
513 }
514 else if(group == "five")
515 {
516 opt->add_flag(GETOPT_FLAG_GROUP_FIVE);
517 }
518 else if(group == "six")
519 {
520 opt->add_flag(GETOPT_FLAG_GROUP_SIX);
521 }
522 else if(group == "seven")
523 {
524 opt->add_flag(GETOPT_FLAG_GROUP_SEVEN);
525 }
526 }
527
528 if(conf->has_parameter(parameter_name + "::show-usage-on-error"))
529 {
530 opt->add_flag(GETOPT_FLAG_SHOW_USAGE_ON_ERROR);
531 }
532
533 if(conf->has_parameter(parameter_name + "::no-arguments"))
534 {
535 opt->add_flag(GETOPT_FLAG_FLAG);
536 }
537
538 if(conf->has_parameter(parameter_name + "::multiple"))
539 {
540 opt->add_flag(GETOPT_FLAG_MULTIPLE);
541 }
542
543 if(conf->has_parameter(parameter_name + "::required"))
544 {
545 opt->add_flag(GETOPT_FLAG_REQUIRED);
546 }
547
548 add_option(opt, ignore_duplicates);
549 }
550}
551
552
562{
563 for(auto & c : f_options_by_name)
564 {
565 if(c.second->has_flag(GETOPT_FLAG_ALIAS))
566 {
567 std::string const & alias_name(c.second->get_help());
568 if(alias_name.empty())
569 {
570 throw getopt_logic_error(
571 "the default value of your alias cannot be an empty string for \""
572 + c.first
573 + "\".");
574 }
575
576 // we have to use the `true` flag in this get_option() because
577 // aliases may not yet be defined
578 //
579 option_info::pointer_t alias(get_option(alias_name, true));
580 if(alias == nullptr)
581 {
582 throw getopt_logic_error(
583 "no option named \""
584 + alias_name
585 + "\" to satisfy the alias of \""
586 + c.first
587 + "\".");
588 }
589
590 flag_t const expected_flags(c.second->get_flags() & ~GETOPT_FLAG_ALIAS);
591 if(alias->get_flags() != expected_flags)
592 {
593 std::stringstream ss;
594 ss << std::hex
595 << "the flags of alias \""
596 << c.first
597 << "\" (0x"
598 << expected_flags
599 << ") are different than the flags of \""
600 << alias_name
601 << "\" (0x"
602 << alias->get_flags()
603 << ").";
604 throw getopt_logic_error(ss.str());
605 }
606
607 c.second->set_alias_destination(alias);
608 }
609 }
610}
611
612
644void getopt::set_short_name(std::string const & name, short_name_t short_name)
645{
646 auto opt(f_options_by_name.find(name));
647 if(opt == f_options_by_name.end())
648 {
649 throw getopt_logic_error(
650 "option with name \""
651 + name
652 + "\" not found.");
653 }
654
655 if(short_name != NO_SHORT_NAME)
656 {
657 auto it(f_options_by_short_name.find(short_name));
658 if(it != f_options_by_short_name.end())
659 {
660 if(it->second == opt->second)
661 {
662 // same option, already named 'short_name'
663 //
664 return;
665 }
666
667 throw getopt_logic_error(
668 "found another option (\""
669 + it->second->get_name()
670 + "\") with short name '"
671 + short_name_to_string(short_name)
672 + "'.");
673 }
674 }
675
676 short_name_t const old_short_name(opt->second->get_short_name());
677 if(old_short_name != NO_SHORT_NAME)
678 {
679 auto it(f_options_by_short_name.find(old_short_name));
680 if(it != f_options_by_short_name.end())
681 {
682 f_options_by_short_name.erase(it);
683 }
684 }
685
686 opt->second->set_short_name(short_name);
687
688 if(short_name != NO_SHORT_NAME)
689 {
690 f_options_by_short_name[short_name] = opt->second;
691 }
692}
693
694
706void getopt::show_option_sources(std::basic_ostream<char> & out)
707{
708 int idx(1);
709 out << "Option Sources:\n";
710 for(auto const & opt : f_options_by_name)
711 {
712 out << " " << idx << ". option \"" << opt.second->get_name() << "\"";
713 string_list_t sources(opt.second->trace_sources());
714 if(sources.empty())
715 {
716 out << " (undefined)\n";
717 }
718 else
719 {
720 out << "\n";
721 for(auto const & src : sources)
722 {
723 out << " " << src << "\n";
724 }
725 }
726 out << "\n";
727
728 ++idx;
729 }
730 out << std::flush;
731}
732
733
734
735} // namespace advgetopt
736// 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:224
option_info::map_by_name_t f_options_by_name
Definition advgetopt.h:225
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:226
std::string get_options_filename() const
Get the path and filename to options.
void parse_options_info(option const *opts, bool ignore_duplicates=false)
Parse the options to option_info objects.
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:229
option_info::pointer_t f_default_option
Definition advgetopt.h:227
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.
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.