LCOV - code coverage report
Current view: top level - port - cplstring.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 184 192 95.8 %
Date: 2025-11-23 23:10:10 Functions: 22 23 95.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  CPLString implementation.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "cpl_string.h"
      16             : 
      17             : #include <cctype>
      18             : #include <cstdarg>
      19             : #include <cstddef>
      20             : #include <cstring>
      21             : #include <string>
      22             : 
      23             : #include "cpl_config.h"
      24             : #include "cpl_conv.h"
      25             : 
      26             : #if !defined(va_copy) && defined(__va_copy)
      27             : #define va_copy __va_copy
      28             : #endif
      29             : 
      30             : /*
      31             :  * The CPLString class is derived from std::string, so the vast majority
      32             :  * of the implementation comes from that.  This module is just the extensions
      33             :  * we add.
      34             :  */
      35             : 
      36             : /************************************************************************/
      37             : /*                               Printf()                               */
      38             : /************************************************************************/
      39             : 
      40             : /** Assign the content of the string using sprintf() */
      41      503119 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
      42             : 
      43             : {
      44             :     va_list args;
      45             : 
      46      503119 :     va_start(args, pszFormat);
      47      503119 :     vPrintf(pszFormat, args);
      48      503074 :     va_end(args);
      49             : 
      50      503074 :     return *this;
      51             : }
      52             : 
      53             : /************************************************************************/
      54             : /*                              vPrintf()                               */
      55             : /************************************************************************/
      56             : 
      57             : /** Assign the content of the string using vsprintf() */
      58      620151 : CPLString &CPLString::vPrintf(CPL_FORMAT_STRING(const char *pszFormat),
      59             :                               va_list args)
      60             : 
      61             : {
      62             :     /* -------------------------------------------------------------------- */
      63             :     /*      This implementation for platforms without vsnprintf() will      */
      64             :     /*      just plain fail if the formatted contents are too large.        */
      65             :     /* -------------------------------------------------------------------- */
      66             : 
      67             : #if !defined(HAVE_VSNPRINTF)
      68             :     char *pszBuffer = static_cast<char *>(CPLMalloc(30000));
      69             :     if (CPLvsnprintf(pszBuffer, 30000, pszFormat, args) > 29998)
      70             :     {
      71             :         CPLError(CE_Fatal, CPLE_AppDefined,
      72             :                  "CPLString::vPrintf() ... buffer overrun.");
      73             :     }
      74             :     *this = pszBuffer;
      75             :     CPLFree(pszBuffer);
      76             : 
      77             : /* -------------------------------------------------------------------- */
      78             : /*      This should grow a big enough buffer to hold any formatted      */
      79             : /*      result.                                                         */
      80             : /* -------------------------------------------------------------------- */
      81             : #else
      82             :     va_list wrk_args;
      83             : 
      84             : #ifdef va_copy
      85      620151 :     va_copy(wrk_args, args);
      86             : #else
      87             :     wrk_args = args;
      88             : #endif
      89             : 
      90      620151 :     char szModestBuffer[500] = {};
      91      620151 :     szModestBuffer[0] = '\0';
      92      620151 :     int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
      93             :                            wrk_args);
      94      620102 :     if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
      95             :     {
      96        1254 :         int nWorkBufferSize = 2000;
      97        1254 :         char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
      98             : 
      99             : #ifdef va_copy
     100        1250 :         va_end(wrk_args);
     101        1250 :         va_copy(wrk_args, args);
     102             : #else
     103             :         wrk_args = args;
     104             : #endif
     105        1457 :         while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
     106        1457 :                                    wrk_args)) >= nWorkBufferSize - 1 ||
     107             :                nPR == -1)
     108             :         {
     109         207 :             nWorkBufferSize *= 4;
     110             :             pszWorkBuffer =
     111         207 :                 static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
     112             : #ifdef va_copy
     113         207 :             va_end(wrk_args);
     114         207 :             va_copy(wrk_args, args);
     115             : #else
     116             :             wrk_args = args;
     117             : #endif
     118             :         }
     119        1250 :         *this = pszWorkBuffer;
     120        1250 :         CPLFree(pszWorkBuffer);
     121             :     }
     122             :     else
     123             :     {
     124      618848 :         *this = szModestBuffer;
     125             :     }
     126             : #ifdef va_copy
     127      620104 :     va_end(wrk_args);
     128             : #endif
     129             : 
     130             : #endif /* !defined(HAVE_VSNPRINTF) */
     131             : 
     132      620104 :     return *this;
     133             : }
     134             : 
     135             : /************************************************************************/
     136             : /*                              FormatC()                               */
     137             : /************************************************************************/
     138             : 
     139             : /**
     140             :  * Format double in C locale.
     141             :  *
     142             :  * The passed value is formatted using the C locale (period as decimal
     143             :  * separator) and appended to the target CPLString.
     144             :  *
     145             :  * @param dfValue the value to format.
     146             :  * @param pszFormat the sprintf() style format to use or omit for default.
     147             :  * Note that this format string should only include one substitution argument
     148             :  * and it must be for a double (%f or %g).
     149             :  *
     150             :  * @return a reference to the CPLString.
     151             :  */
     152             : 
     153        3546 : CPLString &CPLString::FormatC(double dfValue, const char *pszFormat)
     154             : 
     155             : {
     156        3546 :     if (pszFormat == nullptr)
     157        3528 :         pszFormat = "%g";
     158             : 
     159             :     // presumably long enough for any number.
     160        3546 :     const size_t buf_size = 512;
     161        3546 :     char szWork[buf_size] = {};
     162             : 
     163        3546 :     CPLsnprintf(szWork, buf_size, pszFormat, dfValue);
     164             : 
     165        3546 :     *this += szWork;
     166             : 
     167        3546 :     return *this;
     168             : }
     169             : 
     170             : /************************************************************************/
     171             : /*                                Trim()                                */
     172             : /************************************************************************/
     173             : 
     174             : /**
     175             :  * Trim white space.
     176             :  *
     177             :  * Trims white space off the let and right of the string.  White space
     178             :  * is any of a space, a tab, a newline ('\\n') or a carriage control ('\\r').
     179             :  *
     180             :  * @return a reference to the CPLString.
     181             :  */
     182             : 
     183      849359 : CPLString &CPLString::Trim()
     184             : 
     185             : {
     186      849359 :     constexpr char szWhitespace[] = " \t\r\n";
     187             : 
     188      849359 :     const size_t iLeft = find_first_not_of(szWhitespace);
     189      849359 :     const size_t iRight = find_last_not_of(szWhitespace);
     190             : 
     191      849359 :     if (iLeft == std::string::npos)
     192             :     {
     193       92158 :         erase();
     194       92158 :         return *this;
     195             :     }
     196             : 
     197      757201 :     assign(substr(iLeft, iRight - iLeft + 1));
     198             : 
     199      757201 :     return *this;
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                               Recode()                               */
     204             : /************************************************************************/
     205             : 
     206             : /** Recode the string */
     207      467425 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
     208             :                              const char *pszDstEncoding)
     209             : 
     210             : {
     211      467425 :     if (pszSrcEncoding == nullptr)
     212           0 :         pszSrcEncoding = CPL_ENC_UTF8;
     213      467425 :     if (pszDstEncoding == nullptr)
     214           0 :         pszDstEncoding = CPL_ENC_UTF8;
     215             : 
     216      467425 :     if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
     217          50 :         return *this;
     218             : 
     219      467375 :     char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
     220             : 
     221      467375 :     if (pszRecoded == nullptr)
     222           0 :         return *this;
     223             : 
     224      467375 :     assign(pszRecoded);
     225      467375 :     CPLFree(pszRecoded);
     226             : 
     227      467375 :     return *this;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                               ifind()                                */
     232             : /************************************************************************/
     233             : 
     234             : /**
     235             :  * Case insensitive find() alternative.
     236             :  *
     237             :  * @param str substring to find.
     238             :  * @param pos offset in the string at which the search starts.
     239             :  * @return the position of substring in the string or std::string::npos if not
     240             :  * found.
     241             :  */
     242             : 
     243        7353 : size_t CPLString::ifind(const std::string &str, size_t pos) const
     244             : 
     245             : {
     246        7353 :     return ifind(str.c_str(), pos);
     247             : }
     248             : 
     249             : /**
     250             :  * Case insensitive find() alternative.
     251             :  *
     252             :  * @param s substring to find.
     253             :  * @param nPos offset in the string at which the search starts.
     254             :  * @return the position of the substring in the string or std::string::npos if
     255             :  * not found.
     256             :  */
     257             : 
     258      247071 : size_t CPLString::ifind(const char *s, size_t nPos) const
     259             : 
     260             : {
     261      247071 :     const char *pszHaystack = c_str();
     262             :     const char chFirst =
     263      247067 :         static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
     264      247065 :     const size_t nTargetLen = strlen(s);
     265             : 
     266      247065 :     if (nPos > size())
     267           0 :         nPos = size();
     268             : 
     269      247066 :     pszHaystack += nPos;
     270             : 
     271     9353210 :     while (*pszHaystack != '\0')
     272             :     {
     273     9115060 :         if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
     274             :         {
     275      413232 :             if (EQUALN(pszHaystack, s, nTargetLen))
     276        8915 :                 return nPos;
     277             :         }
     278             : 
     279     9106140 :         nPos++;
     280     9106140 :         pszHaystack++;
     281             :     }
     282             : 
     283      238149 :     return std::string::npos;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                              toupper()                               */
     288             : /************************************************************************/
     289             : 
     290             : /**
     291             :  * Convert to upper case in place.
     292             :  */
     293             : 
     294     3527490 : CPLString &CPLString::toupper()
     295             : 
     296             : {
     297    29706300 :     for (size_t i = 0; i < size(); i++)
     298    26178800 :         (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
     299             : 
     300     3527490 :     return *this;
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                              tolower()                               */
     305             : /************************************************************************/
     306             : 
     307             : /**
     308             :  * Convert to lower case in place.
     309             :  */
     310             : 
     311      126437 : CPLString &CPLString::tolower()
     312             : 
     313             : {
     314     1808360 :     for (size_t i = 0; i < size(); i++)
     315     1681940 :         (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
     316             : 
     317      126439 :     return *this;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                             replaceAll()                             */
     322             : /************************************************************************/
     323             : 
     324             : /**
     325             :  * Replace all occurrences of osBefore with osAfter.
     326             :  */
     327     1215340 : CPLString &CPLString::replaceAll(const std::string &osBefore,
     328             :                                  const std::string &osAfter)
     329             : {
     330     1215340 :     const size_t nBeforeSize = osBefore.size();
     331     1215320 :     const size_t nAfterSize = osAfter.size();
     332     1215320 :     if (nBeforeSize)
     333             :     {
     334     1215310 :         size_t nStartPos = 0;
     335     1316960 :         while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
     336             :         {
     337      101676 :             replace(nStartPos, nBeforeSize, osAfter);
     338      101655 :             nStartPos += nAfterSize;
     339             :         }
     340             :     }
     341     1215300 :     return *this;
     342             : }
     343             : 
     344             : /**
     345             :  * Replace all occurrences of chBefore with osAfter.
     346             :  */
     347        4401 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
     348             : {
     349        4401 :     return replaceAll(std::string(&chBefore, 1), osAfter);
     350             : }
     351             : 
     352             : /**
     353             :  * Replace all occurrences of osBefore with chAfter.
     354             :  */
     355     1149400 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
     356             : {
     357     1149400 :     return replaceAll(osBefore, std::string(&chAfter, 1));
     358             : }
     359             : 
     360             : /**
     361             :  * Replace all occurrences of chBefore with chAfter.
     362             :  */
     363       19055 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
     364             : {
     365       19055 :     return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
     366             : }
     367             : 
     368             : /************************************************************************/
     369             : /*                             endsWith()                              */
     370             : /************************************************************************/
     371             : 
     372             : /**
     373             :  * Returns whether the string ends with another string
     374             :  * @param osStr other string.
     375             :  * @return true if the string ends with osStr.
     376             :  */
     377       20565 : bool CPLString::endsWith(const std::string &osStr) const
     378             : {
     379       20565 :     if (size() < osStr.size())
     380         150 :         return false;
     381       20412 :     return substr(size() - osStr.size()) == osStr;
     382             : }
     383             : 
     384             : /************************************************************************/
     385             : /*                             URLEncode()                              */
     386             : /************************************************************************/
     387             : 
     388             : /**
     389             :  * Return a string that *can* be a valid URL.
     390             :  *
     391             :  * Said otherwise if URLEncode() != *this was not a valid URL according to
     392             :  * https://datatracker.ietf.org/doc/html/rfc3986.html.
     393             :  *
     394             :  * This replaces all characters that are not reserved (:/?#[]\@!$&'()*+,;=),
     395             :  * unreserved (a-z, A-Z, 0-9 and -.-~) or already percent-encoded by their
     396             :  * percent-encoding.
     397             :  *
     398             :  * Note that when composing a URL, and typically query-parameters, it might
     399             :  * be needed to use CPLEscape(,,CPLES_URL) to also substitute reserved
     400             :  * characters.
     401             :  *
     402             :  * @return a URL encoded string
     403             :  * @since 3.12
     404             :  */
     405          55 : CPLString CPLString::URLEncode() const
     406             : {
     407             :     // Helper to check if a substring is a valid percent-encoding
     408          21 :     auto isPercentEncoded = [](const char *str) -> bool
     409             :     {
     410          42 :         return str[0] == '%' &&
     411          42 :                std::isxdigit(static_cast<unsigned char>(str[1])) &&
     412          42 :                std::isxdigit(static_cast<unsigned char>(str[2]));
     413             :     };
     414             : 
     415             :     // Cf https://datatracker.ietf.org/doc/html/rfc3986.html
     416          55 :     const char *unreserved =
     417             :         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
     418          55 :     const char *reserved = ":/?#[]@!$&'()*+,;=";
     419          55 :     CPLString osEncoded;
     420          55 :     osEncoded.reserve(size());
     421        4498 :     for (size_t i = 0; i < size(); ++i)
     422             :     {
     423        4443 :         char ch = (*this)[i];
     424             :         // If already percent-encoded, copy as is
     425        4443 :         if (ch == '%' && i + 2 < size() && isPercentEncoded(c_str() + i))
     426             :         {
     427          21 :             osEncoded += ch;
     428          21 :             osEncoded += (*this)[i + 1];
     429          21 :             osEncoded += (*this)[i + 2];
     430          21 :             i += 2;
     431             :         }
     432        4422 :         else if (strchr(unreserved, ch) || strchr(reserved, ch))
     433             :         {
     434        4409 :             osEncoded += ch;
     435             :         }
     436             :         else
     437             :         {
     438             :             char buf[4];
     439          13 :             snprintf(buf, sizeof(buf), "%%%02X",
     440          13 :                      static_cast<unsigned char>(ch));
     441          13 :             osEncoded += buf;
     442             :         }
     443             :     }
     444         110 :     return osEncoded;
     445             : }
     446             : 
     447             : /************************************************************************/
     448             : /*                          SQLQuotedIdentifier()                       */
     449             : /************************************************************************/
     450             : 
     451             : /** Returns a string between double quotes and with all double quotes
     452             :  * inside the string are escaped by being doubled.
     453             :  *
     454             :  * Aimed at being used for SQL identifiers (table names, column names, etc.).
     455             :  *
     456             :  * @since 3.13
     457             :  */
     458          16 : CPLString CPLString::SQLQuotedIdentifier() const
     459             : {
     460          16 :     CPLString ret("\"");
     461          32 :     CPLString tmp(*this);
     462          16 :     tmp.replaceAll('"', "\"\"");
     463          16 :     ret += tmp;
     464          16 :     ret += '"';
     465          32 :     return ret;
     466             : }
     467             : 
     468             : /************************************************************************/
     469             : /*                           SQLQuotedLiteral()                         */
     470             : /************************************************************************/
     471             : 
     472             : /** Returns a string between single quotes and with all single quotes
     473             :  * inside the string are escaped by being doubled.
     474             :  *
     475             :  * Aimed at being used for SQL literal strings.
     476             :  *
     477             :  * @since 3.13
     478             :  */
     479           4 : CPLString CPLString::SQLQuotedLiteral() const
     480             : {
     481           4 :     CPLString ret("'");
     482           8 :     CPLString tmp(*this);
     483           4 :     tmp.replaceAll('\'', "''");
     484           4 :     ret += tmp;
     485           4 :     ret += '\'';
     486           8 :     return ret;
     487             : }
     488             : 
     489             : /************************************************************************/
     490             : /*                         CPLURLGetValue()                             */
     491             : /************************************************************************/
     492             : 
     493             : /**
     494             :  * Return the value matching a key from a key=value pair in a URL.
     495             :  *
     496             :  * @param pszURL the URL.
     497             :  * @param pszKey the key to find.
     498             :  * @return the value of empty string if not found.
     499             :  */
     500        1669 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
     501             : {
     502        3338 :     CPLString osKey(pszKey);
     503        1669 :     osKey += "=";
     504        1669 :     size_t nKeyPos = CPLString(pszURL).ifind(osKey);
     505        1669 :     if (nKeyPos != std::string::npos && nKeyPos > 0 &&
     506         331 :         (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
     507             :     {
     508         662 :         CPLString osValue(pszURL + nKeyPos + osKey.size());
     509         331 :         const char *pszValue = osValue.c_str();
     510         331 :         const char *pszSep = strchr(pszValue, '&');
     511         331 :         if (pszSep)
     512             :         {
     513          95 :             osValue.resize(pszSep - pszValue);
     514             :         }
     515         331 :         return osValue;
     516             :     }
     517        1338 :     return "";
     518             : }
     519             : 
     520             : /************************************************************************/
     521             : /*                          CPLURLAddKVP()                              */
     522             : /************************************************************************/
     523             : 
     524             : /**
     525             :  * Return a new URL with a new key=value pair.
     526             :  *
     527             :  * @param pszURL the URL.
     528             :  * @param pszKey the key to find.
     529             :  * @param pszValue the value of the key (may be NULL to unset an existing KVP).
     530             :  * @return the modified URL.
     531             :  */
     532        5301 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
     533             :                        const char *pszValue)
     534             : {
     535        5301 :     CPLString osURL(strchr(pszURL, '?') == nullptr
     536       15222 :                         ? CPLString(pszURL).append("?")
     537       10602 :                         : pszURL);
     538             : 
     539       10602 :     CPLString osKey(pszKey);
     540        5301 :     osKey += "=";
     541        5301 :     size_t nKeyPos = osURL.ifind(osKey);
     542        5380 :     if (nKeyPos != std::string::npos && nKeyPos > 0 &&
     543          79 :         (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
     544             :     {
     545         150 :         CPLString osNewURL(osURL);
     546          75 :         osNewURL.resize(nKeyPos);
     547          75 :         if (pszValue)
     548             :         {
     549          44 :             osNewURL += osKey;
     550          44 :             osNewURL += pszValue;
     551             :         }
     552          75 :         const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
     553          75 :         if (pszNext)
     554             :         {
     555          72 :             if (osNewURL.back() == '&' || osNewURL.back() == '?')
     556          30 :                 osNewURL += pszNext + 1;
     557             :             else
     558          42 :                 osNewURL += pszNext;
     559             :         }
     560          75 :         return osNewURL;
     561             :     }
     562             :     else
     563             :     {
     564       10452 :         CPLString osNewURL(std::move(osURL));
     565        5226 :         if (pszValue)
     566             :         {
     567        3403 :             if (osNewURL.back() != '&' && osNewURL.back() != '?')
     568        2489 :                 osNewURL += '&';
     569        3403 :             osNewURL += osKey;
     570        3403 :             osNewURL += pszValue;
     571             :         }
     572        5226 :         return osNewURL;
     573             :     }
     574             : }
     575             : 
     576             : /************************************************************************/
     577             : /*                            CPLOPrintf()                              */
     578             : /************************************************************************/
     579             : 
     580             : /** Return a CPLString with the content of sprintf() */
     581       17590 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
     582             : 
     583             : {
     584             :     va_list args;
     585       17590 :     va_start(args, pszFormat);
     586             : 
     587       17590 :     CPLString osTarget;
     588       17590 :     osTarget.vPrintf(pszFormat, args);
     589             : 
     590       17590 :     va_end(args);
     591             : 
     592       35180 :     return osTarget;
     593             : }
     594             : 
     595             : /************************************************************************/
     596             : /*                            CPLOvPrintf()                             */
     597             : /************************************************************************/
     598             : 
     599             : /** Return a CPLString with the content of vsprintf() */
     600           0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
     601             : 
     602             : {
     603           0 :     CPLString osTarget;
     604           0 :     osTarget.vPrintf(pszFormat, args);
     605           0 :     return osTarget;
     606             : }
     607             : 
     608             : /************************************************************************/
     609             : /*                            CPLQuotedSQLIdentifer()                   */
     610             : /************************************************************************/
     611             : 
     612             : /** Return a CPLString of the SQL quoted identifier */
     613           6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
     614             : 
     615             : {
     616           6 :     CPLString osIdent;
     617             : 
     618           6 :     if (pszIdent)
     619             :     {
     620           6 :         char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
     621           6 :         osIdent.Printf("\"%s\"", pszQuotedIdent);
     622           6 :         CPLFree(pszQuotedIdent);
     623             :     }
     624             : 
     625           6 :     return osIdent;
     626             : }

Generated by: LCOV version 1.14