LCOV - code coverage report
Current view: top level - advgetopt - advgetopt_config.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 79.7 % 271 216
Test Date: 2026-01-18 09:38:57 Functions: 100.0 % 8 8
Legend: Lines: hit not hit

            Line data    Source code
       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              : 
      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              : 
      27              : // self
      28              : //
      29              : #include    "advgetopt/advgetopt.h"
      30              : 
      31              : #include    "advgetopt/conf_file.h"
      32              : #include    "advgetopt/exception.h"
      33              : 
      34              : 
      35              : // cppthread
      36              : //
      37              : #include    <cppthread/log.h>
      38              : 
      39              : 
      40              : // C
      41              : //
      42              : #include    <string.h>
      43              : #include    <unistd.h>
      44              : 
      45              : 
      46              : // last include
      47              : //
      48              : #include    <snapdev/poison.h>
      49              : 
      50              : 
      51              : 
      52              : namespace advgetopt
      53              : {
      54              : 
      55              : 
      56              : 
      57              : 
      58              : /** \brief Generate a list of configuration filenames.
      59              :  *
      60              :  * This function goes through the list of filenames and directories and
      61              :  * generates a complete list of all the configuration files that the
      62              :  * system will load when you call the parse_configuration_files()
      63              :  * function.
      64              :  *
      65              :  * Set the flag \p exists to true if you only want the name of files
      66              :  * that currently exists.
      67              :  *
      68              :  * The \p writable file means that we only want files under the
      69              :  * \<project-name>.d folder and the user configuration folder.
      70              :  *
      71              :  * \note
      72              :  * The argc/argv and environment variable parameters are used whenever the
      73              :  * function is called early and we can't call is_defined(). These are
      74              :  * ignored otherwise.
      75              :  *
      76              :  * \param[in] exists  Remove files that do not exist from the list.
      77              :  * \param[in] writable  Only return files we consider writable.
      78              :  * \param[in] argc  The number of arguments in argv.
      79              :  * \param[in] argv  The arguments passed to the finish_parsing() function or
      80              :  *                  nullptr.
      81              :  *
      82              :  * \return The list of configuration filenames.
      83              :  */
      84          345 : string_list_t getopt::get_configuration_filenames(
      85              :       bool exists
      86              :     , bool writable
      87              :     , int argc
      88              :     , char * argv[]) const
      89              : {
      90          345 :     string_list_t result;
      91              : 
      92          345 :     get_managed_configuration_filenames(result, writable, argc, argv);
      93          345 :     get_direct_configuration_filenames(result, writable);
      94              : 
      95          345 :     if(!exists)
      96              :     {
      97          297 :         return result;
      98              :     }
      99              : 
     100           48 :     string_list_t existing_files;
     101           48 :     int const mode(R_OK | (writable ? W_OK : 0));
     102          512 :     for(auto r : result)
     103              :     {
     104          464 :         if(access(r.c_str(), mode) == 0)
     105              :         {
     106           26 :             existing_files.push_back(r);
     107              :         }
     108          464 :     }
     109              : 
     110           48 :     return existing_files;
     111          345 : }
     112              : 
     113              : 
     114              : /** \brief Add one configuration filename to our list.
     115              :  *
     116              :  * This function adds the specified \p add name to the \p names list unless
     117              :  * already present in the list.
     118              :  *
     119              :  * Several of the functions computing configuration filenames can end up
     120              :  * attempting to add the same filename multiple times. This function
     121              :  * prevents the duplication. This also means the order may be slightly
     122              :  * different than expected (i.e. the filenames don't get reordered when
     123              :  * a duplicate is found).
     124              :  *
     125              :  * \param[in,out] names  The list of configuration names.
     126              :  * \param[in] add  The new configuration filename to add.
     127              :  */
     128         1323 : void getopt::add_configuration_filename(string_list_t & names, std::string const & add)
     129              : {
     130         1323 :     if(std::find(names.begin(), names.end(), add) == names.end())
     131              :     {
     132         1320 :         names.push_back(add);
     133              :     }
     134         1323 : }
     135              : 
     136              : 
     137              : /** \brief Generate the list of managed configuration filenames.
     138              :  *
     139              :  * As the programmer, you can define a configuration filename and a set
     140              :  * of directory names. This function uses that information to generate
     141              :  * a list of full configuration filenames that is then used to load
     142              :  * those configurations.
     143              :  *
     144              :  * If a filename is defined, but no directories, the this function
     145              :  * defines three default paths like so:
     146              :  *
     147              :  * \li `/usr/share/advgetopt/options/\<name>`
     148              :  * \li `/usr/share/\<name>/options`
     149              :  * \li `/etc/\<name>`
     150              :  *
     151              :  * \param[in,out] names  The list of names are added to this list.
     152              :  * \param[in] writable  Whether the destination has to be writable.
     153              :  * \param[in] argc  The number of arguments in argv.
     154              :  * \param[in] argv  The command line arguments.
     155              :  */
     156          345 : void getopt::get_managed_configuration_filenames(
     157              :       string_list_t & names
     158              :     , bool writable
     159              :     , int argc
     160              :     , char * argv[]) const
     161              : {
     162          345 :     if(f_options_environment.f_configuration_filename == nullptr
     163          142 :     || *f_options_environment.f_configuration_filename == '\0')
     164              :     {
     165          204 :         return;
     166              :     }
     167              : 
     168          141 :     string_list_t directories;
     169          141 :     if(has_flag(GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS))
     170              :     {
     171           12 :         if(f_parsed)
     172              :         {
     173              :             // WARNING: at this point the command line and environment
     174              :             //          variable may not be parsed in full if at all
     175              :             //
     176              :             //if(has_flag(SYSTEM_OPTION_CONFIGURATION_FILENAMES))
     177            9 :             if(is_defined("config-dir"))
     178              :             {
     179            6 :                 std::size_t const max(size("config-dir"));
     180            2 :                 directories.reserve(max);
     181            6 :                 for(std::size_t idx(0); idx < max; ++idx)
     182              :                 {
     183           12 :                     directories.push_back(get_string("config-dir", idx));
     184              :                 }
     185              :             }
     186              :         }
     187              :         else
     188              :         {
     189              :             // we've got to do some manual parsing (argh!)
     190              :             //
     191            9 :             directories = find_config_dir(argc, argv);
     192            9 :             if(directories.empty())
     193              :             {
     194            5 :                 string_list_t args(split_environment(f_environment_variable));
     195              : 
     196            5 :                 std::vector<char *> sub_argv;
     197            5 :                 sub_argv.resize(args.size() + 2);
     198            5 :                 sub_argv[0] = const_cast<char *>(f_program_fullname.c_str());
     199           16 :                 for(std::size_t idx(0); idx < args.size(); ++idx)
     200              :                 {
     201           11 :                     sub_argv[idx + 1] = const_cast<char *>(args[idx].c_str());
     202              :                 }
     203            5 :                 sub_argv[args.size() + 1] = nullptr;
     204              : 
     205            5 :                 directories = find_config_dir(sub_argv.size() - 1, sub_argv.data());
     206            5 :             }
     207              :         }
     208              :     }
     209              : 
     210          141 :     if(f_options_environment.f_configuration_directories != nullptr)
     211              :     {
     212          210 :         for(char const * const * configuration_directories(f_options_environment.f_configuration_directories);
     213          210 :             *configuration_directories != nullptr;
     214              :             ++configuration_directories)
     215              :         {
     216          492 :             directories.push_back(*configuration_directories);
     217              :         }
     218              :     }
     219              : 
     220          141 :     if(directories.empty())
     221              :     {
     222           94 :         std::string const name(get_group_or_project_name());
     223              : 
     224           94 :         if(!name.empty())
     225              :         {
     226          276 :             std::string directory_name("/usr/share/advgetopt/options/");
     227           92 :             directory_name += name;
     228           92 :             directories.push_back(directory_name);
     229              : 
     230           92 :             directory_name = "/usr/share/";
     231           92 :             directory_name += name;
     232           92 :             directory_name += "/options";
     233           92 :             directories.push_back(directory_name);
     234              : 
     235           92 :             directory_name = "/etc/";
     236           92 :             directory_name += name;
     237           92 :             directories.push_back(directory_name);
     238           92 :         }
     239           94 :     }
     240              : 
     241          423 :     std::string const filename(f_options_environment.f_configuration_filename);
     242              : 
     243          592 :     for(auto directory : directories)
     244              :     {
     245          451 :         if(!directory.empty())
     246              :         {
     247          451 :             std::string const full_filename(directory + ("/" + filename));
     248          451 :             std::string const user_filename(handle_user_directory(full_filename));
     249          451 :             if(user_filename == full_filename)
     250              :             {
     251          414 :                 if(!writable)
     252              :                 {
     253          348 :                     add_configuration_filename(names, user_filename);
     254              :                 }
     255              : 
     256          414 :                 string_list_t const with_project_name(insert_group_name(
     257              :                                       user_filename
     258          414 :                                     , f_options_environment.f_group_name
     259          414 :                                     , f_options_environment.f_project_name));
     260          414 :                 if(!with_project_name.empty())
     261              :                 {
     262          849 :                     for(auto const & n : with_project_name)
     263              :                     {
     264          435 :                         add_configuration_filename(names, n);
     265              :                     }
     266              :                 }
     267          414 :             }
     268              :             else
     269              :             {
     270           37 :                 add_configuration_filename(names, user_filename);
     271              :             }
     272          451 :         }
     273          451 :     }
     274          141 : }
     275              : 
     276              : 
     277              : /** \brief Define the list of direct configuration filenames.
     278              :  *
     279              :  * We generate two lists of configurations: a managed list and a direct
     280              :  * configuration list. The managed list is created with the
     281              :  * get_managed_configuration_filenames(). The direct list is created with
     282              :  * this function and the list of filenames defined in the
     283              :  * f_configuration_files list of paths.
     284              :  *
     285              :  * In this case, the paths defined in that list are directly used. No
     286              :  * additional directory are added, except for the sub-directory to allow
     287              :  * for administrator files to be edited (i.e. `\<name>.d/??-filename.conf`).
     288              :  *
     289              :  * \param[in,out] names  The list of configuration filenames.
     290              :  * \param[in] writable  Whether only writable filenames get added.
     291              :  */
     292          345 : void getopt::get_direct_configuration_filenames(
     293              :       string_list_t & names
     294              :     , bool writable) const
     295              : {
     296          345 :     if(f_options_environment.f_configuration_files == nullptr)
     297              :     {
     298          258 :         return;
     299              :     }
     300              : 
     301              :     // load options from configuration files specified as is by the programmer
     302              :     //
     303          387 :     for(char const * const * configuration_files(f_options_environment.f_configuration_files);
     304          387 :         *configuration_files != nullptr;
     305              :         ++configuration_files)
     306              :     {
     307          300 :         char const * filename(*configuration_files);
     308          300 :         if(*filename == '\0')
     309              :         {
     310            0 :             continue;
     311              :         }
     312              : 
     313          900 :         std::string const user_filename(handle_user_directory(filename));
     314          300 :         if(user_filename == filename)
     315              :         {
     316          293 :             if(!writable)
     317              :             {
     318          203 :                 add_configuration_filename(names, user_filename);
     319              :             }
     320              : 
     321          293 :             string_list_t const with_project_name(insert_group_name(
     322              :                                   user_filename
     323          293 :                                 , f_options_environment.f_group_name
     324          293 :                                 , f_options_environment.f_project_name));
     325          293 :             if(!with_project_name.empty())
     326              :             {
     327          586 :                 for(auto const & n : with_project_name)
     328              :                 {
     329          293 :                     add_configuration_filename(names, n);
     330              :                 }
     331              :             }
     332          293 :         }
     333              :         else
     334              :         {
     335            7 :             add_configuration_filename(names, user_filename);
     336              :         }
     337          300 :     }
     338              : }
     339              : 
     340              : 
     341              : /** \brief Determine the best suited file for updates.
     342              :  *
     343              :  * This function determines the best suited filename where an administrator
     344              :  * is expected to save his changes. For some tools, there may be many
     345              :  * choices. This function looks for the last entry since that last entry
     346              :  * will allow the administrator to override anything defined prior to
     347              :  * this last entry.
     348              :  *
     349              :  * The search first uses the direct configuration filenames if these are
     350              :  * defined. It uses the last directory which does not start with a tilde
     351              :  * (i.e. no user file).
     352              :  *
     353              :  * If the direct configuration is not defined in that process, we next
     354              :  * test with the managed configuration filenames. We again look for the
     355              :  * last path and that along the last configuration filename.
     356              :  *
     357              :  * If all of that fails, we build a name from "/etc/" the project name,
     358              :  * and use the project name plus ".conf" for the filename:
     359              :  *
     360              :  * \code
     361              :  *     "/etc/" + project_name + "/" + project_name + ".conf"
     362              :  * \endcode
     363              :  *
     364              :  * then pass that file to the default_group_name() function. The result
     365              :  * is what gets returned.
     366              :  *
     367              :  * \return The file the administrator is expected to edit to make changes
     368              :  * to the configuration of the given project.
     369              :  */
     370           19 : std::string getopt::get_output_filename() const
     371              : {
     372           19 :     if(f_options_environment.f_configuration_files != nullptr)
     373              :     {
     374              :         // check the programmer defined paths as is
     375              :         //
     376           16 :         char const * found(nullptr);
     377           68 :         for(char const * const * configuration_files(f_options_environment.f_configuration_files);
     378           68 :             *configuration_files != nullptr;
     379              :             ++configuration_files)
     380              :         {
     381           52 :             char const * filename(*configuration_files);
     382           52 :             if(*filename == '\0')
     383              :             {
     384              :                 // ignore empty filenames
     385              :                 //
     386            0 :                 continue;
     387              :             }
     388              : 
     389           52 :             if(filename[0] == '~'
     390            0 :             && (filename[1] == '/' || filename[1] == '\0'))
     391              :             {
     392              :                 // ignore user directory entries
     393              :                 //
     394            0 :                 continue;
     395              :             }
     396              : 
     397              :             // we want the last one, so we are not done once we found one...
     398              :             //
     399           52 :             found = filename;
     400              :         }
     401              : 
     402           16 :         if(found != nullptr)
     403              :         {
     404              :             return default_group_name(
     405              :                   found
     406           13 :                 , f_options_environment.f_group_name
     407           39 :                 , f_options_environment.f_project_name);
     408              :         }
     409              :     }
     410              : 
     411            6 :     if(f_options_environment.f_configuration_filename != nullptr
     412            6 :     && *f_options_environment.f_configuration_filename != '\0')
     413              :     {
     414              :         // check the directories either defined by the programmer or if
     415              :         // none defined by the programmer, as defined by advgetopt which
     416              :         // in this case simply means "/etc/"; we ignore the possible
     417              :         // use of the --config-dir because in that case the administrator
     418              :         // knows where to save his file
     419              :         //
     420            6 :         std::string const name(get_group_or_project_name());
     421              : 
     422            6 :         std::string directory;
     423            6 :         if(f_options_environment.f_configuration_directories != nullptr)
     424              :         {
     425            0 :             for(char const * const * configuration_directories(f_options_environment.f_configuration_directories);
     426            0 :                 *configuration_directories != nullptr;
     427              :                 ++configuration_directories)
     428              :             {
     429            0 :                 char const * dir(*configuration_directories);
     430            0 :                 if(dir[0] == '\0')
     431              :                 {
     432            0 :                     continue;
     433              :                 }
     434              : 
     435            0 :                 if(dir[0] == '~'
     436            0 :                 && (dir[1] == '/' || dir[1] == '\0'))
     437              :                 {
     438            0 :                     continue;
     439              :                 }
     440              : 
     441              :                 // we want to keep the last entry unless it starts with ~/...
     442              :                 //
     443            0 :                 directory = dir;
     444              :             }
     445              :         }
     446              : 
     447            6 :         if(directory.empty())
     448              :         {
     449              :             // no programmer defined directory, use a system defined one
     450              :             // instead
     451              :             //
     452            6 :             directory = "/etc/" + name;
     453              :         }
     454              : 
     455            6 :         if(directory.back() != '/')
     456              :         {
     457            6 :             directory += '/';
     458              :         }
     459              : 
     460            6 :         std::string const filename(directory + f_options_environment.f_configuration_filename);
     461              : 
     462              :         return default_group_name(
     463              :               filename
     464            6 :             , f_options_environment.f_group_name
     465            6 :             , f_options_environment.f_project_name);
     466            6 :     }
     467              : 
     468              :     // the programmer did not define anything, it is likely that no files
     469              :     // will be loaded but we still generate a default name
     470              :     //
     471            0 :     std::string filename("/etc/");
     472            0 :     if(f_options_environment.f_group_name != nullptr
     473            0 :     && *f_options_environment.f_group_name != '\0')
     474              :     {
     475            0 :         filename += f_options_environment.f_group_name;
     476              :     }
     477            0 :     else if(f_options_environment.f_project_name != nullptr
     478            0 :          && *f_options_environment.f_project_name != '\0')
     479              :     {
     480            0 :         filename += f_options_environment.f_project_name;
     481              :     }
     482              :     else
     483              :     {
     484              :         // really nothing can be done in this case... we have no name
     485              :         // to generate a valid path/configuration filename
     486              :         //
     487            0 :         return std::string();
     488              :     }
     489              : 
     490            0 :     filename += '/';
     491              : 
     492            0 :     if(f_options_environment.f_project_name != nullptr
     493            0 :     && *f_options_environment.f_project_name != '\0')
     494              :     {
     495            0 :         filename += f_options_environment.f_project_name;
     496              :     }
     497            0 :     else if(f_options_environment.f_group_name != nullptr
     498            0 :          && *f_options_environment.f_group_name != '\0')
     499              :     {
     500            0 :         filename += f_options_environment.f_group_name;
     501              :     }
     502              :     else
     503              :     {
     504              :         throw getopt_logic_error("we just checked both of those names and at least one was valid.");       // LCOV_EXCL_LINE
     505              :     }
     506              : 
     507            0 :     filename += ".conf";
     508              : 
     509            0 :     return filename;
     510            0 : }
     511              : 
     512              : 
     513              : /** \brief Search for the "--config-dir" option in a set of arguments.
     514              :  *
     515              :  * This function searches the given list of \p argv arguments for the
     516              :  * "--config-dir".
     517              :  *
     518              :  * This is done that way because we prematurely need that information
     519              :  * in order to properly search for the configuration file. This is because
     520              :  * the "--config-dir" is not yet defined when we attempt to read the
     521              :  * user specific configuration file.
     522              :  *
     523              :  * \param[in] argc  The number of arguments.
     524              :  * \param[in] argv  The list of arguments to be searched.
     525              :  */
     526           14 : string_list_t getopt::find_config_dir(
     527              :           int argc
     528              :         , char * argv[])
     529              : {
     530           14 :     if(argv == nullptr)
     531              :     {
     532            2 :         return string_list_t();
     533              :     }
     534              : 
     535           12 :     string_list_t result;
     536           51 :     for(int idx(1); idx < argc; ++idx)
     537              :     {
     538           39 :         if(strcmp(argv[idx], "--config-dir") == 0)
     539              :         {
     540           10 :             for(++idx; idx < argc; ++idx)
     541              :             {
     542            7 :                 if(argv[idx][0] == '-')
     543              :                 {
     544            2 :                     --idx;
     545            2 :                     break;
     546              :                 }
     547           15 :                 result.push_back(argv[idx]);
     548              :             }
     549              :         }
     550           34 :         else if(strncmp(argv[idx], "--config-dir=", 13) == 0)
     551              :         {
     552            6 :             result.push_back(argv[idx] + 13);
     553              :         }
     554              :     }
     555              : 
     556           12 :     return result;
     557           12 : }
     558              : 
     559              : 
     560              : /** \brief This function checks for arguments in configuration files.
     561              :  *
     562              :  * Each configuration file is checked one after another. Each file that is
     563              :  * defined is loaded and each line is viewed as an option. If valid, it is
     564              :  * added to the resulting getopt list of options.
     565              :  *
     566              :  * Note that it is an error to define a command in a configuration file. If
     567              :  * that happens, an error occurs and the process stops. Technically this is
     568              :  * defined with the GETOPT_FLAG_CONFIGURATION_FILE flag in your opt table.
     569              :  *
     570              :  * The list of files is checked from beginning to end. So if a later file
     571              :  * changes an option of an earlier file, it is the one effective.
     572              :  *
     573              :  * The configuration file loader supports a project name as defined in the
     574              :  * get_project_name() function. It allows for a sub-directory to
     575              :  * be inserted between the path and the basename of the configuration
     576              :  * file. This allows for a file to be search in an extra sub-directory
     577              :  * so one can avoid changing the original definitions and only use
     578              :  * configuration files in the sub-directory. The path looks like this
     579              :  * when a project name is specified:
     580              :  *
     581              :  * \code
     582              :  *      <path>/<project name>.d/<basename>
     583              :  * \endcode
     584              :  *
     585              :  * Notice that we add a ".d" as usual in other projects under Linux.
     586              :  *
     587              :  * \exception getopt_exception_invalid
     588              :  * This function generates the getopt_exception_invalid exception whenever
     589              :  * something invalid is found in the list of options passed as the \p opts
     590              :  * parameter.
     591              :  *
     592              :  * \exception getopt_exception_default
     593              :  * The function detects whether two options are marked as the default
     594              :  * option (the one receiving parameters that are not used by another command
     595              :  * or match a command.) This exception is raised when such is detected.
     596              :  *
     597              :  * \param[in] argc  The number of arguments in argv.
     598              :  * \param[in] argv  The arguments passed to the finish_parsing() function.
     599              :  *
     600              :  * \sa process_configuration_file()
     601              :  * \sa get_configuration_filenames()
     602              :  * \sa finish_parsing()
     603              :  */
     604          270 : void getopt::parse_configuration_files(int argc, char * argv[])
     605              : {
     606          270 :     string_list_t const filenames(get_configuration_filenames(false, false, argc, argv));
     607              : 
     608          844 :     for(auto f : filenames)
     609              :     {
     610          574 :         process_configuration_file(f);
     611          574 :         f_parsed = false;
     612          574 :     }
     613              : 
     614          270 :     f_parsed = true;
     615          540 : }
     616              : 
     617              : 
     618              : /** \brief Parse one specific configuration file and process the results.
     619              :  *
     620              :  * This function reads one specific configuration file using a conf_file
     621              :  * object and then goes through the resulting arguments and add them to
     622              :  * the options of this getopt object.
     623              :  *
     624              :  * The options found in the configuration file must match an option by
     625              :  * its long name. In a configuration file, it is not allowed to have an
     626              :  * option which name is only one character.
     627              :  *
     628              :  * \note
     629              :  * If the filename points to a file which can't be read or does not exist,
     630              :  * then nothing happens and the function returns without an error.
     631              :  *
     632              :  * \todo
     633              :  * Extend the support by having the various flags that the conf_file
     634              :  * class supports appear in the list of configuration filenames.
     635              :  *
     636              :  * \param[in] filename  The name of the configuration file to check out.
     637              :  *
     638              :  * \sa parse_configuration_files()
     639              :  */
     640          585 : void getopt::process_configuration_file(std::string const & filename)
     641              : {
     642          585 :     option_info::set_configuration_filename(filename);
     643              : 
     644          585 :     conf_file_setup::pointer_t conf_setup;
     645          585 :     if(f_options_environment.f_config_setup == nullptr)
     646              :     {
     647          585 :         conf_setup = std::make_shared<conf_file_setup>(filename);
     648              :     }
     649              :     else
     650              :     {
     651            0 :         conf_setup = std::make_shared<conf_file_setup>(
     652              :                           filename
     653            0 :                         , *f_options_environment.f_config_setup);
     654              :     }
     655          585 :     if(!conf_setup->is_valid())
     656              :     {
     657              :         // a non-existant file is considered valid now so this should never
     658              :         // happen; later we may use the flag if we find errors in the file
     659              :         //
     660              :         return; // LCOV_EXCL_LINE
     661              :     }
     662          585 :     conf_file::pointer_t conf(conf_file::get_conf_file(*conf_setup));
     663              : 
     664              :     // is there a variable section?
     665              :     //
     666          585 :     if(f_options_environment.f_section_variables_name != nullptr)
     667              :     {
     668           68 :         conf->section_to_variables(
     669              :                       f_options_environment.f_section_variables_name
     670           17 :                     , f_variables);
     671              :     }
     672              : 
     673          585 :     conf_file::sections_t sections(conf->get_sections());
     674          585 :     if(!sections.empty())
     675              :     {
     676           18 :         std::string const name(CONFIGURATION_SECTIONS);
     677            6 :         option_info::pointer_t configuration_sections(get_option(name));
     678            6 :         if(configuration_sections == nullptr)
     679              :         {
     680            3 :             configuration_sections = std::make_shared<option_info>(name);
     681            3 :             configuration_sections->add_flag(
     682              :                           GETOPT_FLAG_MULTIPLE
     683              :                         | GETOPT_FLAG_CONFIGURATION_FILE);
     684            3 :             f_options_by_name[configuration_sections->get_name()] = configuration_sections;
     685              :         }
     686            3 :         else if(!configuration_sections->has_flag(GETOPT_FLAG_MULTIPLE))
     687              :         {
     688            2 :             cppthread::log << cppthread::log_level_t::error
     689            1 :                            << "option \""
     690            1 :                            << name
     691            1 :                            << "\" must have GETOPT_FLAG_MULTIPLE set."
     692            2 :                            << cppthread::end;
     693            1 :             return;
     694              :         }
     695           15 :         for(auto s : sections)
     696              :         {
     697           10 :             if(!configuration_sections->has_value(s))
     698              :             {
     699            6 :                 configuration_sections->add_value(
     700              :                           s
     701            6 :                         , string_list_t()
     702              :                         , option_source_t::SOURCE_CONFIGURATION);
     703              :             }
     704           10 :         }
     705            7 :     }
     706              : 
     707          674 :     for(auto const & param : conf->get_parameters())
     708              :     {
     709              :         // in configuration files we only allow long arguments
     710              :         //
     711           90 :         option_info::pointer_t opt(get_option(param.first));
     712           90 :         if(opt == nullptr)
     713              :         {
     714            5 :             if(!has_flag(GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS)
     715            5 :             || param.first.length() == 1)
     716              :             {
     717            6 :                 cppthread::log << cppthread::log_level_t::error
     718            3 :                                << "unknown option \""
     719            6 :                                << option_with_underscores(param.first)
     720            3 :                                << "\" found in configuration file \""
     721            3 :                                << filename
     722            3 :                                << "\" on line "
     723            6 :                                << param.second.get_line()
     724            3 :                                << "."
     725           12 :                                << cppthread::end;
     726            3 :                 continue;
     727              :             }
     728              :             else
     729              :             {
     730              :                 // add a new parameter dynamically
     731              :                 //
     732            2 :                 opt = std::make_shared<option_info>(param.first);
     733            2 :                 opt->set_variables(f_variables);
     734              : 
     735            2 :                 opt->set_flags(GETOPT_FLAG_CONFIGURATION_FILE | GETOPT_FLAG_DYNAMIC);
     736              : 
     737              :                 // consider the first definition as the default
     738              :                 // (which is likely in our environment)
     739              :                 //
     740            2 :                 opt->set_default(param.second);
     741              : 
     742            2 :                 f_options_by_name[opt->get_name()] = opt;
     743              :             }
     744              :         }
     745              :         else
     746              :         {
     747           85 :             if(!opt->has_flag(GETOPT_FLAG_CONFIGURATION_FILE))
     748              :             {
     749              :                 // in configuration files we are expected to use '_' so
     750              :                 // print an error with such
     751              :                 //
     752            2 :                 cppthread::log << cppthread::log_level_t::error
     753            1 :                                << "option \""
     754            2 :                                << option_with_underscores(param.first)
     755            1 :                                << "\" is not supported in configuration files (found in \""
     756            1 :                                << filename
     757            1 :                                << "\")."
     758            3 :                                << cppthread::end;
     759            1 :                 continue;
     760              :             }
     761              :         }
     762              : 
     763           86 :         std::string value(param.second.get_value());
     764           86 :         switch(param.second.get_assignment_operator())
     765              :         {
     766           86 :         case advgetopt::assignment_t::ASSIGNMENT_SET:
     767              :         case advgetopt::assignment_t::ASSIGNMENT_NONE:
     768              :             // nothing special in this case, just overwrite if already defined
     769              :             //
     770           86 :             break;
     771              : 
     772            0 :         case advgetopt::assignment_t::ASSIGNMENT_OPTIONAL:
     773            0 :             if(opt->is_defined())
     774              :             {
     775              :                 // already set, do not overwrite
     776              :                 //
     777            0 :                 continue;
     778              :             }
     779            0 :             break;
     780              : 
     781            0 :         case advgetopt::assignment_t::ASSIGNMENT_APPEND:
     782            0 :             if(opt->is_defined()
     783            0 :             && !opt->has_flag(GETOPT_FLAG_MULTIPLE))
     784              :             {
     785              :                 // append the new value
     786              :                 //
     787            0 :                 value = opt->get_value() + value;
     788              :             }
     789            0 :             break;
     790              : 
     791            0 :         case advgetopt::assignment_t::ASSIGNMENT_NEW:
     792            0 :             if(opt->is_defined())
     793              :             {
     794              :                 // prevent re-assignment
     795              :                 //
     796            0 :                 cppthread::log << cppthread::log_level_t::error
     797            0 :                                << "option \""
     798            0 :                                << option_with_underscores(param.first)
     799            0 :                                << "\" found in configuration file \""
     800            0 :                                << filename
     801            0 :                                << "\" on line "
     802            0 :                                << param.second.get_line()
     803            0 :                                << " uses the := operator but the value is already defined."
     804            0 :                                << cppthread::end;
     805            0 :                 continue;
     806              :             }
     807            0 :             break;
     808              : 
     809              :         }
     810              : 
     811           86 :         add_option_from_string(
     812              :                   opt
     813              :                 , value
     814              :                 , filename
     815           86 :                 , string_list_t()
     816              :                 , option_source_t::SOURCE_CONFIGURATION);
     817          674 :     }
     818              : 
     819          584 :     f_parsed = true;
     820          587 : }
     821              : 
     822              : 
     823              : 
     824              : 
     825              : 
     826              : 
     827              : } // namespace advgetopt
     828              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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