LCOV - code coverage report
Current view: top level - port - cplstring.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 170 178 95.5 %
Date: 2025-09-10 17:48:50 Functions: 20 21 95.2 %

          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      508258 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
      42             : 
      43             : {
      44             :     va_list args;
      45             : 
      46      508258 :     va_start(args, pszFormat);
      47      508258 :     vPrintf(pszFormat, args);
      48      508164 :     va_end(args);
      49             : 
      50      508164 :     return *this;
      51             : }
      52             : 
      53             : /************************************************************************/
      54             : /*                              vPrintf()                               */
      55             : /************************************************************************/
      56             : 
      57             : /** Assign the content of the string using vsprintf() */
      58      624549 : 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      624549 :     va_copy(wrk_args, args);
      86             : #else
      87             :     wrk_args = args;
      88             : #endif
      89             : 
      90      624549 :     char szModestBuffer[500] = {};
      91      624549 :     szModestBuffer[0] = '\0';
      92      624549 :     int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
      93             :                            wrk_args);
      94      624488 :     if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
      95             :     {
      96        1251 :         int nWorkBufferSize = 2000;
      97        1251 :         char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
      98             : 
      99             : #ifdef va_copy
     100        1215 :         va_end(wrk_args);
     101        1215 :         va_copy(wrk_args, args);
     102             : #else
     103             :         wrk_args = args;
     104             : #endif
     105        1404 :         while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
     106        1404 :                                    wrk_args)) >= nWorkBufferSize - 1 ||
     107             :                nPR == -1)
     108             :         {
     109         189 :             nWorkBufferSize *= 4;
     110             :             pszWorkBuffer =
     111         189 :                 static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
     112             : #ifdef va_copy
     113         189 :             va_end(wrk_args);
     114         189 :             va_copy(wrk_args, args);
     115             : #else
     116             :             wrk_args = args;
     117             : #endif
     118             :         }
     119        1215 :         *this = pszWorkBuffer;
     120        1215 :         CPLFree(pszWorkBuffer);
     121             :     }
     122             :     else
     123             :     {
     124      623237 :         *this = szModestBuffer;
     125             :     }
     126             : #ifdef va_copy
     127      624477 :     va_end(wrk_args);
     128             : #endif
     129             : 
     130             : #endif /* !defined(HAVE_VSNPRINTF) */
     131             : 
     132      624477 :     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      818144 : CPLString &CPLString::Trim()
     184             : 
     185             : {
     186      818144 :     constexpr char szWhitespace[] = " \t\r\n";
     187             : 
     188      818144 :     const size_t iLeft = find_first_not_of(szWhitespace);
     189      818144 :     const size_t iRight = find_last_not_of(szWhitespace);
     190             : 
     191      818144 :     if (iLeft == std::string::npos)
     192             :     {
     193       87722 :         erase();
     194       87722 :         return *this;
     195             :     }
     196             : 
     197      730422 :     assign(substr(iLeft, iRight - iLeft + 1));
     198             : 
     199      730422 :     return *this;
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                               Recode()                               */
     204             : /************************************************************************/
     205             : 
     206             : /** Recode the string */
     207      441680 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
     208             :                              const char *pszDstEncoding)
     209             : 
     210             : {
     211      441680 :     if (pszSrcEncoding == nullptr)
     212           0 :         pszSrcEncoding = CPL_ENC_UTF8;
     213      441680 :     if (pszDstEncoding == nullptr)
     214           0 :         pszDstEncoding = CPL_ENC_UTF8;
     215             : 
     216      441680 :     if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
     217          40 :         return *this;
     218             : 
     219      441640 :     char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
     220             : 
     221      441640 :     if (pszRecoded == nullptr)
     222           0 :         return *this;
     223             : 
     224      441640 :     assign(pszRecoded);
     225      441640 :     CPLFree(pszRecoded);
     226             : 
     227      441640 :     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             :  * @since GDAL 1.9.0
     242             :  */
     243             : 
     244        7313 : size_t CPLString::ifind(const std::string &str, size_t pos) const
     245             : 
     246             : {
     247        7313 :     return ifind(str.c_str(), pos);
     248             : }
     249             : 
     250             : /**
     251             :  * Case insensitive find() alternative.
     252             :  *
     253             :  * @param s substring to find.
     254             :  * @param nPos offset in the string at which the search starts.
     255             :  * @return the position of the substring in the string or std::string::npos if
     256             :  * not found.
     257             :  * @since GDAL 1.9.0
     258             :  */
     259             : 
     260      231738 : size_t CPLString::ifind(const char *s, size_t nPos) const
     261             : 
     262             : {
     263      231738 :     const char *pszHaystack = c_str();
     264             :     const char chFirst =
     265      231726 :         static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
     266      231723 :     const size_t nTargetLen = strlen(s);
     267             : 
     268      231723 :     if (nPos > size())
     269           0 :         nPos = size();
     270             : 
     271      231720 :     pszHaystack += nPos;
     272             : 
     273     8285390 :     while (*pszHaystack != '\0')
     274             :     {
     275     8062560 :         if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
     276             :         {
     277      357555 :             if (EQUALN(pszHaystack, s, nTargetLen))
     278        8891 :                 return nPos;
     279             :         }
     280             : 
     281     8053670 :         nPos++;
     282     8053670 :         pszHaystack++;
     283             :     }
     284             : 
     285      222828 :     return std::string::npos;
     286             : }
     287             : 
     288             : /************************************************************************/
     289             : /*                              toupper()                               */
     290             : /************************************************************************/
     291             : 
     292             : /**
     293             :  * Convert to upper case in place.
     294             :  */
     295             : 
     296     3461380 : CPLString &CPLString::toupper()
     297             : 
     298             : {
     299    29273900 :     for (size_t i = 0; i < size(); i++)
     300    25812500 :         (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
     301             : 
     302     3461380 :     return *this;
     303             : }
     304             : 
     305             : /************************************************************************/
     306             : /*                              tolower()                               */
     307             : /************************************************************************/
     308             : 
     309             : /**
     310             :  * Convert to lower case in place.
     311             :  */
     312             : 
     313      104764 : CPLString &CPLString::tolower()
     314             : 
     315             : {
     316     1711940 :     for (size_t i = 0; i < size(); i++)
     317     1607170 :         (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
     318             : 
     319      104765 :     return *this;
     320             : }
     321             : 
     322             : /************************************************************************/
     323             : /*                             replaceAll()                             */
     324             : /************************************************************************/
     325             : 
     326             : /**
     327             :  * Replace all occurrences of osBefore with osAfter.
     328             :  */
     329     1164650 : CPLString &CPLString::replaceAll(const std::string &osBefore,
     330             :                                  const std::string &osAfter)
     331             : {
     332     1164650 :     const size_t nBeforeSize = osBefore.size();
     333     1164630 :     const size_t nAfterSize = osAfter.size();
     334     1164620 :     if (nBeforeSize)
     335             :     {
     336     1164610 :         size_t nStartPos = 0;
     337     1258510 :         while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
     338             :         {
     339       93913 :             replace(nStartPos, nBeforeSize, osAfter);
     340       93900 :             nStartPos += nAfterSize;
     341             :         }
     342             :     }
     343     1164640 :     return *this;
     344             : }
     345             : 
     346             : /**
     347             :  * Replace all occurrences of chBefore with osAfter.
     348             :  */
     349        4352 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
     350             : {
     351        4352 :     return replaceAll(std::string(&chBefore, 1), osAfter);
     352             : }
     353             : 
     354             : /**
     355             :  * Replace all occurrences of osBefore with chAfter.
     356             :  */
     357     1111020 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
     358             : {
     359     1111020 :     return replaceAll(osBefore, std::string(&chAfter, 1));
     360             : }
     361             : 
     362             : /**
     363             :  * Replace all occurrences of chBefore with chAfter.
     364             :  */
     365        7025 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
     366             : {
     367        7025 :     return replaceAll(std::string(&chBefore, 1), std::string(&chAfter, 1));
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                             endsWith()                              */
     372             : /************************************************************************/
     373             : 
     374             : /**
     375             :  * Returns whether the string ends with another string
     376             :  * @param osStr other string.
     377             :  * @return true if the string ends with osStr.
     378             :  */
     379       20567 : bool CPLString::endsWith(const std::string &osStr) const
     380             : {
     381       20567 :     if (size() < osStr.size())
     382         150 :         return false;
     383       20417 :     return substr(size() - osStr.size()) == osStr;
     384             : }
     385             : 
     386             : /************************************************************************/
     387             : /*                             URLEncode()                              */
     388             : /************************************************************************/
     389             : 
     390             : /**
     391             :  * Return a string that *can* be a valid URL.
     392             :  *
     393             :  * Said otherwise if URLEncode() != *this was not a valid URL according to
     394             :  * https://datatracker.ietf.org/doc/html/rfc3986.html.
     395             :  *
     396             :  * This replaces all characters that are not reserved (:/?#[]\@!$&'()*+,;=),
     397             :  * unreserved (a-z, A-Z, 0-9 and -.-~) or already percent-encoded by their
     398             :  * percent-encoding.
     399             :  *
     400             :  * Note that when composing a URL, and typically query-parameters, it might
     401             :  * be needed to use CPLEscape(,,CPLES_URL) to also substitute reserved
     402             :  * characters.
     403             :  *
     404             :  * @return a URL encoded string
     405             :  * @since 3.12
     406             :  */
     407          55 : CPLString CPLString::URLEncode() const
     408             : {
     409             :     // Helper to check if a substring is a valid percent-encoding
     410          21 :     auto isPercentEncoded = [](const char *str) -> bool
     411             :     {
     412          42 :         return str[0] == '%' &&
     413          42 :                std::isxdigit(static_cast<unsigned char>(str[1])) &&
     414          42 :                std::isxdigit(static_cast<unsigned char>(str[2]));
     415             :     };
     416             : 
     417             :     // Cf https://datatracker.ietf.org/doc/html/rfc3986.html
     418          55 :     const char *unreserved =
     419             :         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
     420          55 :     const char *reserved = ":/?#[]@!$&'()*+,;=";
     421          55 :     CPLString osEncoded;
     422          55 :     osEncoded.reserve(size());
     423        4498 :     for (size_t i = 0; i < size(); ++i)
     424             :     {
     425        4443 :         char ch = (*this)[i];
     426             :         // If already percent-encoded, copy as is
     427        4443 :         if (ch == '%' && i + 2 < size() && isPercentEncoded(c_str() + i))
     428             :         {
     429          21 :             osEncoded += ch;
     430          21 :             osEncoded += (*this)[i + 1];
     431          21 :             osEncoded += (*this)[i + 2];
     432          21 :             i += 2;
     433             :         }
     434        4422 :         else if (strchr(unreserved, ch) || strchr(reserved, ch))
     435             :         {
     436        4409 :             osEncoded += ch;
     437             :         }
     438             :         else
     439             :         {
     440             :             char buf[4];
     441          13 :             snprintf(buf, sizeof(buf), "%%%02X",
     442          13 :                      static_cast<unsigned char>(ch));
     443          13 :             osEncoded += buf;
     444             :         }
     445             :     }
     446         110 :     return osEncoded;
     447             : }
     448             : 
     449             : /************************************************************************/
     450             : /*                         CPLURLGetValue()                             */
     451             : /************************************************************************/
     452             : 
     453             : /**
     454             :  * Return the value matching a key from a key=value pair in a URL.
     455             :  *
     456             :  * @param pszURL the URL.
     457             :  * @param pszKey the key to find.
     458             :  * @return the value of empty string if not found.
     459             :  * @since GDAL 1.9.0
     460             :  */
     461        1656 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
     462             : {
     463        3312 :     CPLString osKey(pszKey);
     464        1656 :     osKey += "=";
     465        1656 :     size_t nKeyPos = CPLString(pszURL).ifind(osKey);
     466        1656 :     if (nKeyPos != std::string::npos && nKeyPos > 0 &&
     467         329 :         (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
     468             :     {
     469         658 :         CPLString osValue(pszURL + nKeyPos + osKey.size());
     470         329 :         const char *pszValue = osValue.c_str();
     471         329 :         const char *pszSep = strchr(pszValue, '&');
     472         329 :         if (pszSep)
     473             :         {
     474          95 :             osValue.resize(pszSep - pszValue);
     475             :         }
     476         329 :         return osValue;
     477             :     }
     478        1327 :     return "";
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                          CPLURLAddKVP()                              */
     483             : /************************************************************************/
     484             : 
     485             : /**
     486             :  * Return a new URL with a new key=value pair.
     487             :  *
     488             :  * @param pszURL the URL.
     489             :  * @param pszKey the key to find.
     490             :  * @param pszValue the value of the key (may be NULL to unset an existing KVP).
     491             :  * @return the modified URL.
     492             :  * @since GDAL 1.9.0
     493             :  */
     494        5274 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
     495             :                        const char *pszValue)
     496             : {
     497        5274 :     CPLString osURL(strchr(pszURL, '?') == nullptr
     498       15145 :                         ? CPLString(pszURL).append("?")
     499       10548 :                         : pszURL);
     500             : 
     501       10548 :     CPLString osKey(pszKey);
     502        5274 :     osKey += "=";
     503        5274 :     size_t nKeyPos = osURL.ifind(osKey);
     504        5353 :     if (nKeyPos != std::string::npos && nKeyPos > 0 &&
     505          79 :         (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
     506             :     {
     507         150 :         CPLString osNewURL(osURL);
     508          75 :         osNewURL.resize(nKeyPos);
     509          75 :         if (pszValue)
     510             :         {
     511          44 :             osNewURL += osKey;
     512          44 :             osNewURL += pszValue;
     513             :         }
     514          75 :         const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
     515          75 :         if (pszNext)
     516             :         {
     517          72 :             if (osNewURL.back() == '&' || osNewURL.back() == '?')
     518          30 :                 osNewURL += pszNext + 1;
     519             :             else
     520          42 :                 osNewURL += pszNext;
     521             :         }
     522          75 :         return osNewURL;
     523             :     }
     524             :     else
     525             :     {
     526       10398 :         CPLString osNewURL(std::move(osURL));
     527        5199 :         if (pszValue)
     528             :         {
     529        3387 :             if (osNewURL.back() != '&' && osNewURL.back() != '?')
     530        2477 :                 osNewURL += '&';
     531        3387 :             osNewURL += osKey;
     532        3387 :             osNewURL += pszValue;
     533             :         }
     534        5199 :         return osNewURL;
     535             :     }
     536             : }
     537             : 
     538             : /************************************************************************/
     539             : /*                            CPLOPrintf()                              */
     540             : /************************************************************************/
     541             : 
     542             : /** Return a CPLString with the content of sprintf() */
     543       17511 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
     544             : 
     545             : {
     546             :     va_list args;
     547       17511 :     va_start(args, pszFormat);
     548             : 
     549       17511 :     CPLString osTarget;
     550       17511 :     osTarget.vPrintf(pszFormat, args);
     551             : 
     552       17511 :     va_end(args);
     553             : 
     554       35022 :     return osTarget;
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                            CPLOvPrintf()                             */
     559             : /************************************************************************/
     560             : 
     561             : /** Return a CPLString with the content of vsprintf() */
     562           0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
     563             : 
     564             : {
     565           0 :     CPLString osTarget;
     566           0 :     osTarget.vPrintf(pszFormat, args);
     567           0 :     return osTarget;
     568             : }
     569             : 
     570             : /************************************************************************/
     571             : /*                            CPLQuotedSQLIdentifer()                   */
     572             : /************************************************************************/
     573             : 
     574             : /** Return a CPLString of the SQL quoted identifier */
     575           6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
     576             : 
     577             : {
     578           6 :     CPLString osIdent;
     579             : 
     580           6 :     if (pszIdent)
     581             :     {
     582           6 :         char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
     583           6 :         osIdent.Printf("\"%s\"", pszQuotedIdent);
     584           6 :         CPLFree(pszQuotedIdent);
     585             :     }
     586             : 
     587           6 :     return osIdent;
     588             : }

Generated by: LCOV version 1.14