LCOV - code coverage report
Current view: top level - advgetopt - advgetopt_config.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 85 85 100.0 %
Date: 2019-07-15 03:11:49 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * File:
       3             :  *    advgetopt/advgetopt_config.cpp -- advanced get option implementation
       4             :  *
       5             :  * License:
       6             :  *    Copyright (c) 2006-2019  Made to Order Software Corp.  All Rights Reserved
       7             :  *
       8             :  *    https://snapwebsites.org/
       9             :  *    contact@m2osw.com
      10             :  *
      11             :  *    This program is free software; you can redistribute it and/or modify
      12             :  *    it under the terms of the GNU General Public License as published by
      13             :  *    the Free Software Foundation; either version 2 of the License, or
      14             :  *    (at your option) any later version.
      15             :  *
      16             :  *    This program is distributed in the hope that it will be useful,
      17             :  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :  *    GNU General Public License for more details.
      20             :  *
      21             :  *    You should have received a copy of the GNU General Public License along
      22             :  *    with this program; if not, write to the Free Software Foundation, Inc.,
      23             :  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      24             :  *
      25             :  * Authors:
      26             :  *    Alexis Wilke   alexis@m2osw.com
      27             :  *    Doug Barbieri  doug@m2osw.com
      28             :  */
      29             : 
      30             : /** \file
      31             :  * \brief Advanced getopt data access implementation.
      32             :  *
      33             :  * The advgetopt class has many function used to access the data in the
      34             :  * class. These functions are gathered here.
      35             :  */
      36             : 
      37             : // self
      38             : //
      39             : #include    "advgetopt/advgetopt.h"
      40             : 
      41             : // advgetopt lib
      42             : //
      43             : #include    "advgetopt/conf_file.h"
      44             : #include    "advgetopt/log.h"
      45             : 
      46             : // boost lib
      47             : //
      48             : #include    <boost/algorithm/string/replace.hpp>
      49             : 
      50             : 
      51             : // last include
      52             : //
      53             : #include <snapdev/poison.h>
      54             : 
      55             : 
      56             : namespace advgetopt
      57             : {
      58             : 
      59             : 
      60             : 
      61             : 
      62             : /** \brief Generate a list of configuration filenames.
      63             :  *
      64             :  * This function goes through the list of filenames and directories and
      65             :  * generates a complete list of all the configuration files that the
      66             :  * system will load when you call the parse_configuration_files()
      67             :  * function.
      68             :  *
      69             :  * Set the flag \p exists to true if you only want the name of files
      70             :  * that currently exists.
      71             :  *
      72             :  * The \p writable file means that we only want files under the
      73             :  * \<project-name>.d folder and the user configuration folder.
      74             :  *
      75             :  * \param[in] exists  Remove files that do not exist from the list.
      76             :  * \param[in] writable  Only return files we consider writable.
      77             :  *
      78             :  * \return The list of configuration filenames.
      79             :  */
      80         332 : string_list_t getopt::get_configuration_filenames(bool exists, bool writable) const
      81             : {
      82         664 :     string_list_t result;
      83             : 
      84         332 :     if(f_options_environment.f_configuration_files != nullptr)
      85             :     {
      86             :         // load options from configuration files specified as is by caller
      87             :         //
      88         449 :         for(char const * const * configuration_files(f_options_environment.f_configuration_files)
      89         449 :           ; *configuration_files != nullptr
      90             :           ; ++configuration_files)
      91             :         {
      92         348 :             char const * filename(*configuration_files);
      93         348 :             if(*filename != '\0')
      94             :             {
      95         696 :                 std::string const user_filename(handle_user_directory(filename));
      96         348 :                 if(user_filename == filename)
      97             :                 {
      98         342 :                     if(!writable)
      99             :                     {
     100         200 :                         result.push_back(user_filename);
     101             :                     }
     102             : 
     103         684 :                     std::string const with_project_name(insert_project_name(user_filename, f_options_environment.f_project_name));
     104         342 :                     if(!with_project_name.empty())
     105             :                     {
     106         342 :                         result.push_back(with_project_name);
     107             :                     }
     108             :                 }
     109             :                 else
     110             :                 {
     111           6 :                     result.push_back(user_filename);
     112             :                 }
     113             :             }
     114             :         }
     115             :     }
     116             : 
     117         332 :     if(f_options_environment.f_configuration_filename != nullptr)
     118             :     {
     119         314 :         string_list_t directories;
     120         157 :         if(has_flag(GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS))
     121             :         {
     122          11 :             if(is_defined("config-dir"))
     123             :             {
     124           2 :                 size_t const max(size("config-dir"));
     125           6 :                 for(size_t idx(0); idx < max; ++idx)
     126             :                 {
     127           4 :                     directories.push_back(get_string("config-dir", idx));
     128             :                 }
     129             :             }
     130             :         }
     131             : 
     132         157 :         if(f_options_environment.f_configuration_directories != nullptr)
     133             :         {
     134         258 :             for(char const * const * configuration_directories(f_options_environment.f_configuration_directories)
     135         258 :               ; *configuration_directories != nullptr
     136             :               ; ++configuration_directories)
     137             :             {
     138         203 :                 directories.push_back(*configuration_directories);
     139             :             }
     140             :         }
     141             : 
     142         314 :         std::string const filename(f_options_environment.f_configuration_filename);
     143             : 
     144         364 :         for(auto directory : directories)
     145             :         {
     146         207 :             if(!directory.empty())
     147             :             {
     148         414 :                 std::string const full_filename(directory + ("/" + filename));
     149         414 :                 std::string const user_filename(handle_user_directory(full_filename));
     150         207 :                 if(user_filename == full_filename)
     151             :                 {
     152         160 :                     if(!writable)
     153             :                     {
     154         100 :                         result.push_back(user_filename);
     155             :                     }
     156             : 
     157         320 :                     std::string const with_project_name(insert_project_name(user_filename, f_options_environment.f_project_name));
     158         160 :                     if(!with_project_name.empty())
     159             :                     {
     160         160 :                         result.push_back(with_project_name);
     161             :                     }
     162             :                 }
     163             :                 else
     164             :                 {
     165          47 :                     result.push_back(user_filename);
     166             :                 }
     167             :             }
     168             :         }
     169             :     }
     170             : 
     171         332 :     if(!exists)
     172             :     {
     173         284 :         return result;
     174             :     }
     175             : 
     176          96 :     string_list_t existing_files;
     177          48 :     int const mode(R_OK | (writable ? W_OK : 0));
     178         404 :     for(auto r : result)
     179             :     {
     180         356 :         if(access(r.c_str(), mode) == 0)
     181             :         {
     182          26 :             existing_files.push_back(r);
     183             :         }
     184             :     }
     185          48 :     return existing_files;
     186             : }
     187             : 
     188             : 
     189             : /** \brief This function checks for arguments in configuration files.
     190             :  *
     191             :  * Each configuration file is checked one after another. Each file that is
     192             :  * defined is loaded and each line is viewed as an option. If valid, it is
     193             :  * added to the resulting getopt list of options.
     194             :  *
     195             :  * Note that it is an error to define a command in a configuration file. If
     196             :  * that happens, an error occurs and the process stops. Technically this is
     197             :  * defined with the GETOPT_FLAG_CONFIGURATION_FILE flag in your opt table.
     198             :  *
     199             :  * The list of files is checked from beginning to end. So if a later file
     200             :  * changes an option of an earlier file, it is the one effective.
     201             :  *
     202             :  * The configuration file loader supports a project name as defined in the
     203             :  * get_project_name() function. It allows for a sub-directory to
     204             :  * be inserted between the path and the basename of the configuration
     205             :  * file. This allows for a file to be search in an extra sub-directory
     206             :  * so one can avoid changing the original definitions and only use
     207             :  * configuration files in the sub-directory. The path looks like this
     208             :  * when a project name is specified:
     209             :  *
     210             :  * \code
     211             :  *      <path>/<project name>.d/<basename>
     212             :  * \endcode
     213             :  *
     214             :  * Notice that we add a ".d" as usual in other projects under Linux.
     215             :  *
     216             :  * \exception getopt_exception_invalid
     217             :  * This function generates the getopt_exception_invalid exception whenever
     218             :  * something invalid is found in the list of options passed as the \p opts
     219             :  * parameter.
     220             :  *
     221             :  * \exception getopt_exception_default
     222             :  * The function detects whether two options are marked as the default
     223             :  * option (the one receiving parameters that are not used by another command
     224             :  * or match a command.) This exception is raised when such is detected.
     225             :  *
     226             :  * \sa process_configuration_file()
     227             :  */
     228         238 : void getopt::parse_configuration_files()
     229             : {
     230         476 :     string_list_t const filenames(get_configuration_filenames(false, false));
     231             : 
     232         417 :     for(auto f : filenames)
     233             :     {
     234         179 :         process_configuration_file(f);
     235             :     }
     236         238 : }
     237             : 
     238             : 
     239             : /** \brief Parse one specific configuration file and process the results.
     240             :  *
     241             :  * This function reads one specific configuration file using a conf_file
     242             :  * object and then goes through the resulting arguments and add them to
     243             :  * the options of this getopt object.
     244             :  *
     245             :  * The options found in the configuration file must match an option by
     246             :  * its long name. In a configuration file, it is not allowed to have an
     247             :  * option which name is only one character.
     248             :  *
     249             :  * \note
     250             :  * If the filename points to a file which can't be read or does not exist,
     251             :  * then nothing happens and the function returns without an error.
     252             :  *
     253             :  * \todo
     254             :  * Extend the support by having the various flags that the conf_file
     255             :  * class supports appear in the list of configuration filenames.
     256             :  *
     257             :  * \param[in] filename  The name of the configuration file to check out.
     258             :  *
     259             :  * \sa parse_configuration_files()
     260             :  */
     261         185 : void getopt::process_configuration_file(std::string const & filename)
     262             : {
     263         207 :     conf_file_setup conf_setup(filename);
     264         185 :     if(!conf_setup.is_valid())
     265             :     {
     266         163 :         return;
     267             :     }
     268          44 :     conf_file::pointer_t conf(conf_file::get_conf_file(conf_setup));
     269             : 
     270          46 :     for(auto const & param : conf->get_parameters())
     271             :     {
     272             :         // in configuration files we only allow long arguments
     273             :         //
     274          44 :         option_info::pointer_t opt(get_option(param.first));
     275          24 :         if(opt == nullptr)
     276             :         {
     277          10 :             if(!has_flag(GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS)
     278           5 :             || param.first.length() == 1)
     279             :             {
     280           6 :                 log << log_level_t::error
     281           3 :                     << "unknown option \""
     282           6 :                     << param.first
     283           3 :                     << "\" found in configuration file \""
     284           3 :                     << filename
     285           3 :                     << "\"."
     286           3 :                     << end;
     287           3 :                 continue;
     288             :             }
     289             :             else
     290             :             {
     291             :                 // add a new parameter dynamically
     292             :                 //
     293           2 :                 opt = std::make_shared<option_info>(param.first);
     294             : 
     295           2 :                 opt->set_flags(GETOPT_FLAG_CONFIGURATION_FILE | GETOPT_FLAG_DYNAMIC);
     296             : 
     297             :                 // consider the first definition as the default
     298             :                 // (which is likely in our environment)
     299             :                 //
     300           2 :                 opt->set_default(param.second);
     301             : 
     302           2 :                 f_options_by_name[param.first] = opt;
     303             :             }
     304             :         }
     305             :         else
     306             :         {
     307          19 :             if(!opt->has_flag(GETOPT_FLAG_CONFIGURATION_FILE))
     308             :             {
     309             :                 // in configuration files we are expected to use '_' so
     310             :                 // print an error with such
     311             :                 //
     312           2 :                 log << log_level_t::error
     313           1 :                     << "option \""
     314           3 :                     << boost::replace_all_copy(param.first, "-", "_")
     315           1 :                     << "\" is not supported in configuration files (found in \""
     316           1 :                     << filename
     317           1 :                     << "\")."
     318           1 :                     << end;
     319           1 :                 continue;
     320             :             }
     321             :         }
     322             : 
     323          20 :         if(opt != nullptr)
     324             :         {
     325          20 :             add_option_from_string(opt, param.second, filename);
     326             :         }
     327             :     }
     328             : }
     329             : 
     330             : 
     331             : 
     332             : 
     333             : 
     334             : 
     335           6 : } // namespace advgetopt
     336             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12