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-10-22 13:51:22 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      504228 : CPLString &CPLString::Printf(CPL_FORMAT_STRING(const char *pszFormat), ...)
      42             : 
      43             : {
      44             :     va_list args;
      45             : 
      46      504228 :     va_start(args, pszFormat);
      47      504228 :     vPrintf(pszFormat, args);
      48      504195 :     va_end(args);
      49             : 
      50      504195 :     return *this;
      51             : }
      52             : 
      53             : /************************************************************************/
      54             : /*                              vPrintf()                               */
      55             : /************************************************************************/
      56             : 
      57             : /** Assign the content of the string using vsprintf() */
      58      621257 : 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      621257 :     va_copy(wrk_args, args);
      86             : #else
      87             :     wrk_args = args;
      88             : #endif
      89             : 
      90      621257 :     char szModestBuffer[500] = {};
      91      621257 :     szModestBuffer[0] = '\0';
      92      621257 :     int nPR = CPLvsnprintf(szModestBuffer, sizeof(szModestBuffer), pszFormat,
      93             :                            wrk_args);
      94      621202 :     if (nPR == -1 || nPR >= static_cast<int>(sizeof(szModestBuffer)) - 1)
      95             :     {
      96        1236 :         int nWorkBufferSize = 2000;
      97        1236 :         char *pszWorkBuffer = static_cast<char *>(CPLMalloc(nWorkBufferSize));
      98             : 
      99             : #ifdef va_copy
     100        1245 :         va_end(wrk_args);
     101        1245 :         va_copy(wrk_args, args);
     102             : #else
     103             :         wrk_args = args;
     104             : #endif
     105        1449 :         while ((nPR = CPLvsnprintf(pszWorkBuffer, nWorkBufferSize, pszFormat,
     106        1449 :                                    wrk_args)) >= nWorkBufferSize - 1 ||
     107             :                nPR == -1)
     108             :         {
     109         204 :             nWorkBufferSize *= 4;
     110             :             pszWorkBuffer =
     111         204 :                 static_cast<char *>(CPLRealloc(pszWorkBuffer, nWorkBufferSize));
     112             : #ifdef va_copy
     113         204 :             va_end(wrk_args);
     114         204 :             va_copy(wrk_args, args);
     115             : #else
     116             :             wrk_args = args;
     117             : #endif
     118             :         }
     119        1245 :         *this = pszWorkBuffer;
     120        1245 :         CPLFree(pszWorkBuffer);
     121             :     }
     122             :     else
     123             :     {
     124      619966 :         *this = szModestBuffer;
     125             :     }
     126             : #ifdef va_copy
     127      621228 :     va_end(wrk_args);
     128             : #endif
     129             : 
     130             : #endif /* !defined(HAVE_VSNPRINTF) */
     131             : 
     132      621228 :     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      810333 : CPLString &CPLString::Trim()
     184             : 
     185             : {
     186      810333 :     constexpr char szWhitespace[] = " \t\r\n";
     187             : 
     188      810333 :     const size_t iLeft = find_first_not_of(szWhitespace);
     189      810333 :     const size_t iRight = find_last_not_of(szWhitespace);
     190             : 
     191      810333 :     if (iLeft == std::string::npos)
     192             :     {
     193       86616 :         erase();
     194       86616 :         return *this;
     195             :     }
     196             : 
     197      723717 :     assign(substr(iLeft, iRight - iLeft + 1));
     198             : 
     199      723717 :     return *this;
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                               Recode()                               */
     204             : /************************************************************************/
     205             : 
     206             : /** Recode the string */
     207      435185 : CPLString &CPLString::Recode(const char *pszSrcEncoding,
     208             :                              const char *pszDstEncoding)
     209             : 
     210             : {
     211      435185 :     if (pszSrcEncoding == nullptr)
     212           0 :         pszSrcEncoding = CPL_ENC_UTF8;
     213      435185 :     if (pszDstEncoding == nullptr)
     214           0 :         pszDstEncoding = CPL_ENC_UTF8;
     215             : 
     216      435185 :     if (strcmp(pszSrcEncoding, pszDstEncoding) == 0)
     217          50 :         return *this;
     218             : 
     219      435135 :     char *pszRecoded = CPLRecode(c_str(), pszSrcEncoding, pszDstEncoding);
     220             : 
     221      435135 :     if (pszRecoded == nullptr)
     222           0 :         return *this;
     223             : 
     224      435135 :     assign(pszRecoded);
     225      435135 :     CPLFree(pszRecoded);
     226             : 
     227      435135 :     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      234574 : size_t CPLString::ifind(const char *s, size_t nPos) const
     259             : 
     260             : {
     261      234574 :     const char *pszHaystack = c_str();
     262             :     const char chFirst =
     263      234569 :         static_cast<char>(CPLTolower(static_cast<unsigned char>(s[0])));
     264      234566 :     const size_t nTargetLen = strlen(s);
     265             : 
     266      234566 :     if (nPos > size())
     267           0 :         nPos = size();
     268             : 
     269      234563 :     pszHaystack += nPos;
     270             : 
     271     8361890 :     while (*pszHaystack != '\0')
     272             :     {
     273     8136260 :         if (chFirst == CPLTolower(static_cast<unsigned char>(*pszHaystack)))
     274             :         {
     275      361232 :             if (EQUALN(pszHaystack, s, nTargetLen))
     276        8921 :                 return nPos;
     277             :         }
     278             : 
     279     8127330 :         nPos++;
     280     8127330 :         pszHaystack++;
     281             :     }
     282             : 
     283      225633 :     return std::string::npos;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                              toupper()                               */
     288             : /************************************************************************/
     289             : 
     290             : /**
     291             :  * Convert to upper case in place.
     292             :  */
     293             : 
     294     3492880 : CPLString &CPLString::toupper()
     295             : 
     296             : {
     297    29474800 :     for (size_t i = 0; i < size(); i++)
     298    25982000 :         (*this)[i] = static_cast<char>(CPLToupper((*this)[i]));
     299             : 
     300     3492880 :     return *this;
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                              tolower()                               */
     305             : /************************************************************************/
     306             : 
     307             : /**
     308             :  * Convert to lower case in place.
     309             :  */
     310             : 
     311      105540 : CPLString &CPLString::tolower()
     312             : 
     313             : {
     314     1719570 :     for (size_t i = 0; i < size(); i++)
     315     1614030 :         (*this)[i] = static_cast<char>(CPLTolower((*this)[i]));
     316             : 
     317      105540 :     return *this;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                             replaceAll()                             */
     322             : /************************************************************************/
     323             : 
     324             : /**
     325             :  * Replace all occurrences of osBefore with osAfter.
     326             :  */
     327     1168140 : CPLString &CPLString::replaceAll(const std::string &osBefore,
     328             :                                  const std::string &osAfter)
     329             : {
     330     1168140 :     const size_t nBeforeSize = osBefore.size();
     331     1168130 :     const size_t nAfterSize = osAfter.size();
     332     1168130 :     if (nBeforeSize)
     333             :     {
     334     1168130 :         size_t nStartPos = 0;
     335     1262500 :         while ((nStartPos = find(osBefore, nStartPos)) != std::string::npos)
     336             :         {
     337       94374 :             replace(nStartPos, nBeforeSize, osAfter);
     338       94372 :             nStartPos += nAfterSize;
     339             :         }
     340             :     }
     341     1168120 :     return *this;
     342             : }
     343             : 
     344             : /**
     345             :  * Replace all occurrences of chBefore with osAfter.
     346             :  */
     347        4379 : CPLString &CPLString::replaceAll(char chBefore, const std::string &osAfter)
     348             : {
     349        4379 :     return replaceAll(std::string(&chBefore, 1), osAfter);
     350             : }
     351             : 
     352             : /**
     353             :  * Replace all occurrences of osBefore with chAfter.
     354             :  */
     355     1114470 : CPLString &CPLString::replaceAll(const std::string &osBefore, char chAfter)
     356             : {
     357     1114470 :     return replaceAll(osBefore, std::string(&chAfter, 1));
     358             : }
     359             : 
     360             : /**
     361             :  * Replace all occurrences of chBefore with chAfter.
     362             :  */
     363        7027 : CPLString &CPLString::replaceAll(char chBefore, char chAfter)
     364             : {
     365        7027 :     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       20563 : bool CPLString::endsWith(const std::string &osStr) const
     378             : {
     379       20563 :     if (size() < osStr.size())
     380         150 :         return false;
     381       20413 :     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             : /*                         CPLURLGetValue()                             */
     449             : /************************************************************************/
     450             : 
     451             : /**
     452             :  * Return the value matching a key from a key=value pair in a URL.
     453             :  *
     454             :  * @param pszURL the URL.
     455             :  * @param pszKey the key to find.
     456             :  * @return the value of empty string if not found.
     457             :  */
     458        1669 : CPLString CPLURLGetValue(const char *pszURL, const char *pszKey)
     459             : {
     460        3338 :     CPLString osKey(pszKey);
     461        1669 :     osKey += "=";
     462        1669 :     size_t nKeyPos = CPLString(pszURL).ifind(osKey);
     463        1669 :     if (nKeyPos != std::string::npos && nKeyPos > 0 &&
     464         331 :         (pszURL[nKeyPos - 1] == '?' || pszURL[nKeyPos - 1] == '&'))
     465             :     {
     466         662 :         CPLString osValue(pszURL + nKeyPos + osKey.size());
     467         331 :         const char *pszValue = osValue.c_str();
     468         331 :         const char *pszSep = strchr(pszValue, '&');
     469         331 :         if (pszSep)
     470             :         {
     471          95 :             osValue.resize(pszSep - pszValue);
     472             :         }
     473         331 :         return osValue;
     474             :     }
     475        1338 :     return "";
     476             : }
     477             : 
     478             : /************************************************************************/
     479             : /*                          CPLURLAddKVP()                              */
     480             : /************************************************************************/
     481             : 
     482             : /**
     483             :  * Return a new URL with a new key=value pair.
     484             :  *
     485             :  * @param pszURL the URL.
     486             :  * @param pszKey the key to find.
     487             :  * @param pszValue the value of the key (may be NULL to unset an existing KVP).
     488             :  * @return the modified URL.
     489             :  */
     490        5301 : CPLString CPLURLAddKVP(const char *pszURL, const char *pszKey,
     491             :                        const char *pszValue)
     492             : {
     493        5301 :     CPLString osURL(strchr(pszURL, '?') == nullptr
     494       15222 :                         ? CPLString(pszURL).append("?")
     495       10602 :                         : pszURL);
     496             : 
     497       10602 :     CPLString osKey(pszKey);
     498        5301 :     osKey += "=";
     499        5301 :     size_t nKeyPos = osURL.ifind(osKey);
     500        5380 :     if (nKeyPos != std::string::npos && nKeyPos > 0 &&
     501          79 :         (osURL[nKeyPos - 1] == '?' || osURL[nKeyPos - 1] == '&'))
     502             :     {
     503         150 :         CPLString osNewURL(osURL);
     504          75 :         osNewURL.resize(nKeyPos);
     505          75 :         if (pszValue)
     506             :         {
     507          44 :             osNewURL += osKey;
     508          44 :             osNewURL += pszValue;
     509             :         }
     510          75 :         const char *pszNext = strchr(osURL.c_str() + nKeyPos, '&');
     511          75 :         if (pszNext)
     512             :         {
     513          72 :             if (osNewURL.back() == '&' || osNewURL.back() == '?')
     514          30 :                 osNewURL += pszNext + 1;
     515             :             else
     516          42 :                 osNewURL += pszNext;
     517             :         }
     518          75 :         return osNewURL;
     519             :     }
     520             :     else
     521             :     {
     522       10452 :         CPLString osNewURL(std::move(osURL));
     523        5226 :         if (pszValue)
     524             :         {
     525        3403 :             if (osNewURL.back() != '&' && osNewURL.back() != '?')
     526        2489 :                 osNewURL += '&';
     527        3403 :             osNewURL += osKey;
     528        3403 :             osNewURL += pszValue;
     529             :         }
     530        5226 :         return osNewURL;
     531             :     }
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                            CPLOPrintf()                              */
     536             : /************************************************************************/
     537             : 
     538             : /** Return a CPLString with the content of sprintf() */
     539       17582 : CPLString CPLOPrintf(CPL_FORMAT_STRING(const char *pszFormat), ...)
     540             : 
     541             : {
     542             :     va_list args;
     543       17582 :     va_start(args, pszFormat);
     544             : 
     545       17582 :     CPLString osTarget;
     546       17582 :     osTarget.vPrintf(pszFormat, args);
     547             : 
     548       17582 :     va_end(args);
     549             : 
     550       35164 :     return osTarget;
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                            CPLOvPrintf()                             */
     555             : /************************************************************************/
     556             : 
     557             : /** Return a CPLString with the content of vsprintf() */
     558           0 : CPLString CPLOvPrintf(CPL_FORMAT_STRING(const char *pszFormat), va_list args)
     559             : 
     560             : {
     561           0 :     CPLString osTarget;
     562           0 :     osTarget.vPrintf(pszFormat, args);
     563           0 :     return osTarget;
     564             : }
     565             : 
     566             : /************************************************************************/
     567             : /*                            CPLQuotedSQLIdentifer()                   */
     568             : /************************************************************************/
     569             : 
     570             : /** Return a CPLString of the SQL quoted identifier */
     571           6 : CPLString CPLQuotedSQLIdentifier(const char *pszIdent)
     572             : 
     573             : {
     574           6 :     CPLString osIdent;
     575             : 
     576           6 :     if (pszIdent)
     577             :     {
     578           6 :         char *pszQuotedIdent = CPLEscapeString(pszIdent, -1, CPLES_SQLI);
     579           6 :         osIdent.Printf("\"%s\"", pszQuotedIdent);
     580           6 :         CPLFree(pszQuotedIdent);
     581             :     }
     582             : 
     583           6 :     return osIdent;
     584             : }

Generated by: LCOV version 1.14