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

Generated by: LCOV version 1.14

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