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

          Line data    Source code
       1             : /*
       2             :  * File:
       3             :  *    advgetopt/utils.cpp -- a replacement to the Unix getopt() 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             : 
      31             : /** \file
      32             :  * \brief Implementation of utility functions.
      33             :  *
      34             :  * This file includes various utility functions that are not specifically
      35             :  * attached to a class.
      36             :  */
      37             : 
      38             : // self
      39             : //
      40             : #include "advgetopt/utils.h"
      41             : 
      42             : 
      43             : // boost lib
      44             : //
      45             : #include <boost/algorithm/string/trim.hpp>
      46             : 
      47             : 
      48             : // last include
      49             : //
      50             : #include <snapdev/poison.h>
      51             : 
      52             : 
      53             : 
      54             : namespace advgetopt
      55             : {
      56             : 
      57             : 
      58             : 
      59             : /** \brief Remove single (') or double (") quotes from a string.
      60             :  *
      61             :  * If a string starts and ends with the same quotation mark, then it
      62             :  * gets removed.
      63             :  *
      64             :  * If no quotes appear, then the function returns a copy of the input as is.
      65             :  *
      66             :  * \param[in] s  The string to unquote.
      67             :  *
      68             :  * \return The unquoted string.
      69             :  */
      70          53 : std::string unquote(std::string const & s)
      71             : {
      72          53 :     if(s.length() >= 2)
      73             :     {
      74         108 :         if((s[0] == '"' && s.back() == '"')
      75          89 :         || (s[0] == '\'' && s.back() == '\''))
      76             :         {
      77          16 :             return s.substr(1, s.length() - 2);
      78             :         }
      79             :     }
      80             : 
      81          37 :     return s;
      82             : }
      83             : 
      84             : 
      85             : /** \brief Split a string in sub-strings separated by \p separators.
      86             :  *
      87             :  * This function searches for any of the \p separators in \p str and
      88             :  * split at those locations.
      89             :  *
      90             :  * For example, to split a comma separated list of strings, use the
      91             :  * following:
      92             :  *
      93             :  * \code
      94             :  *     string_list_t result;
      95             :  *     option_info::split_string(string_to_split, result, {","});
      96             :  * \endcode
      97             :  *
      98             :  * If `string_to_split` is set to "a, b, c", then the `result` vector
      99             :  * will have three strings as a result: `a`, `b`, and `c`. Note that
     100             :  * the function automatically trims all strings and it never keeps
     101             :  * empty strings. So two separators one after another is accepted and
     102             :  * no empty string results.
     103             :  *
     104             :  * The trimming happens after the split occurs. This allows for the
     105             :  * list of separators to include spaces as separators.
     106             :  *
     107             :  * The function does not clear the result vector. This allows you to
     108             :  * call this function multiple times with various strings and the
     109             :  * results will be cumulated.
     110             :  *
     111             :  * \note
     112             :  * This function is a static so it can be used from anywhere to split
     113             :  * strings as required. You do not need to have an option_info instance.
     114             :  *
     115             :  * \todo
     116             :  * See to fix the fact that `a"b"c` becomes `{"a", "b", "c"}` when
     117             :  * there are not separators between `a`, `"b"`, and `c`. To the minimum
     118             :  * we may want to generate an error when such is found (i.e. when a
     119             :  * quote is found and `start < pos` is true.
     120             :  *
     121             :  * \param[in] str  The string to split.
     122             :  * \param[in] result  The vector where the split strings are saved.
     123             :  * \param[in] separators  The vector of strings used as separators.
     124             :  */
     125          91 : void split_string(std::string const & str
     126             :                 , string_list_t & result
     127             :                 , string_list_t const & separators)
     128             : {
     129          91 :     std::string::size_type pos(0);
     130          91 :     std::string::size_type start(0);
     131       12149 :     while(pos < str.length())
     132             :     {
     133        6029 :         if(str[pos] == '\'' || str[pos] == '"')
     134             :         {
     135          14 :             if(start < pos)
     136             :             {
     137          12 :                 std::string v(str.substr(start, pos - start));
     138           6 :                 boost::trim(v);
     139           6 :                 if(!v.empty())
     140             :                 {
     141           4 :                     result.push_back(v);
     142             :                 }
     143           6 :                 start = pos;
     144             :             }
     145             : 
     146             :             // quoted parameters are handled without the separators
     147             :             //
     148          14 :             char const quote(str[pos]);
     149          14 :             for(++pos; pos < str.length() && str[pos] != quote; ++pos);
     150             : 
     151          28 :             std::string const v(str.substr(start + 1, pos - (start + 1)));
     152          14 :             if(!v.empty())
     153             :             {
     154          11 :                 result.push_back(v);
     155             :             }
     156          14 :             if(pos < str.length())
     157             :             {
     158             :                 // skip the closing quote
     159             :                 //
     160          12 :                 ++pos;
     161             :             }
     162          14 :             start = pos;
     163             :         }
     164             :         else
     165             :         {
     166        6015 :             bool found(false);
     167       12097 :             for(auto const & sep : separators)
     168             :             {
     169        6365 :                 if(str.length() - pos >= sep.length())
     170             :                 {
     171        6365 :                     if(str.compare(pos, sep.length(), sep) == 0)
     172             :                     {
     173             :                         // match! cut here
     174             :                         //
     175         283 :                         if(start < pos)
     176             :                         {
     177         522 :                             std::string v(str.substr(start, pos - start));
     178         261 :                             boost::trim(v);
     179         261 :                             if(!v.empty())
     180             :                             {
     181         261 :                                 result.push_back(v);
     182             :                             }
     183             :                         }
     184         283 :                         pos += sep.length();
     185         283 :                         start = pos;
     186         283 :                         found = true;
     187         283 :                         break;
     188             :                     }
     189             :                 }
     190             :             }
     191             : 
     192        6015 :             if(!found)
     193             :             {
     194        5732 :                 ++pos;
     195             :             }
     196             :         }
     197             :     }
     198             : 
     199          91 :     if(start < pos)
     200             :     {
     201         168 :         std::string v(str.substr(start, pos - start));
     202          84 :         boost::trim(v);
     203          84 :         if(!v.empty())
     204             :         {
     205          84 :             result.push_back(v);
     206             :         }
     207             :     }
     208          91 : }
     209             : 
     210             : 
     211             : /** \brief Insert the project name in the filename.
     212             :  *
     213             :  * This function inserts the name of the project in the specified full path
     214             :  * filename. It gets added right before the basename. So for example you
     215             :  * have a path such as:
     216             :  *
     217             :  *     /etc/snapwebsites/advgetopt.conf
     218             :  *
     219             :  * and a project name such as:
     220             :  *
     221             :  *     adventure
     222             :  *
     223             :  * The resulting path is:
     224             :  *
     225             :  *     /etc/snapwebsites/adventure.d/advgetopt.conf
     226             :  *
     227             :  * Notice that the function adds a ".d" as well.
     228             :  *
     229             :  * \param[in] filename  The filename where the project name gets injected.
     230             :  * \param[in] project_name  The name of the project to inject in the filename.
     231             :  *
     232             :  * \return The new filename or an empty string if no project name or filename
     233             :  *         are specified.
     234             :  */
     235         509 : std::string insert_project_name(std::string const & filename
     236             :                               , char const * project_name)
     237             : {
     238         509 :     if(project_name == nullptr
     239         508 :     || *project_name == '\0'
     240        1016 :     || filename.empty())
     241             :     {
     242           3 :         return std::string();
     243             :     }
     244             : 
     245         506 :     std::string::size_type const pos(filename.find_last_of('/'));
     246         506 :     if(pos != std::string::npos
     247         230 :     && pos > 0)
     248             :     {
     249         460 :         return filename.substr(0, pos + 1)
     250         460 :                           + project_name
     251         460 :                           + ".d"
     252         690 :                           + filename.substr(pos);
     253             :     }
     254             : 
     255         276 :     return project_name + (".d/" + filename);
     256             : }
     257             : 
     258             : 
     259             : /** \brief Replace a starting `~/...` with the contents of the \$HOME variable.
     260             :  *
     261             :  * This function checks the beginning of \p filename. If it starts with `'~/'`
     262             :  * then it replaces the `'~'` character with the contents of the \$HOME
     263             :  * environment variable.
     264             :  *
     265             :  * If \p filename is just `"~"`, then the function returns the contents of
     266             :  * the \$HOME environment variable by itself.
     267             :  *
     268             :  * If somehow the \$HOME environment variable is empty, the function does
     269             :  * nothing.
     270             :  *
     271             :  * \param[in] filename  The filename to check for a tilde (~).
     272             :  *
     273             :  * \return The input as is unless the \$HOME path can be prepended to replace
     274             :  *         the tilde (~) character.
     275             :  */
     276         562 : std::string handle_user_directory(std::string const & filename)
     277             : {
     278         562 :     char const * const home(getenv("HOME"));
     279         562 :     if(home != nullptr
     280         404 :     && *home != '\0')
     281             :     {
     282         804 :         if(!filename.empty()
     283         402 :         && filename[0] == '~'
     284         458 :         && (filename.length() == 1 || filename[1] == '/'))
     285             :         {
     286          56 :             return home + filename.substr(1);
     287             :         }
     288             :     }
     289             : 
     290         506 :     return filename;
     291             : }
     292             : 
     293             : 
     294             : 
     295             : }   // namespace advgetopt
     296             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12