LCOV - code coverage report
Current view: top level - advgetopt - utils.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 70 100.0 %
Date: 2019-08-10 16:09:07 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * License:
       3             :  *    Copyright (c) 2006-2019  Made to Order Software Corp.  All Rights Reserved
       4             :  *
       5             :  *    https://snapwebsites.org/
       6             :  *    contact@m2osw.com
       7             :  *
       8             :  *    This program is free software; you can redistribute it and/or modify
       9             :  *    it under the terms of the GNU General Public License as published by
      10             :  *    the Free Software Foundation; either version 2 of the License, or
      11             :  *    (at your option) any later version.
      12             :  *
      13             :  *    This program is distributed in the hope that it will be useful,
      14             :  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :  *    GNU General Public License for more details.
      17             :  *
      18             :  *    You should have received a copy of the GNU General Public License along
      19             :  *    with this program; if not, write to the Free Software Foundation, Inc.,
      20             :  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      21             :  *
      22             :  * Authors:
      23             :  *    Alexis Wilke   alexis@m2osw.com
      24             :  *    Doug Barbieri  doug@m2osw.com
      25             :  */
      26             : 
      27             : 
      28             : /** \file
      29             :  * \brief Implementation of utility functions.
      30             :  *
      31             :  * This file includes various utility functions that are not specifically
      32             :  * attached to a class.
      33             :  */
      34             : 
      35             : // self
      36             : //
      37             : #include "advgetopt/utils.h"
      38             : 
      39             : 
      40             : // boost lib
      41             : //
      42             : #include <boost/algorithm/string/trim.hpp>
      43             : 
      44             : 
      45             : // last include
      46             : //
      47             : #include <snapdev/poison.h>
      48             : 
      49             : 
      50             : 
      51             : namespace advgetopt
      52             : {
      53             : 
      54             : 
      55             : 
      56             : /** \brief Remove single (') or double (") quotes from a string.
      57             :  *
      58             :  * If a string starts and ends with the same quotation mark, then it
      59             :  * gets removed.
      60             :  *
      61             :  * If no quotes appear, then the function returns a copy of the input as is.
      62             :  *
      63             :  * The \p pairs parameter must have an even size (or the last character
      64             :  * gets ignored). By default, it is set to the double and single quotes:
      65             :  *
      66             :  * \code
      67             :  *     "\"\"''"
      68             :  * \endcode
      69             :  *
      70             :  * To remove square, angle, curly brackets:
      71             :  *
      72             :  * \code
      73             :  *     "[]<>{}"
      74             :  * \endcode
      75             :  *
      76             :  * \todo
      77             :  * Add support for UTF-8 quotes. Right now only quotes of 1 byte will
      78             :  * work.
      79             :  *
      80             :  * \param[in] s  The string to unquote.
      81             :  * \param[in] pairs  A list of accepted quotes.
      82             :  *
      83             :  * \return The unquoted string.
      84             :  */
      85         174 : std::string unquote(std::string const & s, std::string const & pairs)
      86             : {
      87         174 :     if(s.length() >= 2)
      88             :     {
      89         138 :         std::string::size_type const max(pairs.length() - 1);
      90         398 :         for(std::string::size_type pos(0); pos < max; pos += 2)
      91             :         {
      92         582 :             if(s.front() == pairs[pos + 0]
      93         291 :             && s.back()  == pairs[pos + 1])
      94             :             {
      95          31 :                 return s.substr(1, s.length() - 2);
      96             :             }
      97             :         }
      98             :     }
      99             : 
     100         143 :     return s;
     101             : }
     102             : 
     103             : 
     104             : /** \brief Split a string in sub-strings separated by \p separators.
     105             :  *
     106             :  * This function searches for any of the \p separators in \p str and
     107             :  * split at those locations.
     108             :  *
     109             :  * For example, to split a comma separated list of strings, use the
     110             :  * following:
     111             :  *
     112             :  * \code
     113             :  *     string_list_t result;
     114             :  *     option_info::split_string(string_to_split, result, {","});
     115             :  * \endcode
     116             :  *
     117             :  * If `string_to_split` is set to "a, b, c", then the `result` vector
     118             :  * will have three strings as a result: `a`, `b`, and `c`. Note that
     119             :  * the function automatically trims all strings and it never keeps
     120             :  * empty strings. So two separators one after another is accepted and
     121             :  * no empty string results.
     122             :  *
     123             :  * The trimming happens after the split occurs. This allows for the
     124             :  * list of separators to include spaces as separators.
     125             :  *
     126             :  * The function does not clear the result vector. This allows you to
     127             :  * call this function multiple times with various strings and the
     128             :  * results will be cumulated.
     129             :  *
     130             :  * \note
     131             :  * This function is a static so it can be used from anywhere to split
     132             :  * strings as required. You do not need to have an option_info instance.
     133             :  *
     134             :  * \todo
     135             :  * See to fix the fact that `a"b"c` becomes `{"a", "b", "c"}` when
     136             :  * there are not separators between `a`, `"b"`, and `c`. To the minimum
     137             :  * we may want to generate an error when such is found (i.e. when a
     138             :  * quote is found and `start < pos` is true.
     139             :  *
     140             :  * \param[in] str  The string to split.
     141             :  * \param[in] result  The vector where the split strings are saved.
     142             :  * \param[in] separators  The vector of strings used as separators.
     143             :  */
     144          91 : void split_string(std::string const & str
     145             :                 , string_list_t & result
     146             :                 , string_list_t const & separators)
     147             : {
     148          91 :     std::string::size_type pos(0);
     149          91 :     std::string::size_type start(0);
     150       11755 :     while(pos < str.length())
     151             :     {
     152        5832 :         if(str[pos] == '\'' || str[pos] == '"')
     153             :         {
     154          14 :             if(start < pos)
     155             :             {
     156          12 :                 std::string v(str.substr(start, pos - start));
     157           6 :                 boost::trim(v);
     158           6 :                 if(!v.empty())
     159             :                 {
     160           4 :                     result.push_back(v);
     161             :                 }
     162           6 :                 start = pos;
     163             :             }
     164             : 
     165             :             // quoted parameters are handled without the separators
     166             :             //
     167          14 :             char const quote(str[pos]);
     168          14 :             for(++pos; pos < str.length() && str[pos] != quote; ++pos);
     169             : 
     170          28 :             std::string const v(str.substr(start + 1, pos - (start + 1)));
     171          14 :             if(!v.empty())
     172             :             {
     173          11 :                 result.push_back(v);
     174             :             }
     175          14 :             if(pos < str.length())
     176             :             {
     177             :                 // skip the closing quote
     178             :                 //
     179          12 :                 ++pos;
     180             :             }
     181          14 :             start = pos;
     182             :         }
     183             :         else
     184             :         {
     185        5818 :             bool found(false);
     186       11714 :             for(auto const & sep : separators)
     187             :             {
     188        6168 :                 if(str.length() - pos >= sep.length())
     189             :                 {
     190        6168 :                     if(str.compare(pos, sep.length(), sep) == 0)
     191             :                     {
     192             :                         // match! cut here
     193             :                         //
     194         272 :                         if(start < pos)
     195             :                         {
     196         500 :                             std::string v(str.substr(start, pos - start));
     197         250 :                             boost::trim(v);
     198         250 :                             if(!v.empty())
     199             :                             {
     200         250 :                                 result.push_back(v);
     201             :                             }
     202             :                         }
     203         272 :                         pos += sep.length();
     204         272 :                         start = pos;
     205         272 :                         found = true;
     206         272 :                         break;
     207             :                     }
     208             :                 }
     209             :             }
     210             : 
     211        5818 :             if(!found)
     212             :             {
     213        5546 :                 ++pos;
     214             :             }
     215             :         }
     216             :     }
     217             : 
     218          91 :     if(start < pos)
     219             :     {
     220         168 :         std::string v(str.substr(start, pos - start));
     221          84 :         boost::trim(v);
     222          84 :         if(!v.empty())
     223             :         {
     224          84 :             result.push_back(v);
     225             :         }
     226             :     }
     227          91 : }
     228             : 
     229             : 
     230             : /** \brief Insert the project name in the filename.
     231             :  *
     232             :  * This function inserts the name of the project in the specified full path
     233             :  * filename. It gets added right before the basename. So for example you
     234             :  * have a path such as:
     235             :  *
     236             :  *     /etc/snapwebsites/advgetopt.conf
     237             :  *
     238             :  * and a project name such as:
     239             :  *
     240             :  *     adventure
     241             :  *
     242             :  * The resulting path is:
     243             :  *
     244             :  *     /etc/snapwebsites/adventure.d/advgetopt.conf
     245             :  *
     246             :  * Notice that the function adds a ".d" as well.
     247             :  *
     248             :  * \param[in] filename  The filename where the project name gets injected.
     249             :  * \param[in] project_name  The name of the project to inject in the filename.
     250             :  *
     251             :  * \return The new filename or an empty string if no project name or filename
     252             :  *         are specified.
     253             :  */
     254         509 : std::string insert_project_name(std::string const & filename
     255             :                               , char const * project_name)
     256             : {
     257         509 :     if(project_name == nullptr
     258         508 :     || *project_name == '\0'
     259        1016 :     || filename.empty())
     260             :     {
     261           3 :         return std::string();
     262             :     }
     263             : 
     264         506 :     std::string::size_type const pos(filename.find_last_of('/'));
     265         506 :     if(pos != std::string::npos
     266         230 :     && pos > 0)
     267             :     {
     268         460 :         return filename.substr(0, pos + 1)
     269         460 :                           + project_name
     270         460 :                           + ".d"
     271         690 :                           + filename.substr(pos);
     272             :     }
     273             : 
     274         276 :     return project_name + (".d/" + filename);
     275             : }
     276             : 
     277             : 
     278             : /** \brief Replace a starting `~/...` with the contents of the \$HOME variable.
     279             :  *
     280             :  * This function checks the beginning of \p filename. If it starts with `'~/'`
     281             :  * then it replaces the `'~'` character with the contents of the \$HOME
     282             :  * environment variable.
     283             :  *
     284             :  * If \p filename is just `"~"`, then the function returns the contents of
     285             :  * the \$HOME environment variable by itself.
     286             :  *
     287             :  * If somehow the \$HOME environment variable is empty, the function does
     288             :  * nothing.
     289             :  *
     290             :  * \param[in] filename  The filename to check for a tilde (~).
     291             :  *
     292             :  * \return The input as is unless the \$HOME path can be prepended to replace
     293             :  *         the tilde (~) character.
     294             :  */
     295         562 : std::string handle_user_directory(std::string const & filename)
     296             : {
     297         562 :     char const * const home(getenv("HOME"));
     298         562 :     if(home != nullptr
     299         404 :     && *home != '\0')
     300             :     {
     301         804 :         if(!filename.empty()
     302         402 :         && filename[0] == '~'
     303         458 :         && (filename.length() == 1 || filename[1] == '/'))
     304             :         {
     305          56 :             return home + filename.substr(1);
     306             :         }
     307             :     }
     308             : 
     309         506 :     return filename;
     310             : }
     311             : 
     312             : 
     313             : 
     314             : }   // namespace advgetopt
     315             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12