LCOV - code coverage report
Current view: top level - advgetopt - utils.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 101 101 100.0 %
Date: 2020-11-13 17:54:34 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2006-2020  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/
       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             : 
      21             : /** \file
      22             :  * \brief Implementation of utility functions.
      23             :  *
      24             :  * This file includes various utility functions that are not specifically
      25             :  * attached to a class.
      26             :  */
      27             : 
      28             : // self
      29             : //
      30             : #include    "advgetopt/utils.h"
      31             : 
      32             : 
      33             : // snapdev lib
      34             : //
      35             : #include    <snapdev/glob_to_list.h>
      36             : #include    <snapdev/not_used.h>
      37             : 
      38             : 
      39             : // cppthread lib
      40             : //
      41             : #include    <cppthread/guard.h>
      42             : #include    <cppthread/mutex.h>
      43             : 
      44             : 
      45             : // boost lib
      46             : //
      47             : #include    <boost/algorithm/string/trim.hpp>
      48             : 
      49             : 
      50             : // C++ lib
      51             : //
      52             : #include    <set>
      53             : 
      54             : 
      55             : // last include
      56             : //
      57             : #include    <snapdev/poison.h>
      58             : 
      59             : 
      60             : 
      61             : namespace advgetopt
      62             : {
      63             : 
      64             : 
      65             : 
      66             : namespace
      67             : {
      68             : 
      69             : 
      70             : 
      71             : /** \brief The configuration file mutex.
      72             :  *
      73             :  * This options are generally viewed as read-only global variables. They
      74             :  * get setup once early on and then used and reused as many times as
      75             :  * required.
      76             :  *
      77             :  * This mutex makes sure that access between multiple thread happens in
      78             :  * a safe manner.
      79             :  */
      80             : cppthread::mutex *      g_mutex;
      81             : 
      82             : 
      83             : 
      84             : }
      85             : // no name namespace
      86             : 
      87             : 
      88             : 
      89             : /** \brief Get a global mutex.
      90             :  *
      91             :  * This function returns a global mutex we can use to lock the advgetopt
      92             :  * whenever multithread functionality is required (i.e. a global is used.)
      93             :  *
      94             :  * It is safe to call this function early (i.e. before main was ever
      95             :  * called.)
      96             :  *
      97             :  * Usage:
      98             :  *
      99             :  * \code
     100             :  *    cppthread::guard lock(get_global_mutex());
     101             :  * \endcode
     102             :  *
     103             :  * \return A reference to our global mutex.
     104             :  */
     105        7017 : cppthread::mutex & get_global_mutex()
     106             : {
     107             :     {
     108       14034 :         cppthread::guard lock(*cppthread::g_system_mutex);
     109             : 
     110        7017 :         if(g_mutex == nullptr)
     111             :         {
     112           1 :             g_mutex = new cppthread::mutex();
     113             :         }
     114             :     }
     115             : 
     116        7017 :     return *g_mutex;
     117             : }
     118             : 
     119             : 
     120             : 
     121             : /** \brief Remove single (') or double (") quotes from a string.
     122             :  *
     123             :  * If a string starts and ends with the same quotation mark, then it
     124             :  * gets removed.
     125             :  *
     126             :  * If no quotes appear, then the function returns a copy of the input as is.
     127             :  *
     128             :  * The \p pairs parameter must have an even size (or the last character
     129             :  * gets ignored). By default, it is set to the double and single quotes:
     130             :  *
     131             :  * \code
     132             :  *     "\"\"''"
     133             :  * \endcode
     134             :  *
     135             :  * To remove square, angle, curly brackets:
     136             :  *
     137             :  * \code
     138             :  *     "[]<>{}"
     139             :  * \endcode
     140             :  *
     141             :  * \todo
     142             :  * Add support for UTF-8 quotes. Right now only quotes of 1 byte will
     143             :  * work.
     144             :  *
     145             :  * \param[in] s  The string to unquote.
     146             :  * \param[in] pairs  A list of accepted quotes.
     147             :  *
     148             :  * \return The unquoted string.
     149             :  */
     150         845 : std::string unquote(std::string const & s, std::string const & pairs)
     151             : {
     152         845 :     if(s.length() >= 2)
     153             :     {
     154         775 :         std::string::size_type const max(pairs.length() - 1);
     155        2227 :         for(std::string::size_type pos(0); pos < max; pos += 2)
     156             :         {
     157        3046 :             if(s.front() == pairs[pos + 0]
     158        1523 :             && s.back()  == pairs[pos + 1])
     159             :             {
     160          71 :                 return s.substr(1, s.length() - 2);
     161             :             }
     162             :         }
     163             :     }
     164             : 
     165         774 :     return s;
     166             : }
     167             : 
     168             : 
     169             : /** \brief Split a string in sub-strings separated by \p separators.
     170             :  *
     171             :  * This function searches for any of the \p separators in \p str and
     172             :  * split at those locations.
     173             :  *
     174             :  * For example, to split a comma separated list of strings, use the
     175             :  * following:
     176             :  *
     177             :  * \code
     178             :  *     string_list_t result;
     179             :  *     option_info::split_string(string_to_split, result, {","});
     180             :  * \endcode
     181             :  *
     182             :  * If `string_to_split` is set to "a, b, c", then the `result` vector
     183             :  * will have three strings as a result: `a`, `b`, and `c`. Note that
     184             :  * the function automatically trims all strings and it never keeps
     185             :  * empty strings. So two separators one after another is accepted and
     186             :  * no empty string results.
     187             :  *
     188             :  * The trimming happens after the split occurs. This allows for the
     189             :  * list of separators to include spaces as separators.
     190             :  *
     191             :  * The function does not clear the result vector. This allows you to
     192             :  * call this function multiple times with various strings and the
     193             :  * results will be cumulated.
     194             :  *
     195             :  * \note
     196             :  * This function is a static so it can be used from anywhere to split
     197             :  * strings as required. You do not need to have an option_info instance.
     198             :  *
     199             :  * \todo
     200             :  * See to fix the fact that `a"b"c` becomes `{"a", "b", "c"}` when
     201             :  * there are not separators between `a`, `"b"`, and `c`. To the minimum
     202             :  * we may want to generate an error when such is found (i.e. when a
     203             :  * quote is found and `start < pos` is true.
     204             :  *
     205             :  * \param[in] str  The string to split.
     206             :  * \param[in] result  The vector where the split strings are saved.
     207             :  * \param[in] separators  The vector of strings used as separators.
     208             :  */
     209          95 : void split_string(std::string const & str
     210             :                 , string_list_t & result
     211             :                 , string_list_t const & separators)
     212             : {
     213          95 :     std::string::size_type pos(0);
     214          95 :     std::string::size_type start(0);
     215       11341 :     while(pos < str.length())
     216             :     {
     217        5623 :         if(str[pos] == '\'' || str[pos] == '"')
     218             :         {
     219          14 :             if(start < pos)
     220             :             {
     221          12 :                 std::string v(str.substr(start, pos - start));
     222           6 :                 boost::trim(v);
     223           6 :                 if(!v.empty())
     224             :                 {
     225           4 :                     result.push_back(v);
     226             :                 }
     227           6 :                 start = pos;
     228             :             }
     229             : 
     230             :             // quoted parameters are handled without the separators
     231             :             //
     232          14 :             char const quote(str[pos]);
     233          14 :             for(++pos; pos < str.length() && str[pos] != quote; ++pos);
     234             : 
     235          28 :             std::string const v(str.substr(start + 1, pos - (start + 1)));
     236          14 :             if(!v.empty())
     237             :             {
     238          11 :                 result.push_back(v);
     239             :             }
     240          14 :             if(pos < str.length())
     241             :             {
     242             :                 // skip the closing quote
     243             :                 //
     244          12 :                 ++pos;
     245             :             }
     246          14 :             start = pos;
     247             :         }
     248             :         else
     249             :         {
     250        5609 :             bool found(false);
     251       11308 :             for(auto const & sep : separators)
     252             :             {
     253        5964 :                 if(str.length() - pos >= sep.length())
     254             :                 {
     255        5964 :                     if(str.compare(pos, sep.length(), sep) == 0)
     256             :                     {
     257             :                         // match! cut here
     258             :                         //
     259         265 :                         if(start < pos)
     260             :                         {
     261         486 :                             std::string v(str.substr(start, pos - start));
     262         243 :                             boost::trim(v);
     263         243 :                             if(!v.empty())
     264             :                             {
     265         243 :                                 result.push_back(v);
     266             :                             }
     267             :                         }
     268         265 :                         pos += sep.length();
     269         265 :                         start = pos;
     270         265 :                         found = true;
     271         265 :                         break;
     272             :                     }
     273             :                 }
     274             :             }
     275             : 
     276        5609 :             if(!found)
     277             :             {
     278        5344 :                 ++pos;
     279             :             }
     280             :         }
     281             :     }
     282             : 
     283          95 :     if(start < pos)
     284             :     {
     285         176 :         std::string v(str.substr(start, pos - start));
     286          88 :         boost::trim(v);
     287          88 :         if(!v.empty())
     288             :         {
     289          88 :             result.push_back(v);
     290             :         }
     291             :     }
     292          95 : }
     293             : 
     294             : 
     295             : /** \brief Insert the group (or project) name in the filename.
     296             :  *
     297             :  * This function inserts the name of the group in the specified full path
     298             :  * filename. It gets added right before the basename. So for example you
     299             :  * have a path such as:
     300             :  *
     301             :  *     /etc/snapwebsites/advgetopt.conf
     302             :  *
     303             :  * and a group name such as:
     304             :  *
     305             :  *     adventure
     306             :  *
     307             :  * The resulting path is:
     308             :  *
     309             :  *     /etc/snapwebsites/adventure.d/advgetopt.conf
     310             :  *
     311             :  * Notice that the function adds a ".d" as well.
     312             :  *
     313             :  * If the group name is empty or null, then the project name is used. If
     314             :  * both are empty, then nothing happens (the function returns an empty list).
     315             :  *
     316             :  * \param[in] filename  The filename where the project name gets injected.
     317             :  * \param[in] group_name  The name of the group to inject in the filename.
     318             :  * \param[in] project_name  The name of the project to inject in the filename.
     319             :  *
     320             :  * \return The list of filenames or an empty list if no group or project name
     321             :  *         or filename are specified.
     322             :  */
     323         527 : string_list_t insert_group_name(
     324             :           std::string const & filename
     325             :         , char const * group_name
     326             :         , char const * project_name)
     327             : {
     328         527 :     if(filename.empty())
     329             :     {
     330           5 :         return string_list_t();
     331             :     }
     332             : 
     333        1044 :     std::string name;
     334         522 :     if(group_name == nullptr
     335         160 :     || *group_name == '\0')
     336             :     {
     337         366 :         if(project_name == nullptr
     338         364 :         || *project_name == '\0')
     339             :         {
     340           4 :             return string_list_t();
     341             :         }
     342         362 :         name = project_name;
     343             :     }
     344             :     else
     345             :     {
     346         156 :         name = group_name;
     347             :     }
     348             : 
     349        1036 :     std::string pattern;
     350         518 :     std::string::size_type const pos(filename.find_last_of('/'));
     351         518 :     if(pos != std::string::npos
     352         242 :     && pos > 0)
     353             :     {
     354         968 :         pattern = filename.substr(0, pos + 1)
     355         484 :                 + name
     356         484 :                 + ".d/[0-9][0-9]-"
     357         968 :                 + filename.substr(pos + 1);
     358             :     }
     359             :     else
     360             :     {
     361         276 :         pattern = name
     362         552 :                 + (".d/[0-9][0-9]-" + filename);
     363             :     }
     364             : 
     365        1036 :     snap::glob_to_list<std::set<std::string>> glob;
     366             : 
     367             :     // the glob() function is not thread safe
     368             :     {
     369        1036 :         cppthread::guard lock(get_global_mutex());
     370         518 :         snap::NOTUSED(glob.read_path<snap::glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS>(pattern));
     371             :     }
     372             : 
     373             :     // we add the default name if none other exists
     374             :     //
     375         518 :     if(glob.empty())
     376             :     {
     377         497 :         if(pos != std::string::npos
     378         221 :         && pos > 0)
     379             :         {
     380         884 :             glob.insert(filename.substr(0, pos + 1)
     381         442 :                     + name
     382         442 :                     + ".d/50-"
     383         884 :                     + filename.substr(pos + 1));
     384             :         }
     385             :         else
     386             :         {
     387         276 :             glob.insert(name
     388         552 :                     + (".d/50-" + filename));
     389             :         }
     390             :     }
     391             : 
     392         518 :     return string_list_t(glob.begin(), glob.end());
     393             : }
     394             : 
     395             : 
     396             : /** \brief Replace a starting `~/...` with the contents of the \$HOME variable.
     397             :  *
     398             :  * This function checks the beginning of \p filename. If it starts with `'~/'`
     399             :  * then it replaces the `'~'` character with the contents of the \$HOME
     400             :  * environment variable.
     401             :  *
     402             :  * If \p filename is just `"~"`, then the function returns the contents of
     403             :  * the \$HOME environment variable by itself.
     404             :  *
     405             :  * If somehow the \$HOME environment variable is empty, the function does
     406             :  * nothing.
     407             :  *
     408             :  * \param[in] filename  The filename to check for a tilde (~).
     409             :  *
     410             :  * \return The input as is unless the \$HOME path can be prepended to replace
     411             :  *         the tilde (~) character.
     412             :  */
     413         566 : std::string handle_user_directory(std::string const & filename)
     414             : {
     415         566 :     char const * const home(getenv("HOME"));
     416         566 :     if(home != nullptr
     417         407 :     && *home != '\0')
     418             :     {
     419         810 :         if(!filename.empty()
     420         405 :         && filename[0] == '~'
     421         462 :         && (filename.length() == 1 || filename[1] == '/'))
     422             :         {
     423          57 :             return home + filename.substr(1);
     424             :         }
     425             :     }
     426             : 
     427         509 :     return filename;
     428             : }
     429             : 
     430             : 
     431             : /** \brief Check whether a value represents "true".
     432             :  *
     433             :  * This function checks a string to see whether it is one of:
     434             :  *
     435             :  * * "true"
     436             :  * * "on"
     437             :  * * "1"
     438             :  *
     439             :  * If so, then the function returns true.
     440             :  *
     441             :  * \param[in] s  The string to be checked.
     442             :  *
     443             :  * \return true if the string represents "true".
     444             :  */
     445           7 : bool is_true(std::string s)
     446             : {
     447           7 :     return s == "true" || s == "on" || s == "1";
     448             : }
     449             : 
     450             : 
     451             : /** \brief Check whether a value represents "false".
     452             :  *
     453             :  * This function checks a string to see whether it is one of:
     454             :  *
     455             :  * * "false"
     456             :  * * "off"
     457             :  * * "0"
     458             :  *
     459             :  * If so, then the function returns true.
     460             :  *
     461             :  * \param[in] s  The string to be checked.
     462             :  *
     463             :  * \return true if the string represents "false".
     464             :  */
     465           7 : bool is_false(std::string s)
     466             : {
     467           7 :     return s == "false" || s == "off" || s == "0";
     468             : }
     469             : 
     470             : 
     471             : 
     472           6 : }   // namespace advgetopt
     473             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13