LCOV - code coverage report
Current view: top level - advgetopt - variables.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 39 51 76.5 %
Date: 2022-05-26 21:41:34 Functions: 7 9 77.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2006-2022  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 implementation.
      22             :  *
      23             :  * The advgetopt class and implementation is an advanced library to parse
      24             :  * command line parameters from static definitions specified by the caller.
      25             :  *
      26             :  * The class supports the command line options, options found in a default
      27             :  * configuration file or in a user defined configuration file.
      28             :  *
      29             :  * The class also includes support for displaying error messages and help
      30             :  * information about all the command line arguments.
      31             :  */
      32             : 
      33             : // self
      34             : //
      35             : #include    "advgetopt/variables.h"
      36             : 
      37             : 
      38             : // C++ libd
      39             : //
      40             : #include    <iostream>
      41             : 
      42             : 
      43             : // last include
      44             : //
      45             : #include    <snapdev/poison.h>
      46             : 
      47             : 
      48             : 
      49             : namespace advgetopt
      50             : {
      51             : 
      52             : 
      53             : 
      54             : /** \brief Canonicalize the variable name.
      55             :  *
      56             :  * This function canonicalizes the name of a variable.
      57             :  *
      58             :  * This means:
      59             :  *
      60             :  * * Replace a sequence of ':' with '::'.
      61             :  * * Replace a sequence of '.' with '::'.
      62             :  * * Replace a '_' with '-'.
      63             :  *
      64             :  * \note
      65             :  * Variable names that come from a configuration file will already have
      66             :  * been canonicalized, but the user can directly call the get_variable()
      67             :  * and set_variable() functions which will also benefit from this
      68             :  * conversion.
      69             :  *
      70             :  * \todo
      71             :  * Check that all section names start with a letter (not a digit).
      72             :  *
      73             :  * \param[in] name  The name of the variable.
      74             :  */
      75          43 : std::string variables::canonicalize_variable_name(std::string const & name)
      76             : {
      77          43 :     std::string result;
      78             : 
      79         255 :     for(char const * n(name.c_str()); *n != '\0'; ++n)
      80             :     {
      81         212 :         if(*n == ':' || *n == '.')
      82             :         {
      83           0 :             while(*n == ':' || *n == '.')
      84             :             {
      85           0 :                 ++n;
      86             :             }
      87           0 :             result += "::";
      88             :         }
      89         212 :         else if(*n == '_')
      90             :         {
      91           2 :             result += '-';
      92             :         }
      93             :         else
      94             :         {
      95         210 :             result += *n;
      96             :         }
      97             :     }
      98             : 
      99          43 :     return result;
     100             : }
     101             : 
     102             : 
     103             : /** \brief Check whether a variable is defined.
     104             :  *
     105             :  * If you want to verify that a variable is defined before retrieving
     106             :  * it, you can use this function. A variable can be set to the empty
     107             :  * string so checking the returned value of the get_variable() is
     108             :  * not sufficient to know whether the variable is defined or log.
     109             :  *
     110             :  * \param[in] name  The name of the variable to check.
     111             :  *
     112             :  * \return true if the variable is defined.
     113             :  */
     114           0 : bool variables::has_variable(std::string const & name) const
     115             : {
     116           0 :     auto it(f_variables.find(canonicalize_variable_name(name)));
     117           0 :     return it != f_variables.end();
     118             : }
     119             : 
     120             : 
     121             : /** \brief Return the value of the named variable.
     122             :  *
     123             :  * This function searches for the named variable and returns its value
     124             :  * if defined.
     125             :  *
     126             :  * \param[in] name  The name of the variable to retrieve.
     127             :  *
     128             :  * \return The variable value of an empty string.
     129             :  */
     130          13 : std::string variables::get_variable(std::string const & name) const
     131             : {
     132          13 :     auto it(f_variables.find(canonicalize_variable_name(name)));
     133          13 :     if(it != f_variables.end())
     134             :     {
     135          13 :         return it->second;
     136             :     }
     137             : 
     138           0 :     return std::string();
     139             : }
     140             : 
     141             : 
     142             : /** \brief Return a reference to the map of variables.
     143             :  *
     144             :  * This function returns a reference to the whole map of variables.
     145             :  *
     146             :  * The map is composed of named values. The first string is the name of
     147             :  * variables and the second string is the value.
     148             :  *
     149             :  * \note
     150             :  * It is not multi-thread safe since the variable make can be updated at any
     151             :  * time.
     152             :  *
     153             :  * \return The reference to the map of variables.
     154             :  */
     155           0 : variables::variable_t const & variables::get_variables() const
     156             : {
     157           0 :     return f_variables;
     158             : }
     159             : 
     160             : 
     161             : /** \brief Set a variable.
     162             :  *
     163             :  * This function sets a variable in the getopt object.
     164             :  *
     165             :  * The value of variables can be used to replace `${...}` entries in
     166             :  * parameters found on the command line or in configuration files.
     167             :  *
     168             :  * By default, if that variable already existed, then its value gets
     169             :  * replaced.
     170             :  *
     171             :  * You can use this function to define a default as in:
     172             :  *
     173             :  * \code
     174             :  *     opt->set_variable("foo", "default value", false);
     175             :  * \endcode
     176             :  *
     177             :  * \note
     178             :  * The value of a variable can itself include `${...}` references.
     179             :  * When parsing a parameter for variables, such are replaced recursively.
     180             :  * See the process_value() for details.
     181             :  *
     182             :  * \param[in] name  The name of the variable.
     183             :  * \param[in] value  The value of the variable.
     184             :  * \param[in] overwrite  Whether to overwrite the value if it already exists.
     185             :  */
     186          15 : void variables::set_variable(
     187             :       std::string const & name
     188             :     , std::string const & value
     189             :     , bool overwrite)
     190             : {
     191          30 :     std::string const var(canonicalize_variable_name(name));
     192          30 :     if(overwrite
     193          15 :     || f_variables.find(var) == f_variables.end())
     194             :     {
     195          15 :         f_variables[canonicalize_variable_name(name)] = value;
     196             :     }
     197          15 : }
     198             : 
     199             : 
     200             : /** \brief Process variables against a parameter.
     201             :  *
     202             :  * Whenever a parameter is retrieved, its value is passed through this
     203             :  * function and if the variable processing is allowed, it searches for
     204             :  * `${...}` sequances and when such are found, it replaces them with the
     205             :  * corresponding variable content.
     206             :  *
     207             :  * The process is recursive meaning that if a variable includes the `${...}`
     208             :  * sequence, that variable will itself also be replaced.
     209             :  *
     210             :  * The variables can be defined in a `[variables]` section and by the
     211             :  * programmer by calling the set_variable() function.
     212             :  *
     213             :  * \note
     214             :  * This functionality is automatically used when the
     215             :  * SYSTEM_OPTION_PROCESS_VARIABLES flag is set in your environment definition.
     216             :  * If you prefer to have it only function for a few of your parameters, then
     217             :  * do not set the SYSTEM_OPTION_PROCESS_VARIABLES and only call this function
     218             :  * for the few values you want to include variables.
     219             :  *
     220             :  * \todo
     221             :  * Consider having a cache, although for variables that would return system
     222             :  * information, it could change at any time.
     223             :  *
     224             :  * \param[in] value  The parameter value to be processed.
     225             :  *
     226             :  * \return The processed value with the variables updated.
     227             :  */
     228          11 : std::string variables::process_value(std::string const & value) const
     229             : {
     230             :     // to support the recursivity, we call a sub-function which calls itself
     231             :     // whenever a variable is discovered to include another variable; that
     232             :     // recursivity is broken immediately if a variable includes itself;
     233             :     // this function is private
     234             :     //
     235          22 :     variable_names_t names;
     236          22 :     return recursive_process_value(value, names);
     237             : }
     238             : 
     239             : 
     240             : /** \brief Internal function processing variables recursively.
     241             :  *
     242             :  * This function goes through value and replaces the `${...}` with the
     243             :  * corresponding variable data. The content of a variable is itself
     244             :  * passed through this process so it is recursive.
     245             :  *
     246             :  * The function records which variables it has worked on so far to
     247             :  * prevent the function from re-adding the same variable (avoid infinite
     248             :  * loop).
     249             :  *
     250             :  * \param[in] value  The value to parse.
     251             :  * \param[in] names  A set of variable names that have already been processed.
     252             :  */
     253          24 : std::string variables::recursive_process_value(
     254             :       std::string const & value
     255             :     , variable_names_t & names) const
     256             : {
     257          24 :     std::string result;
     258             : 
     259         114 :     for(char const * s(value.c_str()); *s != '\0'; ++s)
     260             :     {
     261          90 :         char c(*s);
     262          90 :         if(c == '$' && s[1] == '{') // start variable reference?
     263             :         {
     264          13 :             s += 2;
     265          13 :             char const * name(s);
     266          77 :             for(; *s != '}' && *s != '\0'; ++s);
     267          13 :             if(*s == '\0')
     268             :             {
     269             :                 // invalid variable reference
     270             :                 //
     271           0 :                 result += name;
     272           0 :                 return result;
     273             :             }
     274             : 
     275             :             // TODO: add support for conversions like we have in bash
     276             :             //       (i.e. ${var:-extension} ${var%.extension} ...
     277             :             //       see man bash section "Parameter Expansion")
     278             :             //
     279             :             // TODO: add support for emitting errors on an undefined
     280             :             //       variable
     281             :             //
     282          26 :             std::string var(std::string(name, s - name));
     283          13 :             auto allowed(names.insert(var));
     284          13 :             if(allowed.second)
     285             :             {
     286          13 :                 result += recursive_process_value(get_variable(var), names);
     287          13 :                 names.erase(allowed.first);
     288             :             }
     289             :             else
     290             :             {
     291           0 :                 result += "<variable \"" + var + "\" loops>";
     292          13 :             }
     293             :         }
     294             :         else
     295             :         {
     296          77 :             result += c;
     297             :         }
     298             :     }
     299             : 
     300          24 :     return result;
     301             : }
     302             : 
     303             : 
     304             : 
     305           6 : } // namespace advgetopt
     306             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13