LCOV - code coverage report
Current view: top level - advgetopt - advgetopt_options.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 234 256 91.4 %
Date: 2024-10-05 13:34:54 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      20             : /** \file
      21             :  * \brief Advanced getopt data access implementation.
      22             :  *
      23             :  * The advgetopt class has many function used to access the data in the
      24             :  * class. These functions are gathered here.
      25             :  *
      26             :  * This file is covered by the following tests:
      27             :  *
      28             :  * \li options_parser
      29             :  * \li invalid_options_parser
      30             :  * \li valid_options_files
      31             :  * \li invalid_options_files
      32             :  */
      33             : 
      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             : 
      65             : namespace advgetopt
      66             : {
      67             : 
      68             : 
      69             : 
      70             : 
      71             : 
      72             : 
      73             : 
      74             : /** \brief Reset all the options.
      75             :  *
      76             :  * This function goes through the list of options and mark them all as
      77             :  * undefined. This is useful if you want to reuse a getopt object.
      78             :  *
      79             :  * The effect is that all calls to is_defined() made afterward return false
      80             :  * until new arguments get parsed.
      81             :  */
      82           2 : void getopt::reset()
      83             : {
      84          18 :     for(auto & opt : f_options_by_name)
      85             :     {
      86          16 :         opt.second->reset();
      87             :     }
      88           2 : }
      89             : 
      90             : 
      91             : /** \brief Parse the options to option_info objects.
      92             :  *
      93             :  * This function transforms an array of options in a vector of option_info
      94             :  * objects.
      95             :  *
      96             :  * \param[in] opts  An array of options to be parsed.
      97             :  * \param[in] ignore_duplicates  Whether to ignore potential duplicates.
      98             :  */
      99         482 : void getopt::parse_options_info(option const * opts, bool ignore_duplicates)
     100             : {
     101         482 :     if(opts == nullptr)
     102             :     {
     103          66 :         return;
     104             :     }
     105             : 
     106        2059 :     for(
     107        2059 :       ; (opts->f_flags & GETOPT_FLAG_END) == 0
     108             :       ; ++opts)
     109             :     {
     110        1651 :         if(opts->f_name == nullptr
     111        1650 :         || opts->f_name[0] == '\0')
     112             :         {
     113           2 :             throw getopt_logic_error("option long name missing or empty.");
     114             :         }
     115        1649 :         short_name_t const one_char(string_to_short_name(opts->f_name));
     116        1649 :         if(one_char != NO_SHORT_NAME)
     117             :         {
     118           1 :             throw getopt_logic_error("a long name option must be at least 2 characters.");
     119             :         }
     120             : 
     121        1648 :         short_name_t short_name(opts->f_short_name);
     122             : 
     123        1648 :         option_info::pointer_t o(std::make_shared<option_info>(
     124        1648 :                                               opts->f_name
     125        1648 :                                             , short_name));
     126        1647 :         o->set_variables(f_variables);
     127             : 
     128        1647 :         o->set_environment_variable_name(opts->f_environment_variable_name);
     129        1647 :         o->add_flag(opts->f_flags);
     130        1647 :         o->set_default(opts->f_default);
     131        1647 :         o->set_help(opts->f_help);
     132        1647 :         o->set_multiple_separators(opts->f_multiple_separators);
     133             : 
     134        1647 :         if(opts->f_validator != nullptr)
     135             :         {
     136           6 :             o->set_validator(opts->f_validator);
     137             :         }
     138             : 
     139        1651 :         add_option(o, ignore_duplicates);
     140        1647 :     }
     141             : }
     142             : 
     143             : 
     144             : /** \brief Add one option to the advgetopt object.
     145             :  *
     146             :  * This function is used to dynamically add one option to the advgetopt
     147             :  * object.
     148             :  *
     149             :  * This is often used in a library which wants to dynamically add support
     150             :  * for library specific parameters to the command line.
     151             :  *
     152             :  * \note
     153             :  * The \p ignore_duplicates option still gets the option added if only
     154             :  * the short-name is a duplicate. In that case, we set the option's
     155             :  * short-name to NO_SHORT_NAME before adding the option to the tables.
     156             :  *
     157             :  * \param[in] opt  The option to be added.
     158             :  * \param[in] ignore_duplicate  If option is a duplicate, do not add it.
     159             :  */
     160        1680 : void getopt::add_option(option_info::pointer_t opt, bool ignore_duplicates)
     161             : {
     162        1680 :     if(get_option(opt->get_name(), true) != nullptr)
     163             :     {
     164           2 :         if(ignore_duplicates)
     165             :         {
     166           1 :             return;
     167             :         }
     168           2 :         throw getopt_defined_twice(
     169           2 :                   std::string("option named \"")
     170           3 :                 + opt->get_name()
     171           5 :                 + "\" found twice.");
     172             :     }
     173             : 
     174        1678 :     short_name_t short_name(opt->get_short_name());
     175        1678 :     if(get_option(short_name, true) != nullptr)
     176             :     {
     177           3 :         if(ignore_duplicates)
     178             :         {
     179           2 :             short_name = NO_SHORT_NAME;
     180           2 :             opt->set_short_name(NO_SHORT_NAME);
     181             :         }
     182             :         else
     183             :         {
     184           2 :             throw getopt_defined_twice(
     185             :                       "option with short name \""
     186           2 :                     + short_name_to_string(short_name)
     187           5 :                     + "\" found twice.");
     188             :         }
     189             :     }
     190             : 
     191        1677 :     if(opt->is_default_option())
     192             :     {
     193          48 :         if(f_default_option != nullptr)
     194             :         {
     195           1 :             throw getopt_logic_error("two default options found.");
     196             :         }
     197          47 :         if(opt->has_flag(GETOPT_FLAG_FLAG))
     198             :         {
     199           1 :             throw getopt_logic_error("a default option must accept parameters, it can't be a GETOPT_FLAG_FLAG.");
     200             :         }
     201             : 
     202          46 :         f_default_option = opt;
     203             :     }
     204             : 
     205        1675 :     f_options_by_name[opt->get_name()] = opt;
     206             : 
     207        1675 :     if(short_name != NO_SHORT_NAME)
     208             :     {
     209         729 :         f_options_by_short_name[short_name] = opt;
     210             :     }
     211             : }
     212             : 
     213             : 
     214             : /** \brief Get the path and filename to options.
     215             :  *
     216             :  * The programmer can define a path to options that the tool will load.
     217             :  * By default, that path is expected to be `/usr/share/advgetopt`.
     218             :  *
     219             :  * In order to allow debugging as a programmer, we also support changing
     220             :  * the source through an environment variable named
     221             :  * `ADVGETOPT_OPTIONS_FILES_DIRECTORY`. This variable is checked
     222             :  * first and any other path is ignored if it is defined and not just an
     223             :  * empty string.
     224             :  *
     225             :  * \note
     226             :  * If somehow you did not define a group or a project name, then the
     227             :  * function will return an empty string. Otherwise, this path always
     228             :  * exists.
     229             :  *
     230             :  * \return The path or an empty string.
     231             :  */
     232         376 : std::string getopt::get_options_filename() const
     233             : {
     234         376 :     std::string const filename(get_group_or_project_name());
     235         376 :     if(filename.empty())
     236             :     {
     237           6 :         return std::string();
     238             :     }
     239             : 
     240         370 :     std::string path;
     241         370 :     char const * const options_files_directory(getenv("ADVGETOPT_OPTIONS_FILES_DIRECTORY"));
     242         370 :     if(options_files_directory != nullptr
     243           0 :     && *options_files_directory != '\0')
     244             :     {
     245           0 :         path = options_files_directory;
     246             :     }
     247         370 :     else if(f_options_environment.f_options_files_directory != nullptr
     248          91 :          && f_options_environment.f_options_files_directory[0] != '\0')
     249             :     {
     250          86 :         path = f_options_environment.f_options_files_directory;
     251             :     }
     252             :     else
     253             :     {
     254         284 :         path = "/usr/share/advgetopt/options/";
     255             :     }
     256         370 :     if(path.back() != '/')
     257             :     {
     258          86 :         path += '/';
     259             :     }
     260             : 
     261         740 :     return path + filename + ".ini";
     262         376 : }
     263             : 
     264             : 
     265             : /** \brief Check for a file with option definitions.
     266             :  *
     267             :  * This function tries to read the default option file for this process.
     268             :  * This filename is generated using the the option environment files
     269             :  * directory and the project name.
     270             :  *
     271             :  * If the directory is not defined, the function uses this default path:
     272             :  * `"/usr/share/advgetopt/options/"`. See the other
     273             :  * parse_options_from_file(std::string const & filename, int min_sections, int max_sections)
     274             :  * function for additional details.
     275             :  *
     276             :  * \sa parse_options_from_file(std::string const & filename, int min_sections, int max_sections)
     277             :  */
     278         357 : void getopt::parse_options_from_file()
     279             : {
     280         359 :     parse_options_from_file(get_options_filename(), 1, 1);
     281         355 : }
     282             : 
     283             : 
     284             : /** \brief Check for a file with option definitions.
     285             :  *
     286             :  * This function tries to read the specified file for command line options
     287             :  * for this application. These are similar to the option structure, only it
     288             :  * is defined in a file.
     289             :  *
     290             :  * The format of the file is like so:
     291             :  *
     292             :  * \li Option names are defined on a line by themselves between square brackets.
     293             :  * \li Parameters of that option are defined below as a `name=<value>`.
     294             :  *
     295             :  * Example:
     296             :  *
     297             :  * \code
     298             :  *     [<command-name>]
     299             :  *     short_name=<character>
     300             :  *     default=<default value>
     301             :  *     help=<help sentence>
     302             :  *     validator=<validator name>[(<param>[,<param>...])]|/<regex>/<flags>
     303             :  *     alias=<name of aliased option>
     304             :  *     allowed=command-line,environment-variable,configuration-file
     305             :  *     show-usage-on-error
     306             :  *     no-arguments|multiple
     307             :  *     required
     308             :  * \endcode
     309             :  *
     310             :  * The number of namespaces in `<command-name>` can be limited using the
     311             :  * \p min_sections and \p max_sections parameters.
     312             :  *
     313             :  * The function can be called multiple times. The first time, it verifies
     314             :  * that there are not duplicated settings. On following loads, that test
     315             :  * is ignored.
     316             :  *
     317             :  * \todo
     318             :  * Test that options get 100% updated on a reload.
     319             :  *
     320             :  * \note
     321             :  * By default, this function is called with one specific filename based
     322             :  * on the f_project_name field and the f_options_files_directory as
     323             :  * defined in the options environment.
     324             :  *
     325             :  * \param[in] filename  The filename to load.
     326             :  * \param[in] min_sections  The minimum number of namespaces.
     327             :  * \param[in] max_sections  The maximum number of namespaces.
     328             :  * \param[in] ignore_duplicates  Whether duplicates are okay or not.
     329             :  *
     330             :  * \sa parse_options_from_file()
     331             :  */
     332         359 : void getopt::parse_options_from_file(
     333             :           std::string const & filename
     334             :         , int min_sections
     335             :         , int max_sections
     336             :         , bool ignore_duplicates)
     337             : {
     338         359 :     if(filename.empty())
     339             :     {
     340           6 :         return;
     341             :     }
     342             : 
     343         353 :     section_operator_t operators(SECTION_OPERATOR_INI_FILE);
     344         353 :     if(min_sections == 1
     345         351 :     && max_sections == 1)
     346             :     {
     347         351 :         operators |= SECTION_OPERATOR_ONE_SECTION;
     348             :     }
     349             : 
     350         353 :     conf_file_setup conf_setup(
     351             :               filename
     352             :             , line_continuation_t::line_continuation_unix
     353             :             , ASSIGNMENT_OPERATOR_EQUAL
     354             :             , COMMENT_INI | COMMENT_SHELL
     355         353 :             , operators);
     356         353 :     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         353 :     std::string const section_to_ignore(get_group_or_project_name());
     365         353 :     conf_setup.set_section_to_ignore(section_to_ignore);
     366             : 
     367         353 :     conf_file::pointer_t conf(conf_file::get_conf_file(conf_setup));
     368         353 :     conf_file::sections_t const & sections(conf->get_sections());
     369         388 :     for(auto & section_names : sections)
     370             :     {
     371          37 :         string_list_t names;
     372          74 :         split_string(section_names, names, {"::"});
     373          37 :         std::string option_name;
     374          51 :         if(names.size() > 1
     375          51 :         && *names.begin() == section_to_ignore)
     376             :         {
     377           0 :             names.erase(names.begin());
     378           0 :             option_name = snapdev::join_strings(names, "::");
     379             :         }
     380             :         else
     381             :         {
     382          37 :             option_name = section_names;
     383             :         }
     384             : 
     385          37 :         if(names.size() < static_cast<std::size_t>(min_sections)
     386          37 :         || names.size() > static_cast<std::size_t>(max_sections))
     387             :         {
     388           2 :             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           4 :                 cppthread::log << cppthread::log_level_t::error
     404           2 :                     << "the name of a settings definition must include between "
     405           2 :                     << min_sections
     406           2 :                     << " and "
     407           2 :                     << max_sections
     408           2 :                     << " namespaces; \""
     409           2 :                     << section_names
     410           2 :                     << "\" is not considered valid."
     411           4 :                     << cppthread::end;
     412             :             }
     413           2 :             continue;
     414             :         }
     415             : 
     416          35 :         std::string const parameter_name(option_name);
     417         175 :         std::string const short_name(unquote(conf->get_parameter(parameter_name + "::shortname")));
     418          35 :         if(short_name.length() > 1)
     419             :         {
     420           2 :             throw getopt_logic_error(
     421             :                       "option \""
     422           2 :                     + section_names
     423           3 :                     + "\" has an invalid short name in \""
     424           3 :                     + filename
     425           5 :                     + "\", it can't be more than one character.");
     426             :         }
     427          34 :         short_name_t const sn(short_name.length() == 1
     428          34 :                                     ? short_name[0]
     429          34 :                                     : NO_SHORT_NAME);
     430             : 
     431          34 :         option_info::pointer_t opt(std::make_shared<option_info>(parameter_name, sn));
     432          34 :         opt->set_variables(f_variables);
     433             : 
     434          34 :         std::string const environment_variable_name(parameter_name + "::environment_variable_name");
     435          34 :         if(conf->has_parameter(environment_variable_name))
     436             :         {
     437           8 :             opt->set_environment_variable_name(unquote(conf->get_parameter(environment_variable_name)));
     438             :         }
     439             : 
     440          34 :         std::string const default_name(parameter_name + "::default");
     441          34 :         if(conf->has_parameter(default_name))
     442             :         {
     443          17 :             opt->set_default(unquote(conf->get_parameter(default_name)));
     444             :         }
     445             : 
     446          34 :         opt->set_help(unquote(conf->get_parameter(parameter_name + "::help")));
     447             : 
     448          68 :         std::string const validator_name_and_params(conf->get_parameter(parameter_name + "::validator"));
     449          34 :         opt->set_validator(validator_name_and_params);
     450             : 
     451          34 :         std::string const alias_name(parameter_name + "::alias");
     452          34 :         if(conf->has_parameter(alias_name))
     453             :         {
     454           8 :             if(!opt->get_help().empty())
     455             :             {
     456           2 :                 throw getopt_logic_error(
     457             :                           "option \""
     458           2 :                         + section_names
     459           3 :                         + "\" is an alias and as such it can't include a help=... parameter in \""
     460           3 :                         + filename
     461           5 :                         + "\".");
     462             :             }
     463           7 :             opt->set_help(unquote(conf->get_parameter(alias_name)));
     464           7 :             opt->add_flag(GETOPT_FLAG_ALIAS);
     465             :         }
     466             : 
     467          33 :         std::string const allowed_name(parameter_name + "::allowed");
     468          33 :         if(conf->has_parameter(allowed_name))
     469             :         {
     470          66 :             std::string const allowed_list(conf->get_parameter(allowed_name));
     471          33 :             string_list_t allowed;
     472          66 :             split_string(allowed_list, allowed, {","});
     473          92 :             for(auto const & a : allowed)
     474             :             {
     475          59 :                 if(a == "command-line")
     476             :                 {
     477          24 :                     opt->add_flag(GETOPT_FLAG_COMMAND_LINE);
     478             :                 }
     479          35 :                 else if(a == "environment-variable")
     480             :                 {
     481          22 :                     opt->add_flag(GETOPT_FLAG_ENVIRONMENT_VARIABLE);
     482             :                 }
     483          13 :                 else if(a == "configuration-file")
     484             :                 {
     485          13 :                     opt->add_flag(GETOPT_FLAG_CONFIGURATION_FILE);
     486             :                 }
     487           0 :                 else if(a == "dynamic-configuration")
     488             :                 {
     489           0 :                     opt->add_flag(GETOPT_FLAG_DYNAMIC_CONFIGURATION);
     490             :                 }
     491             :             }
     492          33 :         }
     493             : 
     494          33 :         std::string const group_name(parameter_name + "::group");
     495          33 :         if(conf->has_parameter(group_name))
     496             :         {
     497           0 :             std::string const group(conf->get_parameter(group_name));
     498           0 :             if(group == "commands")
     499             :             {
     500           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_COMMANDS);
     501             :             }
     502           0 :             else if(group == "options")
     503             :             {
     504           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_OPTIONS);
     505             :             }
     506           0 :             else if(group == "three")
     507             :             {
     508           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_THREE);
     509             :             }
     510           0 :             else if(group == "four")
     511             :             {
     512           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_FOUR);
     513             :             }
     514           0 :             else if(group == "five")
     515             :             {
     516           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_FIVE);
     517             :             }
     518           0 :             else if(group == "six")
     519             :             {
     520           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_SIX);
     521             :             }
     522           0 :             else if(group == "seven")
     523             :             {
     524           0 :                 opt->add_flag(GETOPT_FLAG_GROUP_SEVEN);
     525             :             }
     526           0 :         }
     527             : 
     528          33 :         if(conf->has_parameter(parameter_name + "::show-usage-on-error"))
     529             :         {
     530           4 :             opt->add_flag(GETOPT_FLAG_SHOW_USAGE_ON_ERROR);
     531             :         }
     532             : 
     533          33 :         if(conf->has_parameter(parameter_name + "::no-arguments"))
     534             :         {
     535           8 :             opt->add_flag(GETOPT_FLAG_FLAG);
     536             :         }
     537             : 
     538          33 :         if(conf->has_parameter(parameter_name + "::multiple"))
     539             :         {
     540           6 :             opt->add_flag(GETOPT_FLAG_MULTIPLE);
     541             :         }
     542             : 
     543          33 :         if(conf->has_parameter(parameter_name + "::required"))
     544             :         {
     545          17 :             opt->add_flag(GETOPT_FLAG_REQUIRED);
     546             :         }
     547             : 
     548          33 :         add_option(opt, ignore_duplicates);
     549          50 :     }
     550         359 : }
     551             : 
     552             : 
     553             : /** \brief Link options marked as a GETOPT_FLAG_ALIAS.
     554             :  *
     555             :  * After we defined all the options, go through the list again to find
     556             :  * aliases and link them with their corresponding alias option.
     557             :  *
     558             :  * \exception getopt_exception_invalid
     559             :  * All aliases must exist or this exception is raised.
     560             :  */
     561         275 : void getopt::link_aliases()
     562             : {
     563        1603 :     for(auto & c : f_options_by_name)
     564             :     {
     565        1332 :         if(c.second->has_flag(GETOPT_FLAG_ALIAS))
     566             :         {
     567          23 :             std::string const & alias_name(c.second->get_help());
     568          23 :             if(alias_name.empty())
     569             :             {
     570           4 :                 throw getopt_logic_error(
     571             :                           "the default value of your alias cannot be an empty string for \""
     572           4 :                         + c.first
     573          10 :                         + "\".");
     574             :             }
     575             : 
     576             :             // we have to use the `true` flag in this get_option() because
     577             :             // aliases may not yet be defined
     578             :             //
     579          21 :             option_info::pointer_t alias(get_option(alias_name, true));
     580          21 :             if(alias == nullptr)
     581             :             {
     582           2 :                 throw getopt_logic_error(
     583             :                           "no option named \""
     584           2 :                         + alias_name
     585           3 :                         + "\" to satisfy the alias of \""
     586           3 :                         + c.first
     587           5 :                         + "\".");
     588             :             }
     589             : 
     590          20 :             flag_t const expected_flags(c.second->get_flags() & ~GETOPT_FLAG_ALIAS);
     591          20 :             if(alias->get_flags() != expected_flags)
     592             :             {
     593           1 :                 std::stringstream ss;
     594           1 :                 ss << std::hex
     595             :                    << "the flags of alias \""
     596           1 :                    << c.first
     597           1 :                    << "\" (0x"
     598           1 :                    << expected_flags
     599             :                    << ") are different than the flags of \""
     600             :                    << alias_name
     601           1 :                    << "\" (0x"
     602           1 :                    << alias->get_flags()
     603           1 :                    << ").";
     604           1 :                 throw getopt_logic_error(ss.str());
     605           1 :             }
     606             : 
     607          19 :             c.second->set_alias_destination(alias);
     608          21 :         }
     609             :     }
     610         271 : }
     611             : 
     612             : 
     613             : /** \brief Assign a short name to an option.
     614             :  *
     615             :  * This function allows for dynamically assigning a short name to an option.
     616             :  * This is useful for cases where a certain number of options may be added
     617             :  * dynamically and may share the same short name or similar situation.
     618             :  *
     619             :  * On our end we like to add `-c` as the short name of the `--config-dir`
     620             :  * command line or environment variable option. However, some of our tools
     621             :  * use `-c` for other reason (i.e. our `cxpath` tool uses `-c` for its
     622             :  * `--compile` option.) So we do not want to have it as a default in
     623             :  * `--config-dir`. Instead we assign it afterward if possible.
     624             :  *
     625             :  * **IMPORTANT:** It is possible to change the short-name at any time.
     626             :  * However, note that you can't have duplicates. It is also possible
     627             :  * to remove a short-name by setting it to the advgetopt::NO_SHORT_NAME
     628             :  * special value.
     629             :  *
     630             :  * \note
     631             :  * This function requires you to make use of the constructor without the
     632             :  * `argc` and `argv` parameters, add the short name, then run all the
     633             :  * parsing.
     634             :  *
     635             :  * \exception getopt_exception_logic
     636             :  * The same short name cannot be used more than once. This exception is
     637             :  * raised if it is discovered that another option already makes use of
     638             :  * this short name. This exception is also raised if \p name does not
     639             :  * reference an existing option.
     640             :  *
     641             :  * \param[in] name  The name of the option which is to receive a short name.
     642             :  * \param[in] short_name  The short name to assigned to the \p name option.
     643             :  */
     644           9 : void getopt::set_short_name(std::string const & name, short_name_t short_name)
     645             : {
     646           9 :     auto opt(f_options_by_name.find(name));
     647           9 :     if(opt == f_options_by_name.end())
     648             :     {
     649           2 :         throw getopt_logic_error(
     650             :                   "option with name \""
     651           2 :                 + name
     652           5 :                 + "\" not found.");
     653             :     }
     654             : 
     655           8 :     if(short_name != NO_SHORT_NAME)
     656             :     {
     657           6 :         auto it(f_options_by_short_name.find(short_name));
     658           6 :         if(it != f_options_by_short_name.end())
     659             :         {
     660           2 :             if(it->second == opt->second)
     661             :             {
     662             :                 // same option, already named 'short_name'
     663             :                 //
     664           1 :                 return;
     665             :             }
     666             : 
     667           2 :             throw getopt_logic_error(
     668             :                       "found another option (\""
     669           2 :                     + it->second->get_name()
     670           3 :                     + "\") with short name '"
     671           4 :                     + short_name_to_string(short_name)
     672           5 :                     + "'.");
     673             :         }
     674             :     }
     675             : 
     676           6 :     short_name_t const old_short_name(opt->second->get_short_name());
     677           6 :     if(old_short_name != NO_SHORT_NAME)
     678             :     {
     679           2 :         auto it(f_options_by_short_name.find(old_short_name));
     680           2 :         if(it != f_options_by_short_name.end())
     681             :         {
     682           2 :             f_options_by_short_name.erase(it);
     683             :         }
     684             :     }
     685             : 
     686           6 :     opt->second->set_short_name(short_name);
     687             : 
     688           6 :     if(short_name != NO_SHORT_NAME)
     689             :     {
     690           4 :         f_options_by_short_name[short_name] = opt->second;
     691             :     }
     692             : }
     693             : 
     694             : 
     695             : /** \brief Output the source of each option.
     696             :  *
     697             :  * This function goes through the list of options by name ("alphabetically")
     698             :  * and prints out the sources or "(undefined)" if not defined anywhere.
     699             :  *
     700             :  * This function gets called when using the `--show-option-sources`
     701             :  * system command line option at the time the process_system_options()
     702             :  * function gets called.
     703             :  *
     704             :  * \param[in] out  The output streaming where the info is written.
     705             :  */
     706           3 : void getopt::show_option_sources(std::basic_ostream<char> & out)
     707             : {
     708           3 :     int idx(1);
     709           3 :     out << "Option Sources:\n";
     710          68 :     for(auto const & opt : f_options_by_name)
     711             :     {
     712          65 :         out << "  " << idx << ". option \"" << opt.second->get_name() << "\"";
     713          65 :         string_list_t sources(opt.second->trace_sources());
     714          65 :         if(sources.empty())
     715             :         {
     716          33 :             out << " (undefined)\n";
     717             :         }
     718             :         else
     719             :         {
     720          32 :             out << "\n";
     721          84 :             for(auto const & src : sources)
     722             :             {
     723          52 :                 out << "     " << src << "\n";
     724             :             }
     725             :         }
     726          65 :         out << "\n";
     727             : 
     728          65 :         ++idx;
     729          65 :     }
     730           3 :     out << std::flush;
     731           3 : }
     732             : 
     733             : 
     734             : 
     735             : } // namespace advgetopt
     736             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions