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

          Line data    Source code
       1             : /*
       2             :  * File:
       3             :  *    advgetopt/advgetopt_options.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/exception.h"
      45             : #include    "advgetopt/log.h"
      46             : 
      47             : 
      48             : // last include
      49             : //
      50             : #include <snapdev/poison.h>
      51             : 
      52             : 
      53             : 
      54             : 
      55             : namespace advgetopt
      56             : {
      57             : 
      58             : 
      59             : 
      60             : 
      61             : 
      62             : /** \brief Check whether a parameter is defined.
      63             :  *
      64             :  * This function returns true if the specified parameter is found as part of
      65             :  * the command line options.
      66             :  *
      67             :  * You must specify the long name of the option. So a `--verbose` option can
      68             :  * be checked with:
      69             :  *
      70             :  * \code
      71             :  *   if(is_defined("verbose")) ...
      72             :  * \endcode
      73             :  *
      74             :  * For options that come with a short name, you may also specify the short
      75             :  * name. This is done with a string in this case. It can be a UTF-8
      76             :  * character. The short name is used if the string represents exactly one
      77             :  * Unicode character. So the following is equivalent to the previous
      78             :  * example, assuming your verbose definition has `v` as the short name:
      79             :  *
      80             :  * \code
      81             :  *   if(is_defined("v")) ...
      82             :  * \endcode
      83             :  *
      84             :  * \note
      85             :  * This function returns true when the option was found on the command line,
      86             :  * the environment variable, or a configuration file. It returns false if
      87             :  * the option is defined, but was not specified anywhere by the client using
      88             :  * your program. Also, specifying the option in one of those three locations
      89             :  * when not allowed at that location will not result in this flag being raised.
      90             :  *
      91             :  * \param[in] name  The long name or short name of the option to check.
      92             :  *
      93             :  * \return true if the option was defined in a configuration file, the
      94             :  *         environment variable, or the command line.
      95             :  */
      96        1028 : bool getopt::is_defined(std::string const & name) const
      97             : {
      98        2056 :     option_info::pointer_t opt(get_option(name));
      99        1028 :     if(opt != nullptr)
     100             :     {
     101         717 :         return opt->is_defined();
     102             :     }
     103             : 
     104         311 :     return false;
     105             : }
     106             : 
     107             : 
     108             : /** \brief Retrieve the number of arguments.
     109             :  *
     110             :  * This function returns the number of arguments that were specified after
     111             :  * the named option.
     112             :  *
     113             :  * The function returns zero if the argument was never specified on the
     114             :  * command line. If the option accepts exactly one parameter (i.e. not
     115             :  * marked as a multiple arguments option: GETOPT_FLAG_MULTIPLE) then
     116             :  * the function returns either zero (not specified) or one (specified
     117             :  * at least once.)
     118             :  *
     119             :  * \param[in] name  The name of the option to check.
     120             :  *
     121             :  * \return The number of arguments specified on the command line or zero.
     122             :  */
     123         620 : size_t getopt::size(std::string const & name) const
     124             : {
     125        1240 :     option_info::pointer_t opt(get_option(name));
     126         620 :     if(opt != nullptr)
     127             :     {
     128         393 :         return opt->size();
     129             :     }
     130         227 :     return 0;
     131             : }
     132             : 
     133             : 
     134             : /** \brief Check whether an option has a default value.
     135             :  *
     136             :  * Some parameters may be given a default. This function is used to
     137             :  * detect whether such a default value is defined.
     138             :  *
     139             :  * \note
     140             :  * This function is particularly useful in the event the default value
     141             :  * may be an empty string.
     142             :  *
     143             :  * \exception getopt_exception_undefined
     144             :  * The getopt_exception_undefined exception is raised if this function is
     145             :  * called with an empty \p name.
     146             :  *
     147             :  * \param[in] name  The name of the parameter of which you want to know
     148             :  *                  whether it has a default value or not.
     149             :  *
     150             :  * \return true if the default value was defined (even if an empty string.)
     151             :  */
     152          71 : bool getopt::has_default(std::string const & name) const
     153             : {
     154          71 :     if(name.empty())
     155             :     {
     156           2 :         throw getopt_exception_logic("argument name cannot be empty.");
     157             :     }
     158             : 
     159         138 :     option_info::pointer_t opt(get_option(name));
     160          69 :     if(opt != nullptr)
     161             :     {
     162          64 :         return opt->has_default();
     163             :     }
     164             : 
     165           5 :     return false;
     166             : }
     167             : 
     168             : 
     169             : /** \brief Get the default value for this option.
     170             :  *
     171             :  * When an option is not defined, you may use this function to retrieve its
     172             :  * default instead. This is actually done automatically when you call the
     173             :  * get_string() or get_long() functions.
     174             :  *
     175             :  * An option without a default has this function returning nullptr.
     176             :  *
     177             :  * \note
     178             :  * Whether an option has a default value should be checked with the
     179             :  * has_default() function which returns true when the default value
     180             :  * was defined. An option with an empty string as the default is
     181             :  * a valid case which cannot be detected otherwise.
     182             :  *
     183             :  * \exception getopt_exception_undefined
     184             :  * The getopt_exception_undefined exception is raised if this function is
     185             :  * called with an empty \p name.
     186             :  *
     187             :  * \param[in] name  The name of the parameter of which you want to retrieve
     188             :  *                  the default value.
     189             :  *
     190             :  * \return The default value or an empty string if no value is defined.
     191             :  */
     192         537 : std::string getopt::get_default(std::string const & name) const
     193             : {
     194         537 :     if(name.empty())
     195             :     {
     196           2 :         throw getopt_exception_logic("argument name cannot be empty.");
     197             :     }
     198             : 
     199        1070 :     option_info::pointer_t opt(get_option(name));
     200         535 :     if(opt != nullptr)
     201             :     {
     202         310 :         return opt->get_default();
     203             :     }
     204             : 
     205         225 :     return std::string();
     206             : }
     207             : 
     208             : 
     209             : /** \brief This function retrieves an argument as a long value.
     210             :  *
     211             :  * This function reads the specified argument from the named option and
     212             :  * transforms it to a long value. It then checks the result against the
     213             :  * specified minimum and maximum range.
     214             :  *
     215             :  * The function name represents an argument that needs to be defined. You
     216             :  * can test whether it was defined on the command line with the is_defined()
     217             :  * function. The index must be between 0 and 'size() - 1' inclusive. If
     218             :  * the item was not defined, then size() returns zero and you cannot call
     219             :  * this function.
     220             :  *
     221             :  * The function does not check the validity of the minimum and maximum
     222             :  * parameters. If \p min \> \p max is true then the function will always
     223             :  * fail with a call to usage() as no value can be defined between \p min
     224             :  * and \p max in that case. The minimum and maximum values are inclusive,
     225             :  * so a range of 1 to 9 is defined with exactly 1 and 9 in min and max.
     226             :  * For example, the z library compression could be retrieved with:
     227             :  *
     228             :  * \code
     229             :  * int level(6); // default to 6
     230             :  * if(opt.is_defined("zlevel"))
     231             :  * {
     232             :  *   zlevel = opt.get_long("zlevel", 0, 1, 9);
     233             :  * }
     234             :  * \endcode
     235             :  *
     236             :  * Note that the function can be used to read unsigned numbers, however
     237             :  * at this point getopt does not really support negative numbers (i.e. because
     238             :  * -\<number> is viewed as an option.)
     239             :  *
     240             :  * \exception getopt_exception_undefined
     241             :  * The getopt_exception_undefined exception is raised if \p name was not
     242             :  * found on the command line and it has no default, or if \p idx is
     243             :  * out of bounds.
     244             :  *
     245             :  * \param[in] name  The name of the option to retrieve.
     246             :  * \param[in] idx  The index of the argument to retrieve.
     247             :  * \param[in] min  The minimum value that will be returned (inclusive).
     248             :  * \param[in] max  The maximum value that will be returned (inclusive).
     249             :  *
     250             :  * \return The argument as a long.
     251             :  */
     252         122 : long getopt::get_long(std::string const & name, int idx, long min, long max)
     253             : {
     254         244 :     option_info::pointer_t opt(get_option(name));
     255         122 :     if(opt == nullptr)
     256             :     {
     257             :         throw getopt_exception_logic(
     258             :                   "there is no --"
     259           6 :                 + name
     260           9 :                 + " option defined.");
     261             :     }
     262             : 
     263         119 :     long result(0);
     264         119 :     if(!opt->is_defined())
     265             :     {
     266          88 :         std::string const d(opt->get_default());
     267          44 :         if(d.empty())
     268             :         {
     269             :             throw getopt_exception_logic(
     270             :                       "the --"
     271          12 :                     + name
     272          18 :                     + " option was not defined on the command line and it has no or an empty default.");
     273             :         }
     274             :         char * end;
     275          38 :         char const * str(d.c_str());
     276          38 :         result = strtol(str, &end, 10);
     277          38 :         if(end != str + d.length())
     278             :         {
     279             :             // here we throw because this default value is defined in the
     280             :             // options of the tool and not by the user
     281             :             //
     282             :             throw getopt_exception_logic(
     283             :                       "invalid default number \""
     284           6 :                     + d
     285           6 :                     + "\" for option --"
     286           9 :                     + name);
     287             :         }
     288             :     }
     289             :     else
     290             :     {
     291          75 :         result = opt->get_long(idx);
     292             :     }
     293             : 
     294             :     // TODO: replace with validators
     295             :     //
     296         110 :     if(result < min || result > max)
     297             :     {
     298           4 :         log << log_level_t::error
     299           2 :             << result
     300           2 :             << " is out of bounds ("
     301           2 :             << min
     302           2 :             << ".."
     303           2 :             << max
     304           2 :             << " inclusive) in parameter --"
     305           2 :             << name
     306           2 :             << "."
     307           2 :             << end;
     308           2 :         result = -1;
     309             :     }
     310             : 
     311         220 :     return result;
     312             : }
     313             : 
     314             : 
     315             : /** \brief Get the content of an option as a string.
     316             :  *
     317             :  * Get the content of the named parameter as a string. Command line options
     318             :  * that accept multiple arguments accept the \p idx parameter to
     319             :  * specify which item you are interested in.
     320             :  *
     321             :  * Note that the option must have been specified on the command line or have
     322             :  * a default value. For options that do not have a default value, you want
     323             :  * to call the is_defined() function first.
     324             :  *
     325             :  * \exception getopt_exception_undefined
     326             :  * The getopt_exception_undefined exception is raised if \p name was not
     327             :  * found on the command line and it has no default, or if \p idx is
     328             :  * out of bounds.
     329             :  *
     330             :  * \param[in] name  The name of the option to read.
     331             :  * \param[in] idx  The zero based index of a multi-argument command line option.
     332             :  *
     333             :  * \return The option argument as a string.
     334             :  */
     335         498 : std::string getopt::get_string(std::string const & name, int idx) const
     336             : {
     337         996 :     option_info::pointer_t opt(get_option(name));
     338         498 :     if(opt == nullptr)
     339             :     {
     340             :         throw getopt_exception_logic(
     341             :                   "there is no --"
     342           6 :                 + name
     343           9 :                 + " option defined.");
     344             :     }
     345             : 
     346         495 :     if(!opt->is_defined())
     347             :     {
     348          41 :         if(opt->has_default())
     349             :         {
     350          36 :             return opt->get_default();
     351             :         }
     352             :         throw getopt_exception_logic(
     353             :                   "the --"
     354          10 :                 + name
     355          15 :                 + " option was not defined on the command line and it has no default.");
     356             :     }
     357             : 
     358         454 :     return opt->get_value(idx);
     359             : }
     360             : 
     361             : 
     362             : /** \brief Retrieve the value of an argument.
     363             :  *
     364             :  * This operator returns the value of an argument just like the get_string()
     365             :  * does when the argument is defined. When the argument is not defined and it
     366             :  * has no default, it returns an empty string instead of throwing.
     367             :  *
     368             :  * The function is only capable of returning the very first value. If this
     369             :  * argument has the GETOPT_FLAG_MULTIPLE flag set, you probably want to use
     370             :  * the get_string() instead.
     371             :  *
     372             :  * \param[in] name  The name of the option to retrieve.
     373             :  *
     374             :  * \return The value of that option or an empty string if not defined.
     375             :  */
     376         193 : std::string getopt::operator [] (std::string const & name) const
     377             : {
     378         193 :     if(name.empty())
     379             :     {
     380           2 :         throw getopt_exception_logic("argument name cannot be empty.");
     381             :     }
     382             : 
     383         382 :     option_info::pointer_t opt(get_option(name));
     384         191 :     if(opt == nullptr)
     385             :     {
     386           1 :         return std::string();
     387             :     }
     388             : 
     389         190 :     if(!opt->is_defined())
     390             :     {
     391           6 :         if(opt->has_default())
     392             :         {
     393           5 :             return opt->get_default();
     394             :         }
     395           1 :         return std::string();
     396             :     }
     397             : 
     398         184 :     return opt->get_value(0);
     399             : }
     400             : 
     401             : 
     402             : /** \brief Access a parameter in read and write mode.
     403             :  *
     404             :  * This function allows you to access an argument which may or may not
     405             :  * yet exist.
     406             :  *
     407             :  * The return value is a reference to that parameter. You can read
     408             :  * and write to the reference.
     409             :  *
     410             :  * A non-existant argument is created only if necessary. That is,
     411             :  * only if you actually use an assignment operator as follow:
     412             :  *
     413             :  * \code
     414             :  *      // straight assignment:
     415             :  *      opt["my-var"] = "123";
     416             :  *
     417             :  *      // or concatenation:
     418             :  *      opt["my-var"] += "append";
     419             :  * \endcode
     420             :  *
     421             :  * In read mode and unless you defined a default, a non-existant argument
     422             :  * is viewed as an empty string or 0 if retrieved as a long:
     423             :  *
     424             :  * \code
     425             :  *      // if non-existant you get an empty string:
     426             :  *      std::string value = opt["non-existant"];
     427             :  *
     428             :  *      // if non-existant you get zero:
     429             :  *      long value = opt["non-existant"].get_long();
     430             :  * \endcode
     431             :  *
     432             :  * The get_long() function may generate an error if the parameter is not
     433             :  * a valid integer. Also when a default is defined, it tries to convert
     434             :  * the default value to a number and if that fails an error is generated.
     435             :  *
     436             :  * \note
     437             :  * This operator only allows you to access the very first value of
     438             :  * this option. If the option is marked with GETOPT_FLAG_MULTIPLE,
     439             :  * you may want to use the get_option() function and then handle
     440             :  * the option multiple values manually with the option_info::get_value()
     441             :  * and option_info::set_value().
     442             :  *
     443             :  * \warning
     444             :  * If the option is an alias and the destination is not defined you
     445             :  * can still get an exception raised.
     446             :  *
     447             :  * \param[in] name  The name of the option to access.
     448             :  *
     449             :  * \return A reference to this option with support for many std::string like
     450             :  *         operators.
     451             :  */
     452         163 : option_info_ref getopt::operator [] (std::string const & name)
     453             : {
     454         163 :     if(name.empty())
     455             :     {
     456           2 :         throw getopt_exception_logic("argument name cannot be empty.");
     457             :     }
     458             : 
     459         322 :     option_info::pointer_t opt(get_option(name));
     460         161 :     if(opt == nullptr)
     461             :     {
     462          54 :         if(name.length() == 1)
     463             :         {
     464           2 :             throw getopt_exception_logic("argument name cannot be one letter if it does not exist in operator [].");
     465             :         }
     466             : 
     467             :         // The option doesn't exist yet, create it
     468             :         //
     469          52 :         opt = std::make_shared<option_info>(name);
     470          52 :         f_options_by_name[name] = opt;
     471             :     }
     472             : 
     473         318 :     return option_info_ref(opt);
     474             : }
     475             : 
     476             : 
     477             : /** \brief Process the system options.
     478             :  *
     479             :  * If you have the GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS flag turned on,
     480             :  * then several options are automatically added to your list of supported
     481             :  * options, such as `--version`.
     482             :  *
     483             :  * This function processes these options if any were used by the client.
     484             :  *
     485             :  * If the function finds one or more system flags as being defined, it
     486             :  * returns a non-zero set of SYSTEM_OPTION_... flags. This can be useful
     487             :  * to decide whether to continue processing or not.
     488             :  *
     489             :  * We define a set of flags that can help you decide whether to continue
     490             :  * or exit. In most cases, we propose that you exit your program if any
     491             :  * one of the options was a command. This is done like so:
     492             :  *
     493             :  * \code
     494             :  * if(process_system_options & SYSTEM_OPTION_COMMANDS_MASK) != 0)
     495             :  * {
     496             :  *     exit(1);
     497             :  * }
     498             :  * \endcode
     499             :  *
     500             :  * You may still want to continue, though, if other flags where set,
     501             :  * even if some commands were used. For example, some tools will print
     502             :  * their version and move forward with there work (i.e. compilers often do
     503             :  * that to help with logging all the information about a build process,
     504             :  * including the version of the compiler.)
     505             :  *
     506             :  * \param[in] out  The stream where output is sent if required.
     507             :  *
     508             :  * \return non-zero set of flags if any of the system parameters were processed.
     509             :  */
     510          28 : flag_t getopt::process_system_options(std::basic_ostream<char> & out)
     511             : {
     512          28 :     flag_t result(SYSTEM_OPTION_NONE);
     513             : 
     514             :     // --version
     515          28 :     if(is_defined("version"))
     516             :     {
     517           2 :         out << f_options_environment.f_version << std::endl;
     518           2 :         result |= SYSTEM_OPTION_VERSION;
     519             :     }
     520             : 
     521             :     // --help
     522          28 :     if(is_defined("help"))
     523             :     {
     524           1 :         out << usage() << std::endl;
     525           1 :         result |= SYSTEM_OPTION_HELP;
     526             :     }
     527             : 
     528             :     // --long-help
     529          28 :     if(is_defined("long-help"))
     530             :     {
     531           1 :         out << usage(GETOPT_FLAG_SHOW_ALL) << std::endl;
     532           1 :         result |= SYSTEM_OPTION_HELP;
     533             :     }
     534             : 
     535          28 :     if(f_options_environment.f_groups != nullptr)
     536             :     {
     537           6 :         for(group_description const * grp = f_options_environment.f_groups
     538           6 :           ; grp->f_group != GETOPT_FLAG_GROUP_NONE
     539             :           ; ++grp)
     540             :         {
     541             :             // the name is not mandatory, without it you do not get the command
     542             :             // line option but still get the group description
     543             :             //
     544           4 :             if(grp->f_name != nullptr
     545           4 :             && *grp->f_name != '\0')
     546             :             {
     547           8 :                 std::string const name(grp->f_name);
     548           8 :                 std::string const option_name(name + "-help");
     549           4 :                 if(is_defined(option_name))
     550             :                 {
     551           2 :                     out << usage(grp->f_group) << std::endl;
     552           2 :                     result |= SYSTEM_OPTION_HELP;
     553             :                 }
     554             :             }
     555             :         }
     556             :     }
     557             : 
     558             :     // --copyright
     559          28 :     if(is_defined("copyright"))
     560             :     {
     561           2 :         out << f_options_environment.f_copyright << std::endl;
     562           2 :         result |= SYSTEM_OPTION_COPYRIGHT;
     563             :     }
     564             : 
     565             :     // --license
     566          28 :     if(is_defined("license"))
     567             :     {
     568           2 :         out << f_options_environment.f_license << std::endl;
     569           2 :         result |= SYSTEM_OPTION_LICENSE;
     570             :     }
     571             : 
     572             :     // --build-date
     573          28 :     if(is_defined("build-date"))
     574             :     {
     575           2 :         out << "Built on "
     576           4 :             << f_options_environment.f_build_date
     577           2 :             << " at "
     578           4 :             << f_options_environment.f_build_time
     579           2 :             << std::endl;
     580           2 :         result |= SYSTEM_OPTION_BUILD_DATE;
     581             :     }
     582             : 
     583             :     // --environment-variable-name
     584          28 :     if(is_defined("environment-variable-name"))
     585             :     {
     586           3 :         if(f_options_environment.f_environment_variable_name == nullptr
     587           2 :         || *f_options_environment.f_environment_variable_name == '\0')
     588             :         {
     589           2 :             out << f_options_environment.f_project_name
     590           2 :                 << " does not support an environment variable."
     591           2 :                 << std::endl;
     592             :         }
     593             :         else
     594             :         {
     595           1 :             out << f_options_environment.f_environment_variable_name << std::endl;
     596             :         }
     597           3 :         result |= SYSTEM_OPTION_ENVIRONMENT_VARIABLE_NAME;
     598             :     }
     599             : 
     600             :     // --configuration-filenames
     601          28 :     if(is_defined("configuration-filenames"))
     602             :     {
     603           6 :         string_list_t list(get_configuration_filenames(false, false));
     604           3 :         if(list.empty())
     605             :         {
     606           1 :             out << f_options_environment.f_project_name
     607           1 :                 << " does not support configuration files."
     608           1 :                 << std::endl;
     609             :         }
     610             :         else
     611             :         {
     612           2 :             out << "Configuration filenames:" << std::endl;
     613          26 :             for(auto n : list)
     614             :             {
     615          24 :                 out << " . " << n << std::endl;
     616             :             }
     617             :         }
     618           3 :         result |= SYSTEM_OPTION_CONFIGURATION_FILENAMES;
     619             :     }
     620             : 
     621             :     // --path-to-option-definitions
     622          28 :     if(is_defined("path-to-option-definitions"))
     623             :     {
     624           2 :         if(f_options_environment.f_options_files_directory == nullptr
     625           1 :         || *f_options_environment.f_options_files_directory == '\0')
     626             :         {
     627           1 :             out << "/usr/share/advgetopt/options" << std::endl;
     628             :         }
     629             :         else
     630             :         {
     631           1 :             out << f_options_environment.f_options_files_directory << std::endl;
     632             :         }
     633           2 :         result |= SYSTEM_OPTION_PATH_TO_OPTION_DEFINITIONS;
     634             :     }
     635             : 
     636             :     // --config-dir
     637          28 :     if(is_defined("config-dir"))
     638             :     {
     639             :         // these are automatically used in the get_configuration_filenames()
     640             :         // function, there is nothing for us to do here
     641             :         //
     642           1 :         result |= SYSTEM_OPTION_CONFIG_DIR;
     643             :     }
     644             : 
     645          28 :     return result;
     646             : }
     647             : 
     648             : 
     649             : 
     650             : 
     651             : 
     652           6 : } // namespace advgetopt
     653             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12