LCOV - code coverage report
Current view: top level - port - cpl_string.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1058 1168 90.6 %
Date: 2026-08-22 15:37:05 Functions: 71 74 95.9 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     cpl_string.cpp
       4             :  * Project:  CPL - Common Portability Library
       5             :  * Purpose:  String and Stringlist manipulation functions.
       6             :  * Author:   Daniel Morissette, danmo@videotron.ca
       7             :  *
       8             :  **********************************************************************
       9             :  * Copyright (c) 1998, Daniel Morissette
      10             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  **********************************************************************
      14             :  *
      15             :  * Independent Security Audit 2003/04/04 Andrey Kiselev:
      16             :  *   Completed audit of this module. All functions may be used without buffer
      17             :  *   overflows and stack corruptions with any kind of input data strings with
      18             :  *   except of CPLSPrintf() and CSLAppendPrintf() (see note below).
      19             :  *
      20             :  * Security Audit 2003/03/28 warmerda:
      21             :  *   Completed security audit.  I believe that this module may be safely used
      22             :  *   to parse tokenize arbitrary input strings, assemble arbitrary sets of
      23             :  *   names values into string lists, unescape and escape text even if provided
      24             :  *   by a potentially hostile source.
      25             :  *
      26             :  *   CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on
      27             :  *   arbitrary length inputs since it has a fixed size output buffer on system
      28             :  *   without vsnprintf().
      29             :  *
      30             :  **********************************************************************/
      31             : 
      32             : #undef WARN_STANDARD_PRINTF
      33             : 
      34             : #include "cpl_port.h"
      35             : #include "cpl_string.h"
      36             : 
      37             : #include <algorithm>
      38             : #include <cctype>
      39             : #include <climits>
      40             : #include <cmath>
      41             : #include <cstdlib>
      42             : #include <cstring>
      43             : 
      44             : #include <limits>
      45             : 
      46             : #include "cpl_config.h"
      47             : #include "cpl_multiproc.h"
      48             : #include "cpl_vsi.h"
      49             : 
      50             : #if !defined(va_copy) && defined(__va_copy)
      51             : #define va_copy __va_copy
      52             : #endif
      53             : 
      54             : /*=====================================================================
      55             :                     StringList manipulation functions.
      56             :  =====================================================================*/
      57             : 
      58             : /**********************************************************************
      59             :  *                       CSLAddString()
      60             :  **********************************************************************/
      61             : 
      62             : /** Append a string to a StringList and return a pointer to the modified
      63             :  * StringList.
      64             :  *
      65             :  * If the input StringList is NULL, then a new StringList is created.
      66             :  * Note that CSLAddString performance when building a list is in O(n^2)
      67             :  * which can cause noticeable slow down when n > 10000.
      68             :  */
      69      404274 : char **CSLAddString(char **papszStrList, const char *pszNewString)
      70             : {
      71      404274 :     char **papszRet = CSLAddStringMayFail(papszStrList, pszNewString);
      72      404256 :     if (papszRet == nullptr && pszNewString != nullptr)
      73           0 :         abort();
      74      404256 :     return papszRet;
      75             : }
      76             : 
      77             : /** Same as CSLAddString() but may return NULL in case of (memory) failure */
      78      466448 : char **CSLAddStringMayFail(char **papszStrList, const char *pszNewString)
      79             : {
      80      466448 :     if (pszNewString == nullptr)
      81         133 :         return papszStrList;  // Nothing to do!
      82             : 
      83      466315 :     char *pszDup = VSI_STRDUP_VERBOSE(pszNewString);
      84      466304 :     if (pszDup == nullptr)
      85           0 :         return nullptr;
      86             : 
      87             :     // Allocate room for the new string.
      88      466304 :     char **papszStrListNew = nullptr;
      89      466304 :     int nItems = 0;
      90             : 
      91      466304 :     if (papszStrList == nullptr)
      92             :         papszStrListNew =
      93       82042 :             static_cast<char **>(VSI_CALLOC_VERBOSE(2, sizeof(char *)));
      94             :     else
      95             :     {
      96      384262 :         nItems = CSLCount(papszStrList);
      97             :         papszStrListNew = static_cast<char **>(
      98      384265 :             VSI_REALLOC_VERBOSE(papszStrList, (nItems + 2) * sizeof(char *)));
      99             :     }
     100      466299 :     if (papszStrListNew == nullptr)
     101             :     {
     102           0 :         VSIFree(pszDup);
     103           0 :         return nullptr;
     104             :     }
     105             : 
     106             :     // Copy the string in the list.
     107      466299 :     papszStrListNew[nItems] = pszDup;
     108      466299 :     papszStrListNew[nItems + 1] = nullptr;
     109             : 
     110      466299 :     return papszStrListNew;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                              CSLCount()                              */
     115             : /************************************************************************/
     116             : 
     117             : /**
     118             :  * Return number of items in a string list.
     119             :  *
     120             :  * Returns the number of items in a string list, not counting the
     121             :  * terminating NULL.  Passing in NULL is safe, and will result in a count
     122             :  * of zero.
     123             :  *
     124             :  * Lists are counted by iterating through them so long lists will
     125             :  * take more time than short lists.  Care should be taken to avoid using
     126             :  * CSLCount() as an end condition for loops as it will result in O(n^2)
     127             :  * behavior.
     128             :  *
     129             :  * @param papszStrList the string list to count.
     130             :  *
     131             :  * @return the number of entries.
     132             :  */
     133     5212450 : int CSLCount(CSLConstList papszStrList)
     134             : {
     135     5212450 :     if (!papszStrList)
     136     3619000 :         return 0;
     137             : 
     138     1593450 :     int nItems = 0;
     139             : 
     140    12127500 :     while (*papszStrList != nullptr)
     141             :     {
     142    10534000 :         ++nItems;
     143    10534000 :         ++papszStrList;
     144             :     }
     145             : 
     146     1593450 :     return nItems;
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                            CSLGetField()                             */
     151             : /************************************************************************/
     152             : 
     153             : /**
     154             :  * Fetches the indicated field, being careful not to crash if the field
     155             :  * doesn't exist within this string list.
     156             :  *
     157             :  * The returned pointer should not be freed, and doesn't necessarily last long.
     158             :  */
     159        1320 : const char *CSLGetField(CSLConstList papszStrList, int iField)
     160             : 
     161             : {
     162        1320 :     if (papszStrList == nullptr || iField < 0)
     163           0 :         return ("");
     164             : 
     165        2871 :     for (int i = 0; i < iField + 1; i++)
     166             :     {
     167        1552 :         if (papszStrList[i] == nullptr)
     168           1 :             return "";
     169             :     }
     170             : 
     171        1319 :     return (papszStrList[iField]);
     172             : }
     173             : 
     174             : /************************************************************************/
     175             : /*                             CSLDestroy()                             */
     176             : /************************************************************************/
     177             : 
     178             : /**
     179             :  * Free string list.
     180             :  *
     181             :  * Frees the passed string list (null terminated array of strings).
     182             :  * It is safe to pass NULL.
     183             :  *
     184             :  * @param papszStrList the list to free.
     185             :  */
     186    15655900 : void CPL_STDCALL CSLDestroy(char **papszStrList)
     187             : {
     188    15655900 :     if (!papszStrList)
     189    12100400 :         return;
     190             : 
     191    16909300 :     for (char **papszPtr = papszStrList; *papszPtr != nullptr; ++papszPtr)
     192             :     {
     193    13351600 :         CPLFree(*papszPtr);
     194             :     }
     195             : 
     196     3557660 :     CPLFree(papszStrList);
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                            CSLDuplicate()                            */
     201             : /************************************************************************/
     202             : 
     203             : /**
     204             :  * Clone a string list.
     205             :  *
     206             :  * Efficiently allocates a copy of a string list.  The returned list is
     207             :  * owned by the caller and should be freed with CSLDestroy().
     208             :  *
     209             :  * @param papszStrList the input string list.
     210             :  *
     211             :  * @return newly allocated copy.
     212             :  */
     213             : 
     214     3659500 : char **CSLDuplicate(CSLConstList papszStrList)
     215             : {
     216     3659500 :     const int nLines = CSLCount(papszStrList);
     217             : 
     218     3644560 :     if (nLines == 0)
     219     3568940 :         return nullptr;
     220             : 
     221       75620 :     CSLConstList papszSrc = papszStrList;
     222             : 
     223             :     char **papszNewList =
     224       75620 :         static_cast<char **>(VSI_MALLOC2_VERBOSE(nLines + 1, sizeof(char *)));
     225             : 
     226       83239 :     char **papszDst = papszNewList;
     227             : 
     228      535529 :     for (; *papszSrc != nullptr; ++papszSrc, ++papszDst)
     229             :     {
     230      452288 :         *papszDst = VSI_STRDUP_VERBOSE(*papszSrc);
     231      452288 :         if (*papszDst == nullptr)
     232             :         {
     233           0 :             CSLDestroy(papszNewList);
     234           0 :             return nullptr;
     235             :         }
     236             :     }
     237       83241 :     *papszDst = nullptr;
     238             : 
     239       83241 :     return papszNewList;
     240             : }
     241             : 
     242             : /************************************************************************/
     243             : /*                               CSLMerge                               */
     244             : /************************************************************************/
     245             : 
     246             : /**
     247             :  * \brief Merge two lists.
     248             :  *
     249             :  * The two lists are merged, ensuring that if any keys appear in both
     250             :  * that the value from the second (papszOverride) list take precedence.
     251             :  *
     252             :  * @param papszOrig the original list, being modified.
     253             :  * @param papszOverride the list of items being merged in.  This list
     254             :  * is unaltered and remains owned by the caller.
     255             :  *
     256             :  * @return updated list.
     257             :  */
     258             : 
     259      750598 : char **CSLMerge(char **papszOrig, CSLConstList papszOverride)
     260             : 
     261             : {
     262      750598 :     if (papszOrig == nullptr && papszOverride != nullptr)
     263         688 :         return CSLDuplicate(papszOverride);
     264             : 
     265      749910 :     if (papszOverride == nullptr)
     266      747694 :         return papszOrig;
     267             : 
     268        6320 :     for (int i = 0; papszOverride[i] != nullptr; ++i)
     269             :     {
     270        4422 :         char *pszKey = nullptr;
     271        4422 :         const char *pszValue = CPLParseNameValue(papszOverride[i], &pszKey);
     272             : 
     273        4422 :         papszOrig = CSLSetNameValue(papszOrig, pszKey, pszValue);
     274        4422 :         CPLFree(pszKey);
     275             :     }
     276             : 
     277        1898 :     return papszOrig;
     278             : }
     279             : 
     280             : /************************************************************************/
     281             : /*                              CSLLoad2()                              */
     282             : /************************************************************************/
     283             : 
     284             : /**
     285             :  * Load a text file into a string list.
     286             :  *
     287             :  * The VSI*L API is used, so VSIFOpenL() supported objects that aren't
     288             :  * physical files can also be accessed.  Files are returned as a string list,
     289             :  * with one item in the string list per line.  End of line markers are
     290             :  * stripped (by CPLReadLineL()).
     291             :  *
     292             :  * If reading the file fails a CPLError() will be issued and NULL returned.
     293             :  *
     294             :  * @param pszFname the name of the file to read.
     295             :  * @param nMaxLines maximum number of lines to read before stopping, or -1 for
     296             :  * no limit.
     297             :  * @param nMaxCols maximum number of characters in a line before stopping, or -1
     298             :  * for no limit.
     299             :  * @param papszOptions NULL-terminated array of options. Unused for now.
     300             :  *
     301             :  * @return a string list with the files lines, now owned by caller. To be freed
     302             :  * with CSLDestroy()
     303             :  *
     304             :  */
     305             : 
     306        3734 : char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols,
     307             :                 CSLConstList papszOptions)
     308             : {
     309        3734 :     VSILFILE *fp = VSIFOpenL(pszFname, "rb");
     310             : 
     311        3734 :     if (!fp)
     312             :     {
     313        2346 :         if (CPLFetchBool(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", true))
     314             :         {
     315             :             // Unable to open file.
     316           1 :             CPLError(CE_Failure, CPLE_OpenFailed,
     317             :                      "CSLLoad2(\"%s\") failed: unable to open file.", pszFname);
     318             :         }
     319        2346 :         return nullptr;
     320             :     }
     321             : 
     322        1388 :     char **papszStrList = nullptr;
     323        1388 :     int nLines = 0;
     324        1388 :     int nAllocatedLines = 0;
     325             : 
     326        9445 :     while (!VSIFEofL(fp) && (nMaxLines == -1 || nLines < nMaxLines))
     327             :     {
     328        8063 :         const char *pszLine = CPLReadLine2L(fp, nMaxCols, papszOptions);
     329        8063 :         if (pszLine == nullptr)
     330           6 :             break;
     331             : 
     332        8057 :         if (nLines + 1 >= nAllocatedLines)
     333             :         {
     334        1514 :             nAllocatedLines = 16 + nAllocatedLines * 2;
     335             :             char **papszStrListNew = static_cast<char **>(
     336        1514 :                 VSIRealloc(papszStrList, nAllocatedLines * sizeof(char *)));
     337        1514 :             if (papszStrListNew == nullptr)
     338             :             {
     339           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     340           0 :                 CPLReadLineL(nullptr);
     341           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
     342             :                          "CSLLoad2(\"%s\") "
     343             :                          "failed: not enough memory to allocate lines.",
     344             :                          pszFname);
     345           0 :                 return papszStrList;
     346             :             }
     347        1514 :             papszStrList = papszStrListNew;
     348             :         }
     349        8057 :         papszStrList[nLines] = CPLStrdup(pszLine);
     350        8057 :         papszStrList[nLines + 1] = nullptr;
     351        8057 :         ++nLines;
     352             :     }
     353             : 
     354        1388 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     355             : 
     356             :     // Free the internal thread local line buffer.
     357        1388 :     CPLReadLineL(nullptr);
     358             : 
     359        1388 :     return papszStrList;
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*                              CSLLoad()                               */
     364             : /************************************************************************/
     365             : 
     366             : /**
     367             :  * Load a text file into a string list.
     368             :  *
     369             :  * The VSI*L API is used, so VSIFOpenL() supported objects that aren't
     370             :  * physical files can also be accessed.  Files are returned as a string list,
     371             :  * with one item in the string list per line.  End of line markers are
     372             :  * stripped (by CPLReadLineL()).
     373             :  *
     374             :  * If reading the file fails a CPLError() will be issued and NULL returned.
     375             :  *
     376             :  * @param pszFname the name of the file to read.
     377             :  *
     378             :  * @return a string list with the files lines, now owned by caller. To be freed
     379             :  * with CSLDestroy()
     380             :  */
     381             : 
     382         231 : char **CSLLoad(const char *pszFname)
     383             : {
     384         231 :     return CSLLoad2(pszFname, -1, -1, nullptr);
     385             : }
     386             : 
     387             : /**********************************************************************
     388             :  *                       CSLSave()
     389             :  **********************************************************************/
     390             : 
     391             : /** Write a StringList to a text file.
     392             :  *
     393             :  * Returns the number of lines written, or 0 if the file could not
     394             :  * be written.
     395             :  */
     396             : 
     397           2 : int CSLSave(CSLConstList papszStrList, const char *pszFname)
     398             : {
     399           2 :     if (papszStrList == nullptr)
     400           0 :         return 0;
     401             : 
     402           2 :     VSILFILE *fp = VSIFOpenL(pszFname, "wt");
     403           2 :     if (fp == nullptr)
     404             :     {
     405             :         // Unable to open file.
     406           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     407             :                  "CSLSave(\"%s\") failed: unable to open output file.",
     408             :                  pszFname);
     409           1 :         return 0;
     410             :     }
     411             : 
     412           1 :     int nLines = 0;
     413           2 :     while (*papszStrList != nullptr)
     414             :     {
     415           1 :         if (VSIFPrintfL(fp, "%s\n", *papszStrList) < 1)
     416             :         {
     417           0 :             CPLError(CE_Failure, CPLE_FileIO,
     418             :                      "CSLSave(\"%s\") failed: unable to write to output file.",
     419             :                      pszFname);
     420           0 :             break;  // A Problem happened... abort.
     421             :         }
     422             : 
     423           1 :         ++nLines;
     424           1 :         ++papszStrList;
     425             :     }
     426             : 
     427           1 :     if (VSIFCloseL(fp) != 0)
     428             :     {
     429           0 :         CPLError(CE_Failure, CPLE_FileIO,
     430             :                  "CSLSave(\"%s\") failed: unable to write to output file.",
     431             :                  pszFname);
     432             :     }
     433             : 
     434           1 :     return nLines;
     435             : }
     436             : 
     437             : /**********************************************************************
     438             :  *                       CSLPrint()
     439             :  **********************************************************************/
     440             : 
     441             : /** Print a StringList to fpOut.  If fpOut==NULL, then output is sent
     442             :  * to stdout.
     443             :  *
     444             :  * Returns the number of lines printed.
     445             :  */
     446           0 : int CSLPrint(CSLConstList papszStrList, FILE *fpOut)
     447             : {
     448           0 :     if (!papszStrList)
     449           0 :         return 0;
     450             : 
     451           0 :     if (fpOut == nullptr)
     452           0 :         fpOut = stdout;
     453             : 
     454           0 :     int nLines = 0;
     455             : 
     456           0 :     while (*papszStrList != nullptr)
     457             :     {
     458           0 :         if (VSIFPrintf(fpOut, "%s\n", *papszStrList) < 0)
     459           0 :             return nLines;
     460           0 :         ++nLines;
     461           0 :         ++papszStrList;
     462             :     }
     463             : 
     464           0 :     return nLines;
     465             : }
     466             : 
     467             : /**********************************************************************
     468             :  *                       CSLInsertStrings()
     469             :  **********************************************************************/
     470             : 
     471             : /** Copies the contents of a StringList inside another StringList
     472             :  * before the specified line.
     473             :  *
     474             :  * nInsertAtLineNo is a 0-based line index before which the new strings
     475             :  * should be inserted.  If this value is -1 or is larger than the actual
     476             :  * number of strings in the list then the strings are added at the end
     477             :  * of the source StringList.
     478             :  *
     479             :  * Returns the modified StringList.
     480             :  */
     481             : 
     482       18166 : char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo,
     483             :                         CSLConstList papszNewLines)
     484             : {
     485       18166 :     if (papszNewLines == nullptr)
     486          36 :         return papszStrList;  // Nothing to do!
     487             : 
     488       18130 :     const int nToInsert = CSLCount(papszNewLines);
     489       18130 :     if (nToInsert == 0)
     490        1243 :         return papszStrList;  // Nothing to do!
     491             : 
     492       16887 :     const int nSrcLines = CSLCount(papszStrList);
     493       16887 :     const int nDstLines = nSrcLines + nToInsert;
     494             : 
     495             :     // Allocate room for the new strings.
     496             :     papszStrList = static_cast<char **>(
     497       16887 :         CPLRealloc(papszStrList, (nDstLines + 1) * sizeof(char *)));
     498             : 
     499             :     // Make sure the array is NULL-terminated.  It may not be if
     500             :     // papszStrList was NULL before Realloc().
     501       16887 :     papszStrList[nSrcLines] = nullptr;
     502             : 
     503             :     // Make some room in the original list at the specified location.
     504             :     // Note that we also have to move the NULL pointer at the end of
     505             :     // the source StringList.
     506       16887 :     if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines)
     507       16077 :         nInsertAtLineNo = nSrcLines;
     508             : 
     509             :     {
     510       16887 :         char **ppszSrc = papszStrList + nSrcLines;
     511       16887 :         char **ppszDst = papszStrList + nDstLines;
     512             : 
     513       35084 :         for (int i = nSrcLines; i >= nInsertAtLineNo; --i)
     514             :         {
     515       18197 :             *ppszDst = *ppszSrc;
     516       18197 :             --ppszDst;
     517       18197 :             --ppszSrc;
     518             :         }
     519             :     }
     520             : 
     521             :     // Copy the strings to the list.
     522       16887 :     CSLConstList ppszSrc = papszNewLines;
     523       16887 :     char **ppszDst = papszStrList + nInsertAtLineNo;
     524             : 
     525      148423 :     for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst)
     526             :     {
     527      131536 :         *ppszDst = CPLStrdup(*ppszSrc);
     528             :     }
     529             : 
     530       16887 :     return papszStrList;
     531             : }
     532             : 
     533             : /**********************************************************************
     534             :  *                       CSLInsertString()
     535             :  **********************************************************************/
     536             : 
     537             : /** Insert a string at a given line number inside a StringList
     538             :  *
     539             :  * nInsertAtLineNo is a 0-based line index before which the new string
     540             :  * should be inserted.  If this value is -1 or is larger than the actual
     541             :  * number of strings in the list then the string is added at the end
     542             :  * of the source StringList.
     543             :  *
     544             :  * Returns the modified StringList.
     545             :  */
     546             : 
     547         964 : char **CSLInsertString(char **papszStrList, int nInsertAtLineNo,
     548             :                        const char *pszNewLine)
     549             : {
     550         964 :     char *apszList[2] = {const_cast<char *>(pszNewLine), nullptr};
     551             : 
     552        1928 :     return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList);
     553             : }
     554             : 
     555             : /**********************************************************************
     556             :  *                       CSLRemoveStrings()
     557             :  **********************************************************************/
     558             : 
     559             : /** Remove strings inside a StringList
     560             :  *
     561             :  * nFirstLineToDelete is the 0-based line index of the first line to
     562             :  * remove. If this value is -1 or is larger than the actual
     563             :  * number of strings in list then the nNumToRemove last strings are
     564             :  * removed.
     565             :  *
     566             :  * If ppapszRetStrings != NULL then the deleted strings won't be
     567             :  * free'd, they will be stored in a new StringList and the pointer to
     568             :  * this new list will be returned in *ppapszRetStrings.
     569             :  *
     570             :  * Returns the modified StringList.
     571             :  */
     572             : 
     573        7018 : char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete,
     574             :                         int nNumToRemove, char ***ppapszRetStrings)
     575             : {
     576        7018 :     const int nSrcLines = CSLCount(papszStrList);
     577             : 
     578        7018 :     if (nNumToRemove < 1 || nSrcLines == 0)
     579           0 :         return papszStrList;  // Nothing to do!
     580             : 
     581             :     // If operation will result in an empty StringList, don't waste
     582             :     // time here.
     583        7018 :     const int nDstLines = nSrcLines - nNumToRemove;
     584        7018 :     if (nDstLines < 1)
     585             :     {
     586        1163 :         CSLDestroy(papszStrList);
     587        1163 :         return nullptr;
     588             :     }
     589             : 
     590             :     // -1, or a range extending past the end, means "remove the last
     591             :     // nNumToRemove strings". Resolve it before deriving any pointer from it.
     592        5855 :     if (nFirstLineToDelete < 0 || nFirstLineToDelete > nDstLines)
     593           6 :         nFirstLineToDelete = nDstLines;
     594             : 
     595             :     // Remove lines from the source StringList.
     596             :     // Either free() each line or store them to a new StringList depending on
     597             :     // the caller's choice.
     598        5855 :     char **ppszDst = papszStrList + nFirstLineToDelete;
     599             : 
     600        5855 :     if (ppapszRetStrings == nullptr)
     601             :     {
     602             :         // free() all the strings that will be removed.
     603       11713 :         for (int i = 0; i < nNumToRemove; ++i)
     604             :         {
     605        5859 :             CPLFree(*ppszDst);
     606        5859 :             *ppszDst = nullptr;
     607        5859 :             ++ppszDst;
     608             :         }
     609             :     }
     610             :     else
     611             :     {
     612             :         // Store the strings to remove in a new StringList.
     613           1 :         *ppapszRetStrings =
     614           1 :             static_cast<char **>(CPLCalloc(nNumToRemove + 1, sizeof(char *)));
     615             : 
     616           3 :         for (int i = 0; i < nNumToRemove; ++i)
     617             :         {
     618           2 :             (*ppapszRetStrings)[i] = *ppszDst;
     619           2 :             *ppszDst = nullptr;
     620           2 :             ++ppszDst;
     621             :         }
     622             :     }
     623             : 
     624             :     // Shift down all the lines that follow the lines to remove.
     625        5855 :     char **ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove;
     626        5855 :     ppszDst = papszStrList + nFirstLineToDelete;
     627             : 
     628       12300 :     for (; *ppszSrc != nullptr; ++ppszSrc, ++ppszDst)
     629             :     {
     630        6445 :         *ppszDst = *ppszSrc;
     631             :     }
     632             :     // Move the NULL pointer at the end of the StringList.
     633        5855 :     *ppszDst = *ppszSrc;
     634             : 
     635             :     // At this point, we could realloc() papszStrList to a smaller size, but
     636             :     // since this array will likely grow again in further operations on the
     637             :     // StringList we'll leave it as it is.
     638        5855 :     return papszStrList;
     639             : }
     640             : 
     641             : /************************************************************************/
     642             : /*                           CSLFindString()                            */
     643             : /************************************************************************/
     644             : 
     645             : /**
     646             :  * Find a string within a string list (case insensitive).
     647             :  *
     648             :  * Returns the index of the entry in the string list that contains the
     649             :  * target string.  The string in the string list must be a full match for
     650             :  * the target, but the search is case insensitive.
     651             :  *
     652             :  * @param papszList the string list to be searched.
     653             :  * @param pszTarget the string to be searched for.
     654             :  *
     655             :  * @return the index of the string within the list or -1 on failure.
     656             :  */
     657             : 
     658      826213 : int CSLFindString(CSLConstList papszList, const char *pszTarget)
     659             : 
     660             : {
     661      826213 :     if (papszList == nullptr)
     662      303459 :         return -1;
     663             : 
     664    19802300 :     for (int i = 0; papszList[i] != nullptr; ++i)
     665             :     {
     666    19392400 :         if (EQUAL(papszList[i], pszTarget))
     667      112797 :             return i;
     668             :     }
     669             : 
     670      409957 :     return -1;
     671             : }
     672             : 
     673             : /************************************************************************/
     674             : /*                     CSLFindStringCaseSensitive()                     */
     675             : /************************************************************************/
     676             : 
     677             : /**
     678             :  * Find a string within a string list(case sensitive)
     679             :  *
     680             :  * Returns the index of the entry in the string list that contains the
     681             :  * target string.  The string in the string list must be a full match for
     682             :  * the target.
     683             :  *
     684             :  * @param papszList the string list to be searched.
     685             :  * @param pszTarget the string to be searched for.
     686             :  *
     687             :  * @return the index of the string within the list or -1 on failure.
     688             :  *
     689             :  */
     690             : 
     691        3116 : int CSLFindStringCaseSensitive(CSLConstList papszList, const char *pszTarget)
     692             : 
     693             : {
     694        3116 :     if (papszList == nullptr)
     695         742 :         return -1;
     696             : 
     697       14681 :     for (int i = 0; papszList[i] != nullptr; ++i)
     698             :     {
     699       12321 :         if (strcmp(papszList[i], pszTarget) == 0)
     700          14 :             return i;
     701             :     }
     702             : 
     703        2360 :     return -1;
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                        CSLPartialFindString()                        */
     708             : /************************************************************************/
     709             : 
     710             : /**
     711             :  * Find a substring within a string list.
     712             :  *
     713             :  * Returns the index of the entry in the string list that contains the
     714             :  * target string as a substring.  The search is case sensitive (unlike
     715             :  * CSLFindString()).
     716             :  *
     717             :  * @param papszHaystack the string list to be searched.
     718             :  * @param pszNeedle the substring to be searched for.
     719             :  *
     720             :  * @return the index of the string within the list or -1 on failure.
     721             :  */
     722             : 
     723       26075 : int CSLPartialFindString(CSLConstList papszHaystack, const char *pszNeedle)
     724             : {
     725       26075 :     if (papszHaystack == nullptr || pszNeedle == nullptr)
     726        7138 :         return -1;
     727             : 
     728      148316 :     for (int i = 0; papszHaystack[i] != nullptr; ++i)
     729             :     {
     730      138057 :         if (strstr(papszHaystack[i], pszNeedle))
     731        8678 :             return i;
     732             :     }
     733             : 
     734       10259 :     return -1;
     735             : }
     736             : 
     737             : /**********************************************************************
     738             :  *                       CSLTokenizeString()
     739             :  **********************************************************************/
     740             : 
     741             : /** Tokenizes a string and returns a StringList with one string for
     742             :  * each token.
     743             :  */
     744      205759 : char **CSLTokenizeString(const char *pszString)
     745             : {
     746      205759 :     return CSLTokenizeString2(pszString, " ", CSLT_HONOURSTRINGS);
     747             : }
     748             : 
     749             : /************************************************************************/
     750             : /*                      CSLTokenizeStringComplex()                      */
     751             : /************************************************************************/
     752             : 
     753             : /** Obsolete tokenizing api. Use CSLTokenizeString2() */
     754      691658 : char **CSLTokenizeStringComplex(const char *pszString,
     755             :                                 const char *pszDelimiters, int bHonourStrings,
     756             :                                 int bAllowEmptyTokens)
     757             : {
     758      691658 :     int nFlags = 0;
     759             : 
     760      691658 :     if (bHonourStrings)
     761      130807 :         nFlags |= CSLT_HONOURSTRINGS;
     762      691658 :     if (bAllowEmptyTokens)
     763       17825 :         nFlags |= CSLT_ALLOWEMPTYTOKENS;
     764             : 
     765      691658 :     return CSLTokenizeString2(pszString, pszDelimiters, nFlags);
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                         CSLTokenizeString2()                         */
     770             : /************************************************************************/
     771             : 
     772             : /**
     773             :  * Tokenize a string.
     774             :  *
     775             :  * This function will split a string into tokens based on specified
     776             :  * delimiter(s) with a variety of options.  The returned result is a
     777             :  * string list that should be freed with CSLDestroy() when no longer
     778             :  * needed.
     779             :  *
     780             :  * The available parsing options are:
     781             :  *
     782             :  * - CSLT_ALLOWEMPTYTOKENS: allow the return of empty tokens when two
     783             :  * delimiters in a row occur with no other text between them.  If not set,
     784             :  * empty tokens will be discarded;
     785             :  * - CSLT_STRIPLEADSPACES: strip leading space characters from the token (as
     786             :  * reported by isspace());
     787             :  * - CSLT_STRIPENDSPACES: strip ending space characters from the token (as
     788             :  * reported by isspace());
     789             :  * - CSLT_HONOURSTRINGS: double quotes can be used to hold values that should
     790             :  * not be broken into multiple tokens;
     791             :  * - CSLT_HONOURSINGLEQUOTES: single quotes can be used to hold values that should
     792             :  * not be broken into multiple tokens;
     793             :  * - CSLT_PRESERVEQUOTES: string quotes are carried into the tokens when this
     794             :  * is set, otherwise they are removed;
     795             :  * - CSLT_PRESERVEESCAPES: if set backslash escapes (for backslash itself,
     796             :  * and for literal single/double quotes) will be preserved in the tokens, otherwise
     797             :  * the backslashes will be removed in processing.
     798             :  *
     799             :  * \b Example:
     800             :  *
     801             :  * Parse a string into tokens based on various white space (space, newline,
     802             :  * tab) and then print out results and cleanup.  Quotes may be used to hold
     803             :  * white space in tokens.
     804             : 
     805             : \code
     806             :     char **papszTokens =
     807             :         CSLTokenizeString2( pszCommand, " \t\n",
     808             :                             CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS );
     809             : 
     810             :     for( int i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i )
     811             :         printf( "arg %d: '%s'", papszTokens[i] );  // ok
     812             : 
     813             :     CSLDestroy( papszTokens );
     814             : \endcode
     815             : 
     816             :  * @param pszString the string to be split into tokens.
     817             :  * @param pszDelimiters one or more characters to be used as token delimiters.
     818             :  * @param nCSLTFlags an ORing of one or more of the CSLT_ flag values.
     819             :  *
     820             :  * @return a string list of tokens owned by the caller.
     821             :  */
     822             : 
     823     1569160 : char **CSLTokenizeString2(const char *pszString, const char *pszDelimiters,
     824             :                           int nCSLTFlags)
     825             : {
     826     1569160 :     if (pszString == nullptr)
     827        4541 :         return static_cast<char **>(CPLCalloc(sizeof(char *), 1));
     828             : 
     829     3129170 :     return cpl::tokenize_string(pszString, pszDelimiters, nCSLTFlags)
     830     1564580 :         .StealList();
     831             : }
     832             : 
     833             : namespace cpl
     834             : {
     835     1564630 : CPLStringList tokenize_string(std::string_view str, std::string_view delimiters,
     836             :                               int nCSLTFlags)
     837             : {
     838     3129240 :     CPLStringList oRetList;
     839     1564590 :     const bool bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS) != 0;
     840     1564590 :     const bool bHonourStringsSingleQuotes =
     841     1564590 :         (nCSLTFlags & CSLT_HONOURSINGLEQUOTES) != 0;
     842     1564590 :     const bool bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS) != 0;
     843     1564590 :     const bool bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES) != 0;
     844     1564590 :     const bool bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES) != 0;
     845             : 
     846     1564590 :     size_t pos = 0;
     847     3129130 :     std::string token;
     848     4914520 :     while (pos < str.size())
     849             :     {
     850     3349910 :         token.clear();
     851     3349980 :         bool bInString = false;
     852     3349980 :         bool bInStringSingleQuote = false;
     853             : 
     854             :         // Try to find the next delimiter, marking end of token.
     855    42677300 :         while (pos < str.size())
     856             :         {
     857             :             // End if this is a delimiter skip it and break.
     858    81095900 :             if (!bInString && !bInStringSingleQuote &&
     859    39923900 :                 delimiters.find(str[pos]) != std::string_view::npos)
     860             :             {
     861     1844890 :                 pos++;
     862     1844890 :                 break;
     863             :             }
     864             : 
     865             :             // If this is a quote, and we are honouring constant
     866             :             // strings, then process the constant strings, with out delim
     867             :             // but don't copy over the quotes.
     868    39327100 :             if (bHonourStrings && !bInStringSingleQuote && str[pos] == '"')
     869             :             {
     870       76866 :                 if (nCSLTFlags & CSLT_PRESERVEQUOTES)
     871             :                 {
     872        5543 :                     token.push_back(str[pos]);
     873             :                 }
     874             : 
     875       76866 :                 bInString = !bInString;
     876       76866 :                 pos++;
     877       76866 :                 continue;
     878             :             }
     879    39250300 :             else if (bHonourStringsSingleQuotes && !bHonourStrings &&
     880           0 :                      str[pos] == '\'')
     881             :             {
     882           0 :                 if (nCSLTFlags & CSLT_PRESERVEQUOTES)
     883             :                 {
     884           0 :                     token.push_back(str[pos]);
     885             :                 }
     886             : 
     887           0 :                 bInStringSingleQuote = !bInStringSingleQuote;
     888           0 :                 pos++;
     889           0 :                 continue;
     890             :             }
     891             : 
     892             :             /*
     893             :              * Within string constants we allow for escaped quotes, but in
     894             :              * processing them we will unescape the quotes and \\ sequence
     895             :              * reduces to \
     896             :              */
     897    39250300 :             if (bInString && str[pos] == '\\')
     898             :             {
     899         244 :                 if (pos + 1 < str.size() &&
     900         122 :                     (str[pos + 1] == '"' || str[pos + 1] == '\\'))
     901             :                 {
     902          48 :                     if (nCSLTFlags & CSLT_PRESERVEESCAPES)
     903             :                     {
     904           6 :                         token.push_back(str[pos]);
     905             :                     }
     906             : 
     907          48 :                     ++pos;
     908             :                 }
     909             :             }
     910    39250100 :             else if (bInStringSingleQuote && str[pos] == '\\')
     911             :             {
     912           0 :                 if (pos + 1 < str.size() &&
     913           0 :                     (str[pos + 1] == '\'' || str[pos + 1] == '\\'))
     914             :                 {
     915           0 :                     if (nCSLTFlags & CSLT_PRESERVEESCAPES)
     916             :                     {
     917           0 :                         token.push_back(str[pos]);
     918             :                     }
     919             : 
     920           0 :                     ++pos;
     921             :                 }
     922             :             }
     923             : 
     924    39250300 :             token.push_back(str[pos]);
     925    39250500 :             pos++;
     926             :         }
     927             : 
     928             :         // Add the token.
     929     3349940 :         std::string_view token_view(token);
     930     3349990 :         if (bStripLeadSpaces)
     931             :         {
     932       34809 :             token_view = ltrim(token_view);
     933             :         }
     934     3349990 :         if (bStripEndSpaces)
     935             :         {
     936       34756 :             token_view = rtrim(token_view);
     937             :         }
     938             : 
     939     3349990 :         if (!token_view.empty() || bAllowEmptyTokens)
     940     3219550 :             oRetList.AddString(token_view);
     941             :     }
     942             : 
     943             :     /*
     944             :      * If the last token was empty, then we need to capture
     945             :      * it now, as the loop would skip it.
     946             :      */
     947     3101260 :     if (!str.empty() && pos == str.size() && bAllowEmptyTokens &&
     948     3129340 :         oRetList.Count() > 0 &&
     949       28103 :         delimiters.find(str[pos - 1]) != std::string_view::npos)
     950             :     {
     951        1410 :         oRetList.AddString("");
     952             :     }
     953             : 
     954     1564600 :     if (oRetList.List() == nullptr)
     955             :     {
     956             :         // Prefer to return empty lists as a pointer to
     957             :         // a null pointer since some client code might depend on this.
     958       28028 :         oRetList.Assign(static_cast<char **>(CPLCalloc(sizeof(char *), 1)));
     959             :     }
     960             : 
     961     3129170 :     return CPLStringList(oRetList.StealList());
     962             : }
     963             : 
     964             : }  // namespace cpl
     965             : 
     966             : /**********************************************************************
     967             :  *                       CPLSPrintf()
     968             :  *
     969             :  * NOTE: This function should move to cpl_conv.cpp.
     970             :  **********************************************************************/
     971             : 
     972             : // For now, assume that a 8000 chars buffer will be enough.
     973             : constexpr int CPLSPrintf_BUF_SIZE = 8000;
     974             : constexpr int CPLSPrintf_BUF_Count = 10;
     975             : 
     976             : /** CPLSPrintf() that works with 10 static buffer.
     977             :  *
     978             :  * It returns a ref. to a static buffer that should not be freed and
     979             :  * is valid only until the next call to CPLSPrintf().
     980             :  */
     981             : 
     982     1792050 : const char *CPLSPrintf(CPL_FORMAT_STRING(const char *fmt), ...)
     983             : {
     984             :     va_list args;
     985             : 
     986             :     /* -------------------------------------------------------------------- */
     987             :     /*      Get the thread local buffer ring data.                          */
     988             :     /* -------------------------------------------------------------------- */
     989     1792050 :     char *pachBufRingInfo = static_cast<char *>(CPLGetTLS(CTLS_CPLSPRINTF));
     990             : 
     991     1792030 :     if (pachBufRingInfo == nullptr)
     992             :     {
     993        7127 :         pachBufRingInfo = static_cast<char *>(CPLCalloc(
     994             :             1, sizeof(int) + CPLSPrintf_BUF_Count * CPLSPrintf_BUF_SIZE));
     995        7128 :         CPLSetTLS(CTLS_CPLSPRINTF, pachBufRingInfo, TRUE);
     996             :     }
     997             : 
     998             :     /* -------------------------------------------------------------------- */
     999             :     /*      Work out which string in the "ring" we want to use this         */
    1000             :     /*      time.                                                           */
    1001             :     /* -------------------------------------------------------------------- */
    1002     1792040 :     int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
    1003     1792040 :     const size_t nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE;
    1004     1792040 :     char *pachBuffer = pachBufRingInfo + nOffset;
    1005             : 
    1006     1792040 :     *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count;
    1007             : 
    1008             :     /* -------------------------------------------------------------------- */
    1009             :     /*      Format the result.                                              */
    1010             :     /* -------------------------------------------------------------------- */
    1011             : 
    1012     1792040 :     va_start(args, fmt);
    1013             : 
    1014             :     const int ret =
    1015     1792040 :         CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE - 1, fmt, args);
    1016     1792040 :     if (ret < 0 || ret >= CPLSPrintf_BUF_SIZE - 1)
    1017             :     {
    1018          16 :         CPLError(CE_Failure, CPLE_AppDefined,
    1019             :                  "CPLSPrintf() called with too "
    1020             :                  "big string. Output will be truncated !");
    1021             :     }
    1022             : 
    1023     1792020 :     va_end(args);
    1024             : 
    1025     1792020 :     return pachBuffer;
    1026             : }
    1027             : 
    1028             : /**********************************************************************
    1029             :  *                       CSLAppendPrintf()
    1030             :  **********************************************************************/
    1031             : 
    1032             : /** Use CPLSPrintf() to append a new line at the end of a StringList.
    1033             :  * Returns the modified StringList.
    1034             :  */
    1035         194 : char **CSLAppendPrintf(char **papszStrList, CPL_FORMAT_STRING(const char *fmt),
    1036             :                        ...)
    1037             : {
    1038             :     va_list args;
    1039             : 
    1040         194 :     va_start(args, fmt);
    1041         388 :     CPLString osWork;
    1042         194 :     osWork.vPrintf(fmt, args);
    1043         194 :     va_end(args);
    1044             : 
    1045         388 :     return CSLAddString(papszStrList, osWork);
    1046             : }
    1047             : 
    1048             : /************************************************************************/
    1049             : /*                            CPLVASPrintf()                            */
    1050             : /************************************************************************/
    1051             : 
    1052             : /** This is intended to serve as an easy to use C callable vasprintf()
    1053             :  * alternative.  Used in the GeoJSON library for instance */
    1054           0 : int CPLVASPrintf(char **buf, CPL_FORMAT_STRING(const char *fmt), va_list ap)
    1055             : 
    1056             : {
    1057           0 :     CPLString osWork;
    1058             : 
    1059           0 :     osWork.vPrintf(fmt, ap);
    1060             : 
    1061           0 :     if (buf)
    1062           0 :         *buf = CPLStrdup(osWork.c_str());
    1063             : 
    1064           0 :     return static_cast<int>(osWork.size());
    1065             : }
    1066             : 
    1067             : /************************************************************************/
    1068             : /*                 CPLvsnprintf_get_end_of_formatting()                 */
    1069             : /************************************************************************/
    1070             : 
    1071     4702720 : static const char *CPLvsnprintf_get_end_of_formatting(const char *fmt)
    1072             : {
    1073     4702720 :     char ch = '\0';
    1074             :     // Flag.
    1075     5899430 :     for (; (ch = *fmt) != '\0'; ++fmt)
    1076             :     {
    1077     5899370 :         if (ch == '\'')
    1078           0 :             continue;  // Bad idea as this is locale specific.
    1079     5899370 :         if (ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0')
    1080     1196710 :             continue;
    1081     4702650 :         break;
    1082             :     }
    1083             : 
    1084             :     // Field width.
    1085     6061690 :     for (; (ch = *fmt) != '\0'; ++fmt)
    1086             :     {
    1087     6061640 :         if (ch == '$')
    1088           0 :             return nullptr;  // Do not support this.
    1089     6061640 :         if (*fmt >= '0' && *fmt <= '9')
    1090     1358980 :             continue;
    1091     4702660 :         break;
    1092             :     }
    1093             : 
    1094             :     // Precision.
    1095     4702720 :     if (ch == '.')
    1096             :     {
    1097      716319 :         ++fmt;
    1098     2029720 :         for (; (ch = *fmt) != '\0'; ++fmt)
    1099             :         {
    1100     2029720 :             if (ch == '$')
    1101           0 :                 return nullptr;  // Do not support this.
    1102     2029720 :             if (*fmt >= '0' && *fmt <= '9')
    1103     1313400 :                 continue;
    1104      716319 :             break;
    1105             :         }
    1106             :     }
    1107             : 
    1108             :     // Length modifier.
    1109     4827650 :     for (; (ch = *fmt) != '\0'; ++fmt)
    1110             :     {
    1111     4827650 :         if (ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' || ch == 't' ||
    1112             :             ch == 'L')
    1113      125016 :             continue;
    1114     4702630 :         else if (ch == 'I' && fmt[1] == '6' && fmt[2] == '4')
    1115           0 :             fmt += 2;
    1116             :         else
    1117     4702720 :             return fmt;
    1118             :     }
    1119             : 
    1120           0 :     return nullptr;
    1121             : }
    1122             : 
    1123             : /************************************************************************/
    1124             : /*                            CPLvsnprintf()                            */
    1125             : /************************************************************************/
    1126             : 
    1127             : #define call_native_snprintf(type)                                             \
    1128             :     local_ret = snprintf(str + offset_out, size - offset_out, localfmt,        \
    1129             :                          va_arg(wrk_args, type))
    1130             : 
    1131             : /** vsnprintf() wrapper that is not sensitive to LC_NUMERIC settings.
    1132             :  *
    1133             :  * This function has the same contract as standard vsnprintf(), except that
    1134             :  * formatting of floating-point numbers will use decimal point, whatever the
    1135             :  * current locale is set.
    1136             :  *
    1137             :  * @param str output buffer
    1138             :  * @param size size of the output buffer (including space for terminating nul)
    1139             :  * @param fmt formatting string
    1140             :  * @param args arguments
    1141             :  * @return the number of characters (excluding terminating nul) that would be
    1142             :  * written if size is big enough. Or potentially -1 with Microsoft C runtime
    1143             :  * for Visual Studio < 2015.
    1144             :  */
    1145     2790980 : int CPLvsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt),
    1146             :                  va_list args)
    1147             : {
    1148     2790980 :     if (size == 0)
    1149           0 :         return vsnprintf(str, size, fmt, args);
    1150             : 
    1151             :     va_list wrk_args;
    1152             : 
    1153             : #ifdef va_copy
    1154     2790980 :     va_copy(wrk_args, args);
    1155             : #else
    1156             :     wrk_args = args;
    1157             : #endif
    1158             : 
    1159     2790980 :     const char *fmt_ori = fmt;
    1160     2790980 :     size_t offset_out = 0;
    1161     2790980 :     char ch = '\0';
    1162     2790980 :     bool bFormatUnknown = false;
    1163             : 
    1164    38525000 :     for (; (ch = *fmt) != '\0'; ++fmt)
    1165             :     {
    1166    35735900 :         if (ch == '%')
    1167             :         {
    1168     4703360 :             if (strncmp(fmt, "%.*f", 4) == 0)
    1169             :             {
    1170         666 :                 const int precision = va_arg(wrk_args, int);
    1171         666 :                 const double val = va_arg(wrk_args, double);
    1172             :                 const int local_ret =
    1173         682 :                     snprintf(str + offset_out, size - offset_out, "%.*f",
    1174             :                              precision, val);
    1175             :                 // MSVC vsnprintf() returns -1.
    1176         682 :                 if (local_ret < 0 || offset_out + local_ret >= size)
    1177             :                     break;
    1178       11919 :                 for (int j = 0; j < local_ret; ++j)
    1179             :                 {
    1180       11253 :                     if (str[offset_out + j] == ',')
    1181             :                     {
    1182           0 :                         str[offset_out + j] = '.';
    1183           0 :                         break;
    1184             :                     }
    1185             :                 }
    1186         666 :                 offset_out += local_ret;
    1187         666 :                 fmt += strlen("%.*f") - 1;
    1188         666 :                 continue;
    1189             :             }
    1190             : 
    1191     4702690 :             const char *ptrend = CPLvsnprintf_get_end_of_formatting(fmt + 1);
    1192     4702700 :             if (ptrend == nullptr || ptrend - fmt >= 20)
    1193             :             {
    1194           0 :                 bFormatUnknown = true;
    1195           0 :                 break;
    1196             :             }
    1197     4702700 :             char end = *ptrend;
    1198     4702700 :             char end_m1 = ptrend[-1];
    1199             : 
    1200     4702700 :             char localfmt[22] = {};
    1201     4702700 :             memcpy(localfmt, fmt, ptrend - fmt + 1);
    1202     4702700 :             localfmt[ptrend - fmt + 1] = '\0';
    1203             : 
    1204     4702700 :             int local_ret = 0;
    1205     4702700 :             if (end == '%')
    1206             :             {
    1207       15539 :                 if (offset_out == size - 1)
    1208           0 :                     break;
    1209       15539 :                 local_ret = 1;
    1210       15539 :                 str[offset_out] = '%';
    1211             :             }
    1212     4687160 :             else if (end == 'd' || end == 'i' || end == 'c')
    1213             :             {
    1214     1559060 :                 if (end_m1 == 'h')
    1215           0 :                     call_native_snprintf(int);
    1216     1559060 :                 else if (end_m1 == 'l' && ptrend[-2] != 'l')
    1217        4373 :                     call_native_snprintf(long);
    1218     1554690 :                 else if (end_m1 == 'l' && ptrend[-2] == 'l')
    1219       32436 :                     call_native_snprintf(GIntBig);
    1220     1522260 :                 else if (end_m1 == '4' && ptrend[-2] == '6' &&
    1221           0 :                          ptrend[-3] == 'I')
    1222             :                     // Microsoft I64 modifier.
    1223           0 :                     call_native_snprintf(GIntBig);
    1224     1522260 :                 else if (end_m1 == 'z')
    1225           0 :                     call_native_snprintf(size_t);
    1226     1522260 :                 else if ((end_m1 >= 'a' && end_m1 <= 'z') ||
    1227           0 :                          (end_m1 >= 'A' && end_m1 <= 'Z'))
    1228             :                 {
    1229           0 :                     bFormatUnknown = true;
    1230           0 :                     break;
    1231             :                 }
    1232             :                 else
    1233     1522260 :                     call_native_snprintf(int);
    1234             :             }
    1235     3128100 :             else if (end == 'o' || end == 'u' || end == 'x' || end == 'X')
    1236             :             {
    1237     1228320 :                 if (end_m1 == 'h')
    1238           0 :                     call_native_snprintf(unsigned int);
    1239     1228320 :                 else if (end_m1 == 'l' && ptrend[-2] != 'l')
    1240       14140 :                     call_native_snprintf(unsigned long);
    1241     1214180 :                 else if (end_m1 == 'l' && ptrend[-2] == 'l')
    1242       17701 :                     call_native_snprintf(GUIntBig);
    1243     1196480 :                 else if (end_m1 == '4' && ptrend[-2] == '6' &&
    1244           0 :                          ptrend[-3] == 'I')
    1245             :                     // Microsoft I64 modifier.
    1246           0 :                     call_native_snprintf(GUIntBig);
    1247     1196480 :                 else if (end_m1 == 'z')
    1248           0 :                     call_native_snprintf(size_t);
    1249     1196480 :                 else if ((end_m1 >= 'a' && end_m1 <= 'z') ||
    1250           0 :                          (end_m1 >= 'A' && end_m1 <= 'Z'))
    1251             :                 {
    1252           0 :                     bFormatUnknown = true;
    1253           0 :                     break;
    1254             :                 }
    1255             :                 else
    1256     1196480 :                     call_native_snprintf(unsigned int);
    1257             :             }
    1258     1899770 :             else if (end == 'e' || end == 'E' || end == 'f' || end == 'F' ||
    1259     1157250 :                      end == 'g' || end == 'G' || end == 'a' || end == 'A')
    1260             :             {
    1261      742546 :                 if (end_m1 == 'L')
    1262           0 :                     call_native_snprintf(long double);
    1263             :                 else
    1264      742546 :                     call_native_snprintf(double);
    1265             :                 // MSVC vsnprintf() returns -1.
    1266      742608 :                 if (local_ret < 0 || offset_out + local_ret >= size)
    1267             :                     break;
    1268    10305400 :                 for (int j = 0; j < local_ret; ++j)
    1269             :                 {
    1270     9562880 :                     if (str[offset_out + j] == ',')
    1271             :                     {
    1272           0 :                         str[offset_out + j] = '.';
    1273           0 :                         break;
    1274             :                     }
    1275      742520 :                 }
    1276             :             }
    1277     1157220 :             else if (end == 's')
    1278             :             {
    1279     1150710 :                 const char *pszPtr = va_arg(wrk_args, const char *);
    1280     1150660 :                 CPLAssert(pszPtr);
    1281     1150640 :                 local_ret = snprintf(str + offset_out, size - offset_out,
    1282             :                                      localfmt, pszPtr);
    1283             :             }
    1284        6511 :             else if (end == 'p')
    1285             :             {
    1286        6174 :                 call_native_snprintf(void *);
    1287             :             }
    1288             :             else
    1289             :             {
    1290         337 :                 bFormatUnknown = true;
    1291         337 :                 break;
    1292             :             }
    1293             :             // MSVC vsnprintf() returns -1.
    1294     4702190 :             if (local_ret < 0 || offset_out + local_ret >= size)
    1295             :                 break;
    1296     4701320 :             offset_out += local_ret;
    1297     4701320 :             fmt = ptrend;
    1298             :         }
    1299             :         else
    1300             :         {
    1301    31032600 :             if (offset_out == size - 1)
    1302         597 :                 break;
    1303    31032000 :             str[offset_out++] = *fmt;
    1304             :         }
    1305             :     }
    1306     2790920 :     if (ch == '\0' && offset_out < size)
    1307     2788950 :         str[offset_out] = '\0';
    1308             :     else
    1309             :     {
    1310        1975 :         if (bFormatUnknown)
    1311             :         {
    1312         342 :             CPLDebug("CPL",
    1313             :                      "CPLvsnprintf() called with unsupported "
    1314             :                      "formatting string: %s",
    1315             :                      fmt_ori);
    1316             :         }
    1317             : #ifdef va_copy
    1318        1907 :         va_end(wrk_args);
    1319        1907 :         va_copy(wrk_args, args);
    1320             : #else
    1321             :         wrk_args = args;
    1322             : #endif
    1323             : #if defined(HAVE_VSNPRINTF)
    1324        1907 :         offset_out = vsnprintf(str, size, fmt_ori, wrk_args);
    1325             : #else
    1326             :         offset_out = vsprintf(str, fmt_ori, wrk_args);
    1327             : #endif
    1328             :     }
    1329             : 
    1330             : #ifdef va_copy
    1331     2790860 :     va_end(wrk_args);
    1332             : #endif
    1333             : 
    1334     2790860 :     return static_cast<int>(offset_out);
    1335             : }
    1336             : 
    1337             : /************************************************************************/
    1338             : /*                            CPLsnprintf()                             */
    1339             : /************************************************************************/
    1340             : 
    1341             : #if !defined(ALIAS_CPLSNPRINTF_AS_SNPRINTF)
    1342             : 
    1343             : #if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 2
    1344             : #pragma clang diagnostic push
    1345             : #pragma clang diagnostic ignored "-Wunknown-pragmas"
    1346             : #pragma clang diagnostic ignored "-Wdocumentation"
    1347             : #endif
    1348             : 
    1349             : /** snprintf() wrapper that is not sensitive to LC_NUMERIC settings.
    1350             :  *
    1351             :  * This function has the same contract as standard snprintf(), except that
    1352             :  * formatting of floating-point numbers will use decimal point, whatever the
    1353             :  * current locale is set.
    1354             :  *
    1355             :  * @param str output buffer
    1356             :  * @param size size of the output buffer (including space for terminating nul)
    1357             :  * @param fmt formatting string
    1358             :  * @param ... arguments
    1359             :  * @return the number of characters (excluding terminating nul) that would be
    1360             :  * written if size is big enough. Or potentially -1 with Microsoft C runtime
    1361             :  * for Visual Studio < 2015.
    1362             :  */
    1363             : 
    1364      178433 : int CPLsnprintf(char *str, size_t size, CPL_FORMAT_STRING(const char *fmt), ...)
    1365             : {
    1366             :     va_list args;
    1367             : 
    1368      178433 :     va_start(args, fmt);
    1369      178433 :     const int ret = CPLvsnprintf(str, size, fmt, args);
    1370      178433 :     va_end(args);
    1371      178433 :     return ret;
    1372             : }
    1373             : 
    1374             : #endif  //  !defined(ALIAS_CPLSNPRINTF_AS_SNPRINTF)
    1375             : 
    1376             : /************************************************************************/
    1377             : /*                             CPLsprintf()                             */
    1378             : /************************************************************************/
    1379             : 
    1380             : /** sprintf() wrapper that is not sensitive to LC_NUMERIC settings.
    1381             :   *
    1382             :   * This function has the same contract as standard sprintf(), except that
    1383             :   * formatting of floating-point numbers will use decimal point, whatever the
    1384             :   * current locale is set.
    1385             :   *
    1386             :   * @param str output buffer (must be large enough to hold the result)
    1387             :   * @param fmt formatting string
    1388             :   * @param ... arguments
    1389             :   * @return the number of characters (excluding terminating nul) written in
    1390             : ` * output buffer.
    1391             :   */
    1392           0 : int CPLsprintf(char *str, CPL_FORMAT_STRING(const char *fmt), ...)
    1393             : {
    1394             :     va_list args;
    1395             : 
    1396           0 :     va_start(args, fmt);
    1397           0 :     const int ret = CPLvsnprintf(str, INT_MAX, fmt, args);
    1398           0 :     va_end(args);
    1399           0 :     return ret;
    1400             : }
    1401             : 
    1402             : /************************************************************************/
    1403             : /*                             CPLprintf()                              */
    1404             : /************************************************************************/
    1405             : 
    1406             : /** printf() wrapper that is not sensitive to LC_NUMERIC settings.
    1407             :  *
    1408             :  * This function has the same contract as standard printf(), except that
    1409             :  * formatting of floating-point numbers will use decimal point, whatever the
    1410             :  * current locale is set.
    1411             :  *
    1412             :  * @param fmt formatting string
    1413             :  * @param ... arguments
    1414             :  * @return the number of characters (excluding terminating nul) written in
    1415             :  * output buffer.
    1416             :  */
    1417         157 : int CPLprintf(CPL_FORMAT_STRING(const char *fmt), ...)
    1418             : {
    1419             :     va_list wrk_args, args;
    1420             : 
    1421         157 :     va_start(args, fmt);
    1422             : 
    1423             : #ifdef va_copy
    1424         157 :     va_copy(wrk_args, args);
    1425             : #else
    1426             :     wrk_args = args;
    1427             : #endif
    1428             : 
    1429         157 :     char szBuffer[4096] = {};
    1430             :     // Quiet coverity by staring off nul terminated.
    1431         157 :     int ret = CPLvsnprintf(szBuffer, sizeof(szBuffer), fmt, wrk_args);
    1432             : 
    1433             : #ifdef va_copy
    1434         157 :     va_end(wrk_args);
    1435             : #endif
    1436             : 
    1437         157 :     if (ret < int(sizeof(szBuffer)) - 1)
    1438         157 :         ret = printf("%s", szBuffer); /*ok*/
    1439             :     else
    1440             :     {
    1441             : #ifdef va_copy
    1442           0 :         va_copy(wrk_args, args);
    1443             : #else
    1444             :         wrk_args = args;
    1445             : #endif
    1446             : 
    1447           0 :         ret = vfprintf(stdout, fmt, wrk_args);
    1448             : 
    1449             : #ifdef va_copy
    1450           0 :         va_end(wrk_args);
    1451             : #endif
    1452             :     }
    1453             : 
    1454         157 :     va_end(args);
    1455             : 
    1456         157 :     return ret;
    1457             : }
    1458             : 
    1459             : /************************************************************************/
    1460             : /*                             CPLsscanf()                              */
    1461             : /************************************************************************/
    1462             : 
    1463             : /** \brief sscanf() wrapper that is not sensitive to LC_NUMERIC settings.
    1464             :  *
    1465             :  * This function has the same contract as standard sscanf(), except that
    1466             :  * formatting of floating-point numbers will use decimal point, whatever the
    1467             :  * current locale is set.
    1468             :  *
    1469             :  * CAUTION: only works with a very limited number of formatting strings,
    1470             :  * consisting only of "%lf" and regular characters.
    1471             :  *
    1472             :  * @param str input string
    1473             :  * @param fmt formatting string
    1474             :  * @param ... arguments
    1475             :  * @return the number of matched patterns;
    1476             :  */
    1477             : #ifdef DOXYGEN_XML
    1478             : int CPLsscanf(const char *str, const char *fmt, ...)
    1479             : #else
    1480        3078 : int CPLsscanf(const char *str, CPL_SCANF_FORMAT_STRING(const char *fmt), ...)
    1481             : #endif
    1482             : {
    1483        3078 :     bool error = false;
    1484        3078 :     int ret = 0;
    1485        3078 :     const char *fmt_ori = fmt;
    1486             :     va_list args;
    1487             : 
    1488        3078 :     va_start(args, fmt);
    1489       14543 :     for (; *fmt != '\0' && *str != '\0'; ++fmt)
    1490             :     {
    1491       11465 :         if (*fmt == '%')
    1492             :         {
    1493        7253 :             if (fmt[1] == 'l' && fmt[2] == 'f')
    1494             :             {
    1495        7253 :                 fmt += 2;
    1496             :                 char *end;
    1497        7253 :                 *(va_arg(args, double *)) = CPLStrtod(str, &end);
    1498        7253 :                 if (end > str)
    1499             :                 {
    1500        7253 :                     ++ret;
    1501        7253 :                     str = end;
    1502             :                 }
    1503             :                 else
    1504        7253 :                     break;
    1505             :             }
    1506             :             else
    1507             :             {
    1508           0 :                 error = true;
    1509           0 :                 break;
    1510             :             }
    1511             :         }
    1512        4212 :         else if (isspace(static_cast<unsigned char>(*fmt)))
    1513             :         {
    1514        1754 :             while (*str != '\0' && isspace(static_cast<unsigned char>(*str)))
    1515         877 :                 ++str;
    1516             :         }
    1517        3335 :         else if (*str != *fmt)
    1518           0 :             break;
    1519             :         else
    1520        3335 :             ++str;
    1521             :     }
    1522        3078 :     va_end(args);
    1523             : 
    1524        3078 :     if (error)
    1525             :     {
    1526           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1527             :                  "Format %s not supported by CPLsscanf()", fmt_ori);
    1528             :     }
    1529             : 
    1530        3078 :     return ret;
    1531             : }
    1532             : 
    1533             : #if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 2
    1534             : #pragma clang diagnostic pop
    1535             : #endif
    1536             : 
    1537             : /************************************************************************/
    1538             : /*                            CPLTestBool()                             */
    1539             : /************************************************************************/
    1540             : 
    1541             : /**
    1542             :  * Test what boolean value contained in the string.
    1543             :  *
    1544             :  * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned false.
    1545             :  * Otherwise, true will be returned.
    1546             :  *
    1547             :  * @param pszValue the string should be tested.
    1548             :  *
    1549             :  * @return true or false.
    1550             :  */
    1551             : 
    1552     3829330 : bool CPLTestBool(const char *pszValue)
    1553             : {
    1554     4939960 :     return !(EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") ||
    1555     4939960 :              EQUAL(pszValue, "OFF") || EQUAL(pszValue, "0"));
    1556             : }
    1557             : 
    1558             : /// Return true if the config option's value represents a boolean true.
    1559             : /// \param configVal  String name of config value.
    1560             : /// \return  Whether the config option's value represents true.
    1561        4419 : bool CPLTestConfigOption(const char *configVal)
    1562             : {
    1563        4419 :     return CPLTestBool(CPLGetConfigOption(configVal, "NO"));
    1564             : }
    1565             : 
    1566             : /************************************************************************/
    1567             : /*                           CSLTestBoolean()                           */
    1568             : /************************************************************************/
    1569             : 
    1570             : /**
    1571             :  * Test what boolean value contained in the string.
    1572             :  *
    1573             :  * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
    1574             :  * Otherwise, TRUE will be returned.
    1575             :  *
    1576             :  * Deprecated.  Removed in GDAL 3.x.
    1577             :  *
    1578             :  * Use CPLTestBoolean() for C and CPLTestBool() for C++.
    1579             :  *
    1580             :  * @param pszValue the string should be tested.
    1581             :  *
    1582             :  * @return TRUE or FALSE.
    1583             :  */
    1584             : 
    1585         760 : int CSLTestBoolean(const char *pszValue)
    1586             : {
    1587         760 :     return CPLTestBool(pszValue) ? TRUE : FALSE;
    1588             : }
    1589             : 
    1590             : /************************************************************************/
    1591             : /*                           CPLTestBoolean()                           */
    1592             : /************************************************************************/
    1593             : 
    1594             : /**
    1595             :  * Test what boolean value contained in the string.
    1596             :  *
    1597             :  * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE.
    1598             :  * Otherwise, TRUE will be returned.
    1599             :  *
    1600             :  * Use this only in C code.  In C++, prefer CPLTestBool().
    1601             :  *
    1602             :  * @param pszValue the string should be tested.
    1603             :  *
    1604             :  * @return TRUE or FALSE.
    1605             :  */
    1606             : 
    1607         164 : int CPLTestBoolean(const char *pszValue)
    1608             : {
    1609         164 :     return CPLTestBool(pszValue) ? TRUE : FALSE;
    1610             : }
    1611             : 
    1612             : /**********************************************************************
    1613             :  *                       CPLFetchBool()
    1614             :  **********************************************************************/
    1615             : 
    1616             : /** Check for boolean key value.
    1617             :  *
    1618             :  * In a StringList of "Name=Value" pairs, look to see if there is a key
    1619             :  * with the given name, and if it can be interpreted as being TRUE.  If
    1620             :  * the key appears without any "=Value" portion it will be considered true.
    1621             :  * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
    1622             :  * if the key appears in the list it will be considered TRUE.  If the key
    1623             :  * doesn't appear at all, the indicated default value will be returned.
    1624             :  *
    1625             :  * @param papszStrList the string list to search.
    1626             :  * @param pszKey the key value to look for (case insensitive).
    1627             :  * @param bDefault the value to return if the key isn't found at all.
    1628             :  *
    1629             :  * @return true or false
    1630             :  */
    1631             : 
    1632      379801 : bool CPLFetchBool(CSLConstList papszStrList, const char *pszKey, bool bDefault)
    1633             : 
    1634             : {
    1635      379801 :     if (CSLFindString(papszStrList, pszKey) != -1)
    1636           2 :         return true;
    1637             : 
    1638      379752 :     const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey);
    1639      379745 :     if (pszValue == nullptr)
    1640      360093 :         return bDefault;
    1641             : 
    1642       19652 :     return CPLTestBool(pszValue);
    1643             : }
    1644             : 
    1645             : /**********************************************************************
    1646             :  *                       CSLFetchBoolean()
    1647             :  **********************************************************************/
    1648             : 
    1649             : /** DEPRECATED.  Check for boolean key value.
    1650             :  *
    1651             :  * In a StringList of "Name=Value" pairs, look to see if there is a key
    1652             :  * with the given name, and if it can be interpreted as being TRUE.  If
    1653             :  * the key appears without any "=Value" portion it will be considered true.
    1654             :  * If the value is NO, FALSE or 0 it will be considered FALSE otherwise
    1655             :  * if the key appears in the list it will be considered TRUE.  If the key
    1656             :  * doesn't appear at all, the indicated default value will be returned.
    1657             :  *
    1658             :  * @param papszStrList the string list to search.
    1659             :  * @param pszKey the key value to look for (case insensitive).
    1660             :  * @param bDefault the value to return if the key isn't found at all.
    1661             :  *
    1662             :  * @return TRUE or FALSE
    1663             :  */
    1664             : 
    1665        1026 : int CSLFetchBoolean(CSLConstList papszStrList, const char *pszKey, int bDefault)
    1666             : 
    1667             : {
    1668        1026 :     return CPLFetchBool(papszStrList, pszKey, CPL_TO_BOOL(bDefault));
    1669             : }
    1670             : 
    1671             : /************************************************************************/
    1672             : /*                     CSLFetchNameValueDefaulted()                     */
    1673             : /************************************************************************/
    1674             : 
    1675             : /** Same as CSLFetchNameValue() but return pszDefault in case of no match */
    1676      975803 : const char *CSLFetchNameValueDef(CSLConstList papszStrList, const char *pszName,
    1677             :                                  const char *pszDefault)
    1678             : 
    1679             : {
    1680      975803 :     const char *pszResult = CSLFetchNameValue(papszStrList, pszName);
    1681      975600 :     if (pszResult != nullptr)
    1682      192284 :         return pszResult;
    1683             : 
    1684      783316 :     return pszDefault;
    1685             : }
    1686             : 
    1687             : /**********************************************************************
    1688             :  *                       CSLFetchNameValue()
    1689             :  **********************************************************************/
    1690             : 
    1691             : /** In a StringList of "Name=Value" pairs, look for the
    1692             :  * first value associated with the specified name.  The search is not
    1693             :  * case sensitive.
    1694             :  * ("Name:Value" pairs are also supported for backward compatibility
    1695             :  * with older stuff.)
    1696             :  *
    1697             :  * Returns a reference to the value in the StringList that the caller
    1698             :  * should not attempt to free.
    1699             :  *
    1700             :  * Returns NULL if the name is not found.
    1701             :  */
    1702             : 
    1703    20345800 : const char *CSLFetchNameValue(CSLConstList papszStrList, const char *pszName)
    1704             : {
    1705    20345800 :     if (papszStrList == nullptr || pszName == nullptr)
    1706     5716570 :         return nullptr;
    1707             : 
    1708    14629200 :     const size_t nLen = strlen(pszName);
    1709    24235200 :     while (*papszStrList != nullptr)
    1710             :     {
    1711     9981350 :         if (EQUALN(*papszStrList, pszName, nLen) &&
    1712      386427 :             ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
    1713             :         {
    1714      375418 :             return (*papszStrList) + nLen + 1;
    1715             :         }
    1716     9605930 :         ++papszStrList;
    1717             :     }
    1718    14253800 :     return nullptr;
    1719             : }
    1720             : 
    1721             : /************************************************************************/
    1722             : /*                            CSLFindName()                             */
    1723             : /************************************************************************/
    1724             : 
    1725             : /**
    1726             :  * Find StringList entry with given key name.
    1727             :  *
    1728             :  * @param papszStrList the string list to search.
    1729             :  * @param pszName the key value to look for (case insensitive).
    1730             :  *
    1731             :  * @return -1 on failure or the list index of the first occurrence
    1732             :  * matching the given key.
    1733             :  */
    1734             : 
    1735    18331700 : int CSLFindName(CSLConstList papszStrList, const char *pszName)
    1736             : {
    1737    18331700 :     if (papszStrList == nullptr || pszName == nullptr)
    1738      933604 :         return -1;
    1739             : 
    1740    17398000 :     const size_t nLen = strlen(pszName);
    1741    17398000 :     int iIndex = 0;
    1742   157829000 :     while (*papszStrList != nullptr)
    1743             :     {
    1744   148254000 :         if (EQUALN(*papszStrList, pszName, nLen) &&
    1745     8710390 :             ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
    1746             :         {
    1747     7823390 :             return iIndex;
    1748             :         }
    1749   140431000 :         ++iIndex;
    1750   140431000 :         ++papszStrList;
    1751             :     }
    1752     9574660 :     return -1;
    1753             : }
    1754             : 
    1755             : /************************************************************************/
    1756             : /*                         CPLParseMemorySize()                         */
    1757             : /************************************************************************/
    1758             : 
    1759             : /** Parse a memory size from a string.
    1760             :  *
    1761             :  * The string may indicate the units of the memory (e.g., "230k", "500 MB"),
    1762             :  * using the prefixes "k", "m", or "g" in either lower or upper-case,
    1763             :  * optionally followed by a "b" or "B". The string may alternatively specify
    1764             :  * memory as a fraction of the usable RAM (e.g., "25%"). Spaces before the
    1765             :  * number, between the number and the units, or after the units are ignored,
    1766             :  * but other characters will cause a parsing failure. If the string cannot
    1767             :  * be understood, the function will return CE_Failure.
    1768             :  *
    1769             :  * @param pszValue the string to parse
    1770             :  * @param[out] pnValue the parsed size, converted to bytes (if unit was specified)
    1771             :  * @param[out] pbUnitSpecified whether the string indicated the units
    1772             :  *
    1773             :  * @return CE_None on success, CE_Failure otherwise
    1774             :  * @since 3.10
    1775             :  */
    1776        8731 : CPLErr CPLParseMemorySize(const char *pszValue, GIntBig *pnValue,
    1777             :                           bool *pbUnitSpecified)
    1778             : {
    1779        8731 :     const char *start = pszValue;
    1780        8731 :     char *end = nullptr;
    1781             : 
    1782             :     // trim leading whitespace
    1783        8735 :     while (*start == ' ')
    1784             :     {
    1785           4 :         start++;
    1786             :     }
    1787             : 
    1788        8731 :     auto len = CPLStrnlen(start, 100);
    1789        8731 :     double value = CPLStrtodM(start, &end);
    1790        8731 :     const char *unit = nullptr;
    1791        8731 :     bool unitIsNotPercent = false;
    1792             : 
    1793        8731 :     if (end == start)
    1794             :     {
    1795           3 :         CPLError(CE_Failure, CPLE_IllegalArg, "Received non-numeric value: %s",
    1796             :                  pszValue);
    1797           3 :         return CE_Failure;
    1798             :     }
    1799             : 
    1800        8728 :     if (value < 0 || !std::isfinite(value))
    1801             :     {
    1802           3 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1803             :                  "Memory size must be a positive number or zero.");
    1804           3 :         return CE_Failure;
    1805             :     }
    1806             : 
    1807       25272 :     for (const char *c = end; c < start + len; c++)
    1808             :     {
    1809       16553 :         if (unit == nullptr)
    1810             :         {
    1811             :             // check various suffixes and convert number into bytes
    1812        8576 :             if (*c == '%')
    1813             :             {
    1814         546 :                 if (value < 0 || value > 100)
    1815             :                 {
    1816           2 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    1817             :                              "Memory percentage must be between 0 and 100.");
    1818           2 :                     return CE_Failure;
    1819             :                 }
    1820         544 :                 auto bytes = CPLGetUsablePhysicalRAM();
    1821         544 :                 if (bytes == 0)
    1822             :                 {
    1823           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1824             :                              "Cannot determine usable physical RAM");
    1825           0 :                     return CE_Failure;
    1826             :                 }
    1827         544 :                 value *= static_cast<double>(bytes / 100);
    1828         544 :                 unit = c;
    1829             :             }
    1830             :             else
    1831             :             {
    1832        8030 :                 switch (*c)
    1833             :                 {
    1834          35 :                     case 'G':
    1835             :                     case 'g':
    1836          35 :                         value *= 1024;
    1837             :                         [[fallthrough]];
    1838        7973 :                     case 'M':
    1839             :                     case 'm':
    1840        7973 :                         value *= 1024;
    1841             :                         [[fallthrough]];
    1842        8019 :                     case 'K':
    1843             :                     case 'k':
    1844        8019 :                         value *= 1024;
    1845        8019 :                         unit = c;
    1846        8019 :                         unitIsNotPercent = true;
    1847        8019 :                         break;
    1848           9 :                     case ' ':
    1849           9 :                         break;
    1850           2 :                     default:
    1851           2 :                         CPLError(CE_Failure, CPLE_IllegalArg,
    1852             :                                  "Failed to parse memory size: %s", pszValue);
    1853           2 :                         return CE_Failure;
    1854             :                 }
    1855             :             }
    1856             :         }
    1857        7977 :         else if (unitIsNotPercent && c == unit + 1 && (*c == 'b' || *c == 'B'))
    1858             :         {
    1859             :             // ignore 'B' or 'b' as part of unit
    1860        7975 :             continue;
    1861             :         }
    1862           2 :         else if (*c != ' ')
    1863             :         {
    1864           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1865             :                      "Failed to parse memory size: %s", pszValue);
    1866           2 :             return CE_Failure;
    1867             :         }
    1868             :     }
    1869             : 
    1870       17437 :     if (value > static_cast<double>(std::numeric_limits<GIntBig>::max()) ||
    1871        8718 :         value > static_cast<double>(std::numeric_limits<size_t>::max()))
    1872             :     {
    1873           1 :         CPLError(CE_Failure, CPLE_IllegalArg, "Memory size is too large: %s",
    1874             :                  pszValue);
    1875           1 :         return CE_Failure;
    1876             :     }
    1877             : 
    1878        8718 :     *pnValue = static_cast<GIntBig>(value);
    1879        8718 :     if (pbUnitSpecified)
    1880             :     {
    1881         719 :         *pbUnitSpecified = (unit != nullptr);
    1882             :     }
    1883        8718 :     return CE_None;
    1884             : }
    1885             : 
    1886             : /**********************************************************************
    1887             :  *                       CPLParseNameValue()
    1888             :  **********************************************************************/
    1889             : 
    1890             : /**
    1891             :  * Parse NAME=VALUE string into name and value components.
    1892             :  *
    1893             :  * Note that if ppszKey is non-NULL, the key (or name) portion will be
    1894             :  * allocated using CPLMalloc() and returned in that pointer.  It is the
    1895             :  * application's responsibility to free this string, but the application should
    1896             :  * not modify or free the returned value portion.
    1897             :  *
    1898             :  * This function also supports "NAME:VALUE" strings and will strip white
    1899             :  * space from around the delimiter when forming name and value strings.
    1900             :  *
    1901             :  * Eventually CSLFetchNameValue() and friends may be modified to use
    1902             :  * CPLParseNameValue().
    1903             :  *
    1904             :  * @param pszNameValue string in "NAME=VALUE" format.
    1905             :  * @param ppszKey optional pointer though which to return the name
    1906             :  * portion.
    1907             :  *
    1908             :  * @return the value portion (pointing into the original string).
    1909             :  */
    1910             : 
    1911       91296 : const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey)
    1912             : {
    1913     1338060 :     for (int i = 0; pszNameValue[i] != '\0'; ++i)
    1914             :     {
    1915     1335090 :         if (pszNameValue[i] == '=' || pszNameValue[i] == ':')
    1916             :         {
    1917       88329 :             const char *pszValue = pszNameValue + i + 1;
    1918       95268 :             while (*pszValue == ' ' || *pszValue == '\t')
    1919        6939 :                 ++pszValue;
    1920             : 
    1921       88329 :             if (ppszKey != nullptr)
    1922             :             {
    1923       88305 :                 *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
    1924       88305 :                 memcpy(*ppszKey, pszNameValue, i);
    1925       88305 :                 (*ppszKey)[i] = '\0';
    1926       88666 :                 while (i > 0 &&
    1927       88666 :                        ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
    1928             :                 {
    1929         361 :                     (*ppszKey)[i - 1] = '\0';
    1930         361 :                     i--;
    1931             :                 }
    1932             :             }
    1933             : 
    1934       88329 :             return pszValue;
    1935             :         }
    1936             :     }
    1937             : 
    1938        2967 :     return nullptr;
    1939             : }
    1940             : 
    1941             : namespace cpl
    1942             : {
    1943             : std::pair<std::string_view, std::string_view>
    1944           6 : parse_name_value(std::string_view svNameValue)
    1945             : {
    1946          40 :     for (size_t i = 0; i < svNameValue.size(); ++i)
    1947             :     {
    1948          38 :         if (svNameValue[i] == '=' || svNameValue[i] == ':')
    1949             :         {
    1950           4 :             auto parsed = std::make_pair(trim(svNameValue.substr(0, i)),
    1951           8 :                                          trim(svNameValue.substr(i + 1)));
    1952             : 
    1953           4 :             if (!parsed.first.empty())
    1954             :             {
    1955           3 :                 return parsed;
    1956             :             }
    1957             :             else
    1958             :             {
    1959           2 :                 return std::make_pair(std::string_view(), std::string_view());
    1960             :             }
    1961             :         }
    1962             :     }
    1963             : 
    1964           4 :     return std::make_pair(std::string_view(), std::string_view());
    1965             : }
    1966             : 
    1967             : std::pair<std::string_view, std::string_view>
    1968           2 : parse_name_value(const char *pszNameValue)
    1969             : {
    1970           2 :     return parse_name_value(std::string_view(pszNameValue));
    1971             : }
    1972             : 
    1973             : }  // namespace cpl
    1974             : 
    1975             : /**********************************************************************
    1976             :  *                       CPLParseNameValueSep()
    1977             :  **********************************************************************/
    1978             : /**
    1979             :  * Parse NAME<Sep>VALUE string into name and value components.
    1980             :  *
    1981             :  * This is derived directly from CPLParseNameValue() which will separate
    1982             :  * on '=' OR ':', here chSep is required for specifying the separator
    1983             :  * explicitly.
    1984             :  *
    1985             :  * @param pszNameValue string in "NAME=VALUE" format.
    1986             :  * @param ppszKey optional pointer though which to return the name
    1987             :  * portion.
    1988             :  * @param chSep required single char separator
    1989             :  * @return the value portion (pointing into original string).
    1990             :  */
    1991             : 
    1992          17 : const char *CPLParseNameValueSep(const char *pszNameValue, char **ppszKey,
    1993             :                                  char chSep)
    1994             : {
    1995         140 :     for (int i = 0; pszNameValue[i] != '\0'; ++i)
    1996             :     {
    1997         138 :         if (pszNameValue[i] == chSep)
    1998             :         {
    1999          15 :             const char *pszValue = pszNameValue + i + 1;
    2000          15 :             while (*pszValue == ' ' || *pszValue == '\t')
    2001           0 :                 ++pszValue;
    2002             : 
    2003          15 :             if (ppszKey != nullptr)
    2004             :             {
    2005          15 :                 *ppszKey = static_cast<char *>(CPLMalloc(i + 1));
    2006          15 :                 memcpy(*ppszKey, pszNameValue, i);
    2007          15 :                 (*ppszKey)[i] = '\0';
    2008          15 :                 while (i > 0 &&
    2009          15 :                        ((*ppszKey)[i - 1] == ' ' || (*ppszKey)[i - 1] == '\t'))
    2010             :                 {
    2011           0 :                     (*ppszKey)[i - 1] = '\0';
    2012           0 :                     i--;
    2013             :                 }
    2014             :             }
    2015             : 
    2016          15 :             return pszValue;
    2017             :         }
    2018             :     }
    2019             : 
    2020           2 :     return nullptr;
    2021             : }
    2022             : 
    2023             : /**********************************************************************
    2024             :  *                       CSLFetchNameValueMultiple()
    2025             :  **********************************************************************/
    2026             : 
    2027             : /** In a StringList of "Name=Value" pairs, look for all the
    2028             :  * values with the specified name.  The search is not case
    2029             :  * sensitive.
    2030             :  * ("Name:Value" pairs are also supported for backward compatibility
    2031             :  * with older stuff.)
    2032             :  *
    2033             :  * Returns StringList with one entry for each occurrence of the
    2034             :  * specified name.  The StringList should eventually be destroyed
    2035             :  * by calling CSLDestroy().
    2036             :  *
    2037             :  * Returns NULL if the name is not found.
    2038             :  */
    2039             : 
    2040       15193 : char **CSLFetchNameValueMultiple(CSLConstList papszStrList, const char *pszName)
    2041             : {
    2042       15193 :     if (papszStrList == nullptr || pszName == nullptr)
    2043        6690 :         return nullptr;
    2044             : 
    2045        8503 :     const size_t nLen = strlen(pszName);
    2046        8503 :     char **papszValues = nullptr;
    2047       23869 :     while (*papszStrList != nullptr)
    2048             :     {
    2049       15366 :         if (EQUALN(*papszStrList, pszName, nLen) &&
    2050          65 :             ((*papszStrList)[nLen] == '=' || (*papszStrList)[nLen] == ':'))
    2051             :         {
    2052          65 :             papszValues = CSLAddString(papszValues, (*papszStrList) + nLen + 1);
    2053             :         }
    2054       15366 :         ++papszStrList;
    2055             :     }
    2056             : 
    2057        8503 :     return papszValues;
    2058             : }
    2059             : 
    2060             : /**********************************************************************
    2061             :  *                       CSLAddNameValue()
    2062             :  **********************************************************************/
    2063             : 
    2064             : /** Add a new entry to a StringList of "Name=Value" pairs,
    2065             :  * ("Name:Value" pairs are also supported for backward compatibility
    2066             :  * with older stuff.)
    2067             :  *
    2068             :  * This function does not check if a "Name=Value" pair already exists
    2069             :  * for that name and can generate multiple entries for the same name.
    2070             :  * Use CSLSetNameValue() if you want each name to have only one value.
    2071             :  *
    2072             :  * Returns the modified StringList.
    2073             :  */
    2074             : 
    2075      371455 : char **CSLAddNameValue(char **papszStrList, const char *pszName,
    2076             :                        const char *pszValue)
    2077             : {
    2078      371455 :     if (pszName == nullptr || pszValue == nullptr)
    2079           1 :         return papszStrList;
    2080             : 
    2081      371454 :     const size_t nLen = strlen(pszName) + strlen(pszValue) + 2;
    2082      371454 :     char *pszLine = static_cast<char *>(CPLMalloc(nLen));
    2083      371452 :     snprintf(pszLine, nLen, "%s=%s", pszName, pszValue);
    2084      371452 :     papszStrList = CSLAddString(papszStrList, pszLine);
    2085      371441 :     CPLFree(pszLine);
    2086             : 
    2087      371456 :     return papszStrList;
    2088             : }
    2089             : 
    2090             : /************************************************************************/
    2091             : /*                          CSLSetNameValue()                           */
    2092             : /************************************************************************/
    2093             : 
    2094             : /**
    2095             :  * Assign value to name in StringList.
    2096             :  *
    2097             :  * Set the value for a given name in a StringList of "Name=Value" pairs
    2098             :  * ("Name:Value" pairs are also supported for backward compatibility
    2099             :  * with older stuff.)
    2100             :  *
    2101             :  * If there is already a value for that name in the list then the value
    2102             :  * is changed, otherwise a new "Name=Value" pair is added.
    2103             :  *
    2104             :  * @param papszList the original list, the modified version is returned.
    2105             :  * @param pszName the name to be assigned a value.  This should be a well
    2106             :  * formed token (no spaces or very special characters).
    2107             :  * @param pszValue the value to assign to the name.  This should not contain
    2108             :  * any newlines (CR or LF) but is otherwise pretty much unconstrained.  If
    2109             :  * NULL any corresponding value will be removed.
    2110             :  *
    2111             :  * @return modified StringList.
    2112             :  */
    2113             : 
    2114      403330 : char **CSLSetNameValue(char **papszList, const char *pszName,
    2115             :                        const char *pszValue)
    2116             : {
    2117      403330 :     if (pszName == nullptr)
    2118          38 :         return papszList;
    2119             : 
    2120      403292 :     size_t nLen = strlen(pszName);
    2121      403966 :     while (nLen > 0 && pszName[nLen - 1] == ' ')
    2122         674 :         nLen--;
    2123      403292 :     char **papszPtr = papszList;
    2124     4586080 :     while (papszPtr && *papszPtr != nullptr)
    2125             :     {
    2126     4226470 :         if (EQUALN(*papszPtr, pszName, nLen))
    2127             :         {
    2128             :             size_t i;
    2129       46069 :             for (i = nLen; (*papszPtr)[i] == ' '; ++i)
    2130             :             {
    2131             :             }
    2132       45395 :             if ((*papszPtr)[i] == '=' || (*papszPtr)[i] == ':')
    2133             :             {
    2134             :                 // Found it.
    2135             :                 // Change the value... make sure to keep the ':' or '='.
    2136       43689 :                 const char cSep = (*papszPtr)[i];
    2137             : 
    2138       43689 :                 CPLFree(*papszPtr);
    2139             : 
    2140             :                 // If the value is NULL, remove this entry completely.
    2141       43691 :                 if (pszValue == nullptr)
    2142             :                 {
    2143       48460 :                     while (papszPtr[1] != nullptr)
    2144             :                     {
    2145       12720 :                         *papszPtr = papszPtr[1];
    2146       12720 :                         ++papszPtr;
    2147             :                     }
    2148       35740 :                     *papszPtr = nullptr;
    2149             :                 }
    2150             : 
    2151             :                 // Otherwise replace with new value.
    2152             :                 else
    2153             :                 {
    2154        7951 :                     const size_t nLen2 = strlen(pszName) + strlen(pszValue) + 2;
    2155        7951 :                     *papszPtr = static_cast<char *>(CPLMalloc(nLen2));
    2156        7945 :                     snprintf(*papszPtr, nLen2, "%s%c%s", pszName, cSep,
    2157             :                              pszValue);
    2158             :                 }
    2159       43685 :                 return papszList;
    2160             :             }
    2161             :         }
    2162     4182780 :         ++papszPtr;
    2163             :     }
    2164             : 
    2165      359603 :     if (pszValue == nullptr)
    2166        3028 :         return papszList;
    2167             : 
    2168             :     // The name does not exist yet.  Create a new entry.
    2169      356575 :     return CSLAddNameValue(papszList, pszName, pszValue);
    2170             : }
    2171             : 
    2172             : /************************************************************************/
    2173             : /*                      CSLSetNameValueSeparator()                      */
    2174             : /************************************************************************/
    2175             : 
    2176             : /**
    2177             :  * Replace the default separator (":" or "=") with the passed separator
    2178             :  * in the given name/value list.
    2179             :  *
    2180             :  * Note that if a separator other than ":" or "=" is used, the resulting
    2181             :  * list will not be manipulable by the CSL name/value functions any more.
    2182             :  *
    2183             :  * The CPLParseNameValue() function is used to break the existing lines,
    2184             :  * and it also strips white space from around the existing delimiter, thus
    2185             :  * the old separator, and any white space will be replaced by the new
    2186             :  * separator.  For formatting purposes it may be desirable to include some
    2187             :  * white space in the new separator.  e.g. ": " or " = ".
    2188             :  *
    2189             :  * @param papszList the list to update.  Component strings may be freed
    2190             :  * but the list array will remain at the same location.
    2191             :  *
    2192             :  * @param pszSeparator the new separator string to insert.
    2193             :  */
    2194             : 
    2195          68 : void CSLSetNameValueSeparator(char **papszList, const char *pszSeparator)
    2196             : 
    2197             : {
    2198          68 :     const int nLines = CSLCount(papszList);
    2199             : 
    2200         583 :     for (int iLine = 0; iLine < nLines; ++iLine)
    2201             :     {
    2202         515 :         char *pszKey = nullptr;
    2203         515 :         const char *pszValue = CPLParseNameValue(papszList[iLine], &pszKey);
    2204         515 :         if (pszValue == nullptr || pszKey == nullptr)
    2205             :         {
    2206           0 :             CPLFree(pszKey);
    2207           0 :             continue;
    2208             :         }
    2209             : 
    2210        1030 :         char *pszNewLine = static_cast<char *>(CPLMalloc(
    2211         515 :             strlen(pszValue) + strlen(pszKey) + strlen(pszSeparator) + 1));
    2212         515 :         strcpy(pszNewLine, pszKey);
    2213         515 :         strcat(pszNewLine, pszSeparator);
    2214         515 :         strcat(pszNewLine, pszValue);
    2215         515 :         CPLFree(papszList[iLine]);
    2216         515 :         papszList[iLine] = pszNewLine;
    2217         515 :         CPLFree(pszKey);
    2218             :     }
    2219          68 : }
    2220             : 
    2221             : /************************************************************************/
    2222             : /*                          CPLEscapeString()                           */
    2223             : /************************************************************************/
    2224             : 
    2225             : /**
    2226             :  * Apply escaping to string to preserve special characters.
    2227             :  *
    2228             :  * This function will "escape" a variety of special characters
    2229             :  * to make the string suitable to embed within a string constant
    2230             :  * or to write within a text stream but in a form that can be
    2231             :  * reconstituted to its original form.  The escaping will even preserve
    2232             :  * zero bytes allowing preservation of raw binary data.
    2233             :  *
    2234             :  * CPLES_BackslashQuotable(0): This scheme turns a binary string into
    2235             :  * a form suitable to be placed within double quotes as a string constant.
    2236             :  * The backslash, quote, '\\0' and newline characters are all escaped in
    2237             :  * the usual C style.
    2238             :  *
    2239             :  * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into
    2240             :  * their XML/HTML equivalent (&lt;, &gt;, &quot; and &amp;) making a string safe
    2241             :  * to embed as CDATA within an XML element.  The '\\0' is not escaped and
    2242             :  * should not be included in the input.
    2243             :  *
    2244             :  * CPLES_URL(2): Everything except alphanumerics and the characters
    2245             :  * '$', '-', '_', '.', '+', '!', '*', ''', '(', ')' and ',' (see RFC1738) are
    2246             :  * converted to a percent followed by a two digit hex encoding of the character
    2247             :  * (leading zero supplied if needed).  This is the mechanism used for encoding
    2248             :  * values to be passed in URLs. Note that this is different from what
    2249             :  * CPLString::URLEncode() does.
    2250             :  *
    2251             :  * CPLES_SQL(3): All single quotes are replaced with two single quotes.
    2252             :  * Suitable for use when constructing literal values for SQL commands where
    2253             :  * the literal will be enclosed in single quotes.
    2254             :  *
    2255             :  * CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes,
    2256             :  * or newlines it placed in double quotes, and double quotes in the value are
    2257             :  * doubled. Suitable for use when constructing field values for .csv files.
    2258             :  * Note that CPLUnescapeString() currently does not support this format, only
    2259             :  * CPLEscapeString().  See cpl_csv.cpp for CSV parsing support.
    2260             :  *
    2261             :  * CPLES_SQLI(7): All double quotes are replaced with two double quotes.
    2262             :  * Suitable for use when constructing identifiers for SQL commands where
    2263             :  * the literal will be enclosed in double quotes.
    2264             :  *
    2265             :  * @param pszInput the string to escape.
    2266             :  * @param nLength The number of bytes of data to preserve.  If this is -1
    2267             :  * the strlen(pszString) function will be used to compute the length.
    2268             :  * @param nScheme the encoding scheme to use.
    2269             :  *
    2270             :  * @return an escaped, zero terminated string that should be freed with
    2271             :  * CPLFree() when no longer needed.
    2272             :  */
    2273             : 
    2274      717292 : char *CPLEscapeString(const char *pszInput, int nLength, int nScheme)
    2275             : {
    2276      717292 :     const size_t szLength =
    2277      717292 :         (nLength < 0) ? strlen(pszInput) : static_cast<size_t>(nLength);
    2278             : #define nLength no_longer_use_me
    2279             : 
    2280      717292 :     size_t nSizeAlloc = 1;
    2281             : #if SIZEOF_VOIDP < 8
    2282             :     bool bWrapAround = false;
    2283             :     const auto IncSizeAlloc = [&nSizeAlloc, &bWrapAround](size_t inc)
    2284             :     {
    2285             :         constexpr size_t SZ_MAX = std::numeric_limits<size_t>::max();
    2286             :         if (nSizeAlloc > SZ_MAX - inc)
    2287             :         {
    2288             :             bWrapAround = true;
    2289             :             nSizeAlloc = 0;
    2290             :         }
    2291             :         nSizeAlloc += inc;
    2292             :     };
    2293             : #else
    2294    43351600 :     const auto IncSizeAlloc = [&nSizeAlloc](size_t inc) { nSizeAlloc += inc; };
    2295             : #endif
    2296             : 
    2297      717292 :     if (nScheme == CPLES_BackslashQuotable)
    2298             :     {
    2299       67426 :         for (size_t iIn = 0; iIn < szLength; iIn++)
    2300             :         {
    2301       67233 :             if (pszInput[iIn] == '\0' || pszInput[iIn] == '\n' ||
    2302       55586 :                 pszInput[iIn] == '"' || pszInput[iIn] == '\\')
    2303       11814 :                 IncSizeAlloc(2);
    2304             :             else
    2305       55419 :                 IncSizeAlloc(1);
    2306             :         }
    2307             :     }
    2308      717099 :     else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
    2309             :     {
    2310    43240900 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2311             :         {
    2312    42527200 :             if (pszInput[iIn] == '<')
    2313             :             {
    2314        1408 :                 IncSizeAlloc(4);
    2315             :             }
    2316    42525800 :             else if (pszInput[iIn] == '>')
    2317             :             {
    2318        1534 :                 IncSizeAlloc(4);
    2319             :             }
    2320    42524300 :             else if (pszInput[iIn] == '&')
    2321             :             {
    2322        1653 :                 IncSizeAlloc(5);
    2323             :             }
    2324    42522600 :             else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES)
    2325             :             {
    2326        2700 :                 IncSizeAlloc(6);
    2327             :             }
    2328             :             // Python 2 does not display the UTF-8 character corresponding
    2329             :             // to the byte-order mark (BOM), so escape it.
    2330    42519900 :             else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] ==
    2331           2 :                          0xEF &&
    2332             :                      (reinterpret_cast<const unsigned char *>(
    2333           2 :                          pszInput))[iIn + 1] == 0xBB &&
    2334             :                      (reinterpret_cast<const unsigned char *>(
    2335           2 :                          pszInput))[iIn + 2] == 0xBF)
    2336             :             {
    2337           2 :                 IncSizeAlloc(8);
    2338           2 :                 iIn += 2;
    2339             :             }
    2340    42519900 :             else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] <
    2341       21952 :                          0x20 &&
    2342       21952 :                      pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA &&
    2343         146 :                      pszInput[iIn] != 0xD)
    2344             :             {
    2345             :                 // These control characters are unrepresentable in XML format,
    2346             :                 // so we just drop them.  #4117
    2347             :             }
    2348             :             else
    2349             :             {
    2350    42519900 :                 IncSizeAlloc(1);
    2351             :             }
    2352      713690 :         }
    2353             :     }
    2354        3409 :     else if (nScheme == CPLES_URL)  // Untested at implementation.
    2355             :     {
    2356       15538 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2357             :         {
    2358       14889 :             if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
    2359        8031 :                 (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
    2360        3065 :                 (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') ||
    2361        1800 :                 pszInput[iIn] == '$' || pszInput[iIn] == '-' ||
    2362        1712 :                 pszInput[iIn] == '_' || pszInput[iIn] == '.' ||
    2363         698 :                 pszInput[iIn] == '+' || pszInput[iIn] == '!' ||
    2364         676 :                 pszInput[iIn] == '*' || pszInput[iIn] == '\'' ||
    2365         674 :                 pszInput[iIn] == '(' || pszInput[iIn] == ')' ||
    2366         664 :                 pszInput[iIn] == ',')
    2367             :             {
    2368       14231 :                 IncSizeAlloc(1);
    2369             :             }
    2370             :             else
    2371             :             {
    2372         658 :                 IncSizeAlloc(3);
    2373             :             }
    2374             :         }
    2375             :     }
    2376        2760 :     else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
    2377             :     {
    2378         855 :         const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"';
    2379       12084 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2380             :         {
    2381       11229 :             if (pszInput[iIn] == chQuote)
    2382             :             {
    2383           5 :                 IncSizeAlloc(2);
    2384             :             }
    2385             :             else
    2386             :             {
    2387       11224 :                 IncSizeAlloc(1);
    2388             :             }
    2389         855 :         }
    2390             :     }
    2391        1905 :     else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING)
    2392             :     {
    2393        1905 :         if (nScheme == CPLES_CSV && strcspn(pszInput, "\",;\t\n\r") == szLength)
    2394             :         {
    2395             :             char *pszOutput =
    2396        1627 :                 static_cast<char *>(VSI_MALLOC_VERBOSE(szLength + 1));
    2397        1627 :             if (pszOutput == nullptr)
    2398           0 :                 return nullptr;
    2399        1627 :             memcpy(pszOutput, pszInput, szLength + 1);
    2400        1627 :             return pszOutput;
    2401             :         }
    2402             :         else
    2403             :         {
    2404         278 :             IncSizeAlloc(1);
    2405       13461 :             for (size_t iIn = 0; iIn < szLength; ++iIn)
    2406             :             {
    2407       13183 :                 if (pszInput[iIn] == '\"')
    2408             :                 {
    2409         169 :                     IncSizeAlloc(2);
    2410             :                 }
    2411             :                 else
    2412       13014 :                     IncSizeAlloc(1);
    2413             :             }
    2414         278 :             IncSizeAlloc(1);
    2415         278 :         }
    2416             :     }
    2417             :     else
    2418             :     {
    2419           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2420             :                  "Undefined escaping scheme (%d) in CPLEscapeString()",
    2421             :                  nScheme);
    2422           0 :         return CPLStrdup("");
    2423             :     }
    2424             : 
    2425             : #if SIZEOF_VOIDP < 8
    2426             :     if (bWrapAround)
    2427             :     {
    2428             :         CPLError(CE_Failure, CPLE_OutOfMemory,
    2429             :                  "Out of memory in CPLEscapeString()");
    2430             :         return nullptr;
    2431             :     }
    2432             : #endif
    2433             : 
    2434      715665 :     char *pszOutput = static_cast<char *>(VSI_MALLOC_VERBOSE(nSizeAlloc));
    2435      715665 :     if (pszOutput == nullptr)
    2436           0 :         return nullptr;
    2437             : 
    2438      715665 :     size_t iOut = 0;
    2439             : 
    2440      715665 :     if (nScheme == CPLES_BackslashQuotable)
    2441             :     {
    2442       67426 :         for (size_t iIn = 0; iIn < szLength; iIn++)
    2443             :         {
    2444       67233 :             if (pszInput[iIn] == '\0')
    2445             :             {
    2446       11469 :                 pszOutput[iOut++] = '\\';
    2447       11469 :                 pszOutput[iOut++] = '0';
    2448             :             }
    2449       55764 :             else if (pszInput[iIn] == '\n')
    2450             :             {
    2451         178 :                 pszOutput[iOut++] = '\\';
    2452         178 :                 pszOutput[iOut++] = 'n';
    2453             :             }
    2454       55586 :             else if (pszInput[iIn] == '"')
    2455             :             {
    2456         128 :                 pszOutput[iOut++] = '\\';
    2457         128 :                 pszOutput[iOut++] = '\"';
    2458             :             }
    2459       55458 :             else if (pszInput[iIn] == '\\')
    2460             :             {
    2461          39 :                 pszOutput[iOut++] = '\\';
    2462          39 :                 pszOutput[iOut++] = '\\';
    2463             :             }
    2464             :             else
    2465       55419 :                 pszOutput[iOut++] = pszInput[iIn];
    2466             :         }
    2467         193 :         pszOutput[iOut++] = '\0';
    2468             :     }
    2469      715472 :     else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
    2470             :     {
    2471    43240900 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2472             :         {
    2473    42527200 :             if (pszInput[iIn] == '<')
    2474             :             {
    2475        1408 :                 pszOutput[iOut++] = '&';
    2476        1408 :                 pszOutput[iOut++] = 'l';
    2477        1408 :                 pszOutput[iOut++] = 't';
    2478        1408 :                 pszOutput[iOut++] = ';';
    2479             :             }
    2480    42525800 :             else if (pszInput[iIn] == '>')
    2481             :             {
    2482        1534 :                 pszOutput[iOut++] = '&';
    2483        1534 :                 pszOutput[iOut++] = 'g';
    2484        1534 :                 pszOutput[iOut++] = 't';
    2485        1534 :                 pszOutput[iOut++] = ';';
    2486             :             }
    2487    42524300 :             else if (pszInput[iIn] == '&')
    2488             :             {
    2489        1653 :                 pszOutput[iOut++] = '&';
    2490        1653 :                 pszOutput[iOut++] = 'a';
    2491        1653 :                 pszOutput[iOut++] = 'm';
    2492        1653 :                 pszOutput[iOut++] = 'p';
    2493        1653 :                 pszOutput[iOut++] = ';';
    2494             :             }
    2495    42522600 :             else if (pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES)
    2496             :             {
    2497        2700 :                 pszOutput[iOut++] = '&';
    2498        2700 :                 pszOutput[iOut++] = 'q';
    2499        2700 :                 pszOutput[iOut++] = 'u';
    2500        2700 :                 pszOutput[iOut++] = 'o';
    2501        2700 :                 pszOutput[iOut++] = 't';
    2502        2700 :                 pszOutput[iOut++] = ';';
    2503             :             }
    2504             :             // Python 2 does not display the UTF-8 character corresponding
    2505             :             // to the byte-order mark (BOM), so escape it.
    2506    42519900 :             else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] ==
    2507           2 :                          0xEF &&
    2508             :                      (reinterpret_cast<const unsigned char *>(
    2509           2 :                          pszInput))[iIn + 1] == 0xBB &&
    2510             :                      (reinterpret_cast<const unsigned char *>(
    2511           2 :                          pszInput))[iIn + 2] == 0xBF)
    2512             :             {
    2513           2 :                 pszOutput[iOut++] = '&';
    2514           2 :                 pszOutput[iOut++] = '#';
    2515           2 :                 pszOutput[iOut++] = 'x';
    2516           2 :                 pszOutput[iOut++] = 'F';
    2517           2 :                 pszOutput[iOut++] = 'E';
    2518           2 :                 pszOutput[iOut++] = 'F';
    2519           2 :                 pszOutput[iOut++] = 'F';
    2520           2 :                 pszOutput[iOut++] = ';';
    2521           2 :                 iIn += 2;
    2522             :             }
    2523    42519900 :             else if ((reinterpret_cast<const unsigned char *>(pszInput))[iIn] <
    2524       21952 :                          0x20 &&
    2525       21952 :                      pszInput[iIn] != 0x9 && pszInput[iIn] != 0xA &&
    2526         146 :                      pszInput[iIn] != 0xD)
    2527             :             {
    2528             :                 // These control characters are unrepresentable in XML format,
    2529             :                 // so we just drop them.  #4117
    2530             :             }
    2531             :             else
    2532             :             {
    2533    42519900 :                 pszOutput[iOut++] = pszInput[iIn];
    2534             :             }
    2535             :         }
    2536      713690 :         pszOutput[iOut++] = '\0';
    2537             :     }
    2538        1782 :     else if (nScheme == CPLES_URL)  // Untested at implementation.
    2539             :     {
    2540       15538 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2541             :         {
    2542       14889 :             if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
    2543        8031 :                 (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
    2544        3065 :                 (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') ||
    2545        1800 :                 pszInput[iIn] == '$' || pszInput[iIn] == '-' ||
    2546        1712 :                 pszInput[iIn] == '_' || pszInput[iIn] == '.' ||
    2547         698 :                 pszInput[iIn] == '+' || pszInput[iIn] == '!' ||
    2548         676 :                 pszInput[iIn] == '*' || pszInput[iIn] == '\'' ||
    2549         674 :                 pszInput[iIn] == '(' || pszInput[iIn] == ')' ||
    2550         664 :                 pszInput[iIn] == ',')
    2551             :             {
    2552       14231 :                 pszOutput[iOut++] = pszInput[iIn];
    2553             :             }
    2554             :             else
    2555             :             {
    2556         658 :                 snprintf(pszOutput + iOut, nSizeAlloc - iOut, "%%%02X",
    2557         658 :                          static_cast<unsigned char>(pszInput[iIn]));
    2558         658 :                 iOut += 3;
    2559             :             }
    2560             :         }
    2561         649 :         pszOutput[iOut++] = '\0';
    2562             :     }
    2563        1133 :     else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
    2564             :     {
    2565         855 :         const char chQuote = nScheme == CPLES_SQL ? '\'' : '\"';
    2566       12084 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2567             :         {
    2568       11229 :             if (pszInput[iIn] == chQuote)
    2569             :             {
    2570           5 :                 pszOutput[iOut++] = chQuote;
    2571           5 :                 pszOutput[iOut++] = chQuote;
    2572             :             }
    2573             :             else
    2574             :             {
    2575       11224 :                 pszOutput[iOut++] = pszInput[iIn];
    2576             :             }
    2577             :         }
    2578         855 :         pszOutput[iOut++] = '\0';
    2579             :     }
    2580         278 :     else if (nScheme == CPLES_CSV || nScheme == CPLES_CSV_FORCE_QUOTING)
    2581             :     {
    2582         278 :         pszOutput[iOut++] = '\"';
    2583             : 
    2584       13461 :         for (size_t iIn = 0; iIn < szLength; ++iIn)
    2585             :         {
    2586       13183 :             if (pszInput[iIn] == '\"')
    2587             :             {
    2588         169 :                 pszOutput[iOut++] = '\"';
    2589         169 :                 pszOutput[iOut++] = '\"';
    2590             :             }
    2591             :             else
    2592       13014 :                 pszOutput[iOut++] = pszInput[iIn];
    2593             :         }
    2594         278 :         pszOutput[iOut++] = '\"';
    2595         278 :         pszOutput[iOut++] = '\0';
    2596             :     }
    2597             : 
    2598      715665 :     return pszOutput;
    2599             : #undef nLength
    2600             : }
    2601             : 
    2602             : /************************************************************************/
    2603             : /*                         CPLUnescapeString()                          */
    2604             : /************************************************************************/
    2605             : 
    2606             : /**
    2607             :  * Unescape a string.
    2608             :  *
    2609             :  * This function does the opposite of CPLEscapeString().  Given a string
    2610             :  * with special values escaped according to some scheme, it will return a
    2611             :  * new copy of the string returned to its original form.
    2612             :  *
    2613             :  * @param pszInput the input string.  This is a zero terminated string.
    2614             :  * @param pnLength location to return the length of the unescaped string,
    2615             :  * which may in some cases include embedded '\\0' characters.
    2616             :  * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a
    2617             :  * list).  Does not yet support CSV.
    2618             :  *
    2619             :  * @return a copy of the unescaped string that should be freed by the
    2620             :  * application using CPLFree() when no longer needed.
    2621             :  */
    2622             : 
    2623             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    2624       40409 : char *CPLUnescapeString(const char *pszInput, int *pnLength, int nScheme)
    2625             : 
    2626             : {
    2627       40409 :     int iOut = 0;
    2628             : 
    2629             :     // TODO: Why times 4?
    2630       40409 :     char *pszOutput = static_cast<char *>(CPLMalloc(4 * strlen(pszInput) + 1));
    2631       40409 :     pszOutput[0] = '\0';
    2632             : 
    2633       40409 :     if (nScheme == CPLES_BackslashQuotable)
    2634             :     {
    2635       58939 :         for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
    2636             :         {
    2637       58378 :             if (pszInput[iIn] == '\\')
    2638             :             {
    2639         975 :                 ++iIn;
    2640         975 :                 if (pszInput[iIn] == '\0')
    2641           0 :                     break;
    2642         975 :                 if (pszInput[iIn] == 'n')
    2643           6 :                     pszOutput[iOut++] = '\n';
    2644         969 :                 else if (pszInput[iIn] == '0')
    2645         881 :                     pszOutput[iOut++] = '\0';
    2646             :                 else
    2647          88 :                     pszOutput[iOut++] = pszInput[iIn];
    2648             :             }
    2649             :             else
    2650             :             {
    2651       57403 :                 pszOutput[iOut++] = pszInput[iIn];
    2652             :             }
    2653             :         }
    2654             :     }
    2655       39848 :     else if (nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES)
    2656             :     {
    2657       38988 :         char ch = '\0';
    2658    33785500 :         for (int iIn = 0; (ch = pszInput[iIn]) != '\0'; ++iIn)
    2659             :         {
    2660    33746500 :             if (ch != '&')
    2661             :             {
    2662    33380500 :                 pszOutput[iOut++] = ch;
    2663             :             }
    2664      366040 :             else if (STARTS_WITH_CI(pszInput + iIn, "&lt;"))
    2665             :             {
    2666        5048 :                 pszOutput[iOut++] = '<';
    2667        5048 :                 iIn += 3;
    2668             :             }
    2669      360992 :             else if (STARTS_WITH_CI(pszInput + iIn, "&gt;"))
    2670             :             {
    2671        5176 :                 pszOutput[iOut++] = '>';
    2672        5176 :                 iIn += 3;
    2673             :             }
    2674      355816 :             else if (STARTS_WITH_CI(pszInput + iIn, "&amp;"))
    2675             :             {
    2676      257595 :                 pszOutput[iOut++] = '&';
    2677      257595 :                 iIn += 4;
    2678             :             }
    2679       98221 :             else if (STARTS_WITH_CI(pszInput + iIn, "&apos;"))
    2680             :             {
    2681         686 :                 pszOutput[iOut++] = '\'';
    2682         686 :                 iIn += 5;
    2683             :             }
    2684       97535 :             else if (STARTS_WITH_CI(pszInput + iIn, "&quot;"))
    2685             :             {
    2686       97369 :                 pszOutput[iOut++] = '"';
    2687       97369 :                 iIn += 5;
    2688             :             }
    2689         166 :             else if (STARTS_WITH_CI(pszInput + iIn, "&#x"))
    2690             :             {
    2691           4 :                 wchar_t anVal[2] = {0, 0};
    2692           4 :                 iIn += 3;
    2693             : 
    2694           4 :                 unsigned int nVal = 0;
    2695             :                 while (true)
    2696             :                 {
    2697          10 :                     ch = pszInput[iIn++];
    2698          10 :                     if (ch >= 'a' && ch <= 'f')
    2699           1 :                         nVal = nVal * 16U +
    2700             :                                static_cast<unsigned int>(ch - 'a' + 10);
    2701           9 :                     else if (ch >= 'A' && ch <= 'F')
    2702           1 :                         nVal = nVal * 16U +
    2703             :                                static_cast<unsigned int>(ch - 'A' + 10);
    2704           8 :                     else if (ch >= '0' && ch <= '9')
    2705           4 :                         nVal = nVal * 16U + static_cast<unsigned int>(ch - '0');
    2706             :                     else
    2707             :                         break;
    2708             :                 }
    2709           4 :                 anVal[0] = static_cast<wchar_t>(nVal);
    2710           4 :                 if (ch != ';')
    2711           1 :                     break;
    2712           3 :                 iIn--;
    2713             : 
    2714             :                 char *pszUTF8 =
    2715           3 :                     CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8);
    2716           3 :                 int nLen = static_cast<int>(strlen(pszUTF8));
    2717           3 :                 memcpy(pszOutput + iOut, pszUTF8, nLen);
    2718           3 :                 CPLFree(pszUTF8);
    2719           3 :                 iOut += nLen;
    2720             :             }
    2721         162 :             else if (STARTS_WITH_CI(pszInput + iIn, "&#"))
    2722             :             {
    2723         159 :                 wchar_t anVal[2] = {0, 0};
    2724         159 :                 iIn += 2;
    2725             : 
    2726         159 :                 unsigned int nVal = 0;
    2727             :                 while (true)
    2728             :                 {
    2729         646 :                     ch = pszInput[iIn++];
    2730         646 :                     if (ch >= '0' && ch <= '9')
    2731         487 :                         nVal = nVal * 10U + static_cast<unsigned int>(ch - '0');
    2732             :                     else
    2733             :                         break;
    2734             :                 }
    2735         159 :                 anVal[0] = static_cast<wchar_t>(nVal);
    2736         159 :                 if (ch != ';')
    2737           1 :                     break;
    2738         158 :                 iIn--;
    2739             : 
    2740             :                 char *pszUTF8 =
    2741         158 :                     CPLRecodeFromWChar(anVal, "WCHAR_T", CPL_ENC_UTF8);
    2742         158 :                 const int nLen = static_cast<int>(strlen(pszUTF8));
    2743         158 :                 memcpy(pszOutput + iOut, pszUTF8, nLen);
    2744         158 :                 CPLFree(pszUTF8);
    2745         158 :                 iOut += nLen;
    2746             :             }
    2747             :             else
    2748             :             {
    2749             :                 // Illegal escape sequence.
    2750           3 :                 CPLDebug("CPL",
    2751             :                          "Error unescaping CPLES_XML text, '&' character "
    2752             :                          "followed by unhandled escape sequence.");
    2753           2 :                 break;
    2754             :             }
    2755       38987 :         }
    2756             :     }
    2757         860 :     else if (nScheme == CPLES_URL)
    2758             :     {
    2759       41323 :         for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
    2760             :         {
    2761       40508 :             if (pszInput[iIn] == '%' && pszInput[iIn + 1] != '\0' &&
    2762        1144 :                 pszInput[iIn + 2] != '\0')
    2763             :             {
    2764        1144 :                 int nHexChar = 0;
    2765             : 
    2766        1144 :                 if (pszInput[iIn + 1] >= 'A' && pszInput[iIn + 1] <= 'F')
    2767           0 :                     nHexChar += 16 * (pszInput[iIn + 1] - 'A' + 10);
    2768        1144 :                 else if (pszInput[iIn + 1] >= 'a' && pszInput[iIn + 1] <= 'f')
    2769           0 :                     nHexChar += 16 * (pszInput[iIn + 1] - 'a' + 10);
    2770        1144 :                 else if (pszInput[iIn + 1] >= '0' && pszInput[iIn + 1] <= '9')
    2771        1144 :                     nHexChar += 16 * (pszInput[iIn + 1] - '0');
    2772             :                 else
    2773           0 :                     CPLDebug("CPL",
    2774             :                              "Error unescaping CPLES_URL text, percent not "
    2775             :                              "followed by two hex digits.");
    2776             : 
    2777        1144 :                 if (pszInput[iIn + 2] >= 'A' && pszInput[iIn + 2] <= 'F')
    2778        1120 :                     nHexChar += pszInput[iIn + 2] - 'A' + 10;
    2779          24 :                 else if (pszInput[iIn + 2] >= 'a' && pszInput[iIn + 2] <= 'f')
    2780           0 :                     nHexChar += pszInput[iIn + 2] - 'a' + 10;
    2781          24 :                 else if (pszInput[iIn + 2] >= '0' && pszInput[iIn + 2] <= '9')
    2782          24 :                     nHexChar += pszInput[iIn + 2] - '0';
    2783             :                 else
    2784           0 :                     CPLDebug("CPL",
    2785             :                              "Error unescaping CPLES_URL text, percent not "
    2786             :                              "followed by two hex digits.");
    2787             : 
    2788        1144 :                 pszOutput[iOut++] = static_cast<char>(nHexChar);
    2789        1144 :                 iIn += 2;
    2790             :             }
    2791       39364 :             else if (pszInput[iIn] == '+')
    2792             :             {
    2793           0 :                 pszOutput[iOut++] = ' ';
    2794             :             }
    2795             :             else
    2796             :             {
    2797       39364 :                 pszOutput[iOut++] = pszInput[iIn];
    2798             :             }
    2799             :         }
    2800             :     }
    2801          45 :     else if (nScheme == CPLES_SQL || nScheme == CPLES_SQLI)
    2802             :     {
    2803          45 :         char szQuote = nScheme == CPLES_SQL ? '\'' : '\"';
    2804         565 :         for (int iIn = 0; pszInput[iIn] != '\0'; ++iIn)
    2805             :         {
    2806         520 :             if (pszInput[iIn] == szQuote && pszInput[iIn + 1] == szQuote)
    2807             :             {
    2808           3 :                 ++iIn;
    2809           3 :                 pszOutput[iOut++] = pszInput[iIn];
    2810             :             }
    2811             :             else
    2812             :             {
    2813         517 :                 pszOutput[iOut++] = pszInput[iIn];
    2814             :             }
    2815          45 :         }
    2816             :     }
    2817           0 :     else if (nScheme == CPLES_CSV)
    2818             :     {
    2819           0 :         CPLError(CE_Fatal, CPLE_NotSupported,
    2820             :                  "CSV Unescaping not yet implemented.");
    2821             :     }
    2822             :     else
    2823             :     {
    2824           0 :         CPLError(CE_Fatal, CPLE_NotSupported, "Unknown escaping style.");
    2825             :     }
    2826             : 
    2827       40408 :     pszOutput[iOut] = '\0';
    2828             : 
    2829       40408 :     if (pnLength != nullptr)
    2830       24138 :         *pnLength = iOut;
    2831             : 
    2832       40408 :     return pszOutput;
    2833             : }
    2834             : 
    2835             : /************************************************************************/
    2836             : /*                           CPLBinaryToHex()                           */
    2837             : /************************************************************************/
    2838             : 
    2839             : /**
    2840             :  * Binary to hexadecimal translation.
    2841             :  *
    2842             :  * @param nBytes number of bytes of binary data in pabyData.
    2843             :  * @param pabyData array of data bytes to translate.
    2844             :  *
    2845             :  * @return hexadecimal translation, zero terminated.  Free with CPLFree().
    2846             :  */
    2847             : 
    2848        4313 : char *CPLBinaryToHex(int nBytes, const GByte *pabyData)
    2849             : 
    2850             : {
    2851        4313 :     CPLAssert(nBytes >= 0);
    2852             :     char *pszHex = static_cast<char *>(
    2853        4313 :         VSI_MALLOC_VERBOSE(static_cast<size_t>(nBytes) * 2 + 1));
    2854        4313 :     if (!pszHex)
    2855             :     {
    2856           0 :         pszHex = CPLStrdup("");
    2857           0 :         return pszHex;
    2858             :     }
    2859        4313 :     pszHex[nBytes * 2] = '\0';
    2860             : 
    2861        4313 :     constexpr char achHex[] = "0123456789ABCDEF";
    2862             : 
    2863      261289 :     for (size_t i = 0; i < static_cast<size_t>(nBytes); ++i)
    2864             :     {
    2865      256976 :         const int nLow = pabyData[i] & 0x0f;
    2866      256976 :         const int nHigh = (pabyData[i] & 0xf0) >> 4;
    2867             : 
    2868      256976 :         pszHex[i * 2] = achHex[nHigh];
    2869      256976 :         pszHex[i * 2 + 1] = achHex[nLow];
    2870             :     }
    2871             : 
    2872        4313 :     return pszHex;
    2873             : }
    2874             : 
    2875             : /************************************************************************/
    2876             : /*                           CPLHexToBinary()                           */
    2877             : /************************************************************************/
    2878             : 
    2879             : constexpr unsigned char hex2char[256] = {
    2880             :     // Not Hex characters.
    2881             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2882             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2883             :     // 0-9
    2884             :     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
    2885             :     // A-F
    2886             :     0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2887             :     // Not Hex characters.
    2888             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2889             :     // a-f
    2890             :     0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2891             :     0, 0, 0, 0, 0, 0, 0, 0, 0,
    2892             :     // Not Hex characters (upper 128 characters).
    2893             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2894             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2895             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2896             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2897             :     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    2898             :     0, 0, 0};
    2899             : 
    2900             : /**
    2901             :  * Hexadecimal to binary translation
    2902             :  *
    2903             :  * @param pszHex the input hex encoded string.
    2904             :  * @param pnBytes the returned count of decoded bytes placed here.
    2905             :  *
    2906             :  * @return returns binary buffer of data - free with CPLFree().
    2907             :  */
    2908             : 
    2909        3990 : GByte *CPLHexToBinary(const char *pszHex, int *pnBytes)
    2910             : {
    2911        3990 :     const GByte *pabyHex = reinterpret_cast<const GByte *>(pszHex);
    2912        3990 :     const size_t nHexLen = strlen(pszHex);
    2913             : 
    2914        3990 :     GByte *pabyWKB = static_cast<GByte *>(CPLMalloc(nHexLen / 2 + 2));
    2915             : 
    2916     1051740 :     for (size_t i = 0; i < nHexLen / 2; ++i)
    2917             :     {
    2918     1047750 :         const unsigned char h1 = hex2char[pabyHex[2 * i]];
    2919     1047750 :         const unsigned char h2 = hex2char[pabyHex[2 * i + 1]];
    2920             : 
    2921             :         // First character is high bits, second is low bits.
    2922     1047750 :         pabyWKB[i] = static_cast<GByte>((h1 << 4) | h2);
    2923             :     }
    2924        3990 :     pabyWKB[nHexLen / 2] = 0;
    2925        3990 :     *pnBytes = static_cast<int>(nHexLen / 2);
    2926             : 
    2927        3990 :     return pabyWKB;
    2928             : }
    2929             : 
    2930             : /************************************************************************/
    2931             : /*                          CPLGetValueType()                           */
    2932             : /************************************************************************/
    2933             : 
    2934             : /**
    2935             :  * Detect the type of the value contained in a string, whether it is
    2936             :  * a real, an integer or a string
    2937             :  * Leading and trailing spaces are skipped in the analysis.
    2938             :  *
    2939             :  * Note: in the context of this function, integer must be understood in a
    2940             :  * broad sense. It does not mean that the value can fit into a 32 bit integer
    2941             :  * for example. It might be larger.
    2942             :  *
    2943             :  * @param pszValue the string to analyze
    2944             :  *
    2945             :  * @return returns the type of the value contained in the string.
    2946             :  */
    2947             : 
    2948      167189 : CPLValueType CPLGetValueType(const char *pszValue)
    2949             : {
    2950             :     // Doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 "
    2951             :     // Not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d", "d1"
    2952             :     //              "XXeYYYYYYYYYYYYYYYYYYY" that evaluates to infinity
    2953             : 
    2954      167189 :     if (pszValue == nullptr)
    2955           0 :         return CPL_VALUE_STRING;
    2956             : 
    2957      167189 :     const char *pszValueInit = pszValue;
    2958             : 
    2959             :     // Skip leading spaces.
    2960      167240 :     while (isspace(static_cast<unsigned char>(*pszValue)))
    2961          51 :         ++pszValue;
    2962             : 
    2963      167189 :     if (*pszValue == '\0')
    2964         392 :         return CPL_VALUE_STRING;
    2965             : 
    2966             :     // Skip leading + or -.
    2967      166797 :     if (*pszValue == '+' || *pszValue == '-')
    2968       11355 :         ++pszValue;
    2969             : 
    2970      166797 :     constexpr char DIGIT_ZERO = '0';
    2971      166797 :     if (pszValue[0] == DIGIT_ZERO && pszValue[1] != '\0' && pszValue[1] != '.')
    2972        1122 :         return CPL_VALUE_STRING;
    2973             : 
    2974      165675 :     bool bFoundDot = false;
    2975      165675 :     bool bFoundExponent = false;
    2976      165675 :     bool bIsLastCharExponent = false;
    2977      165675 :     bool bIsReal = false;
    2978      165675 :     const char *pszAfterExponent = nullptr;
    2979      165675 :     bool bFoundMantissa = false;
    2980             : 
    2981      575465 :     for (; *pszValue != '\0'; ++pszValue)
    2982             :     {
    2983      462865 :         if (isdigit(static_cast<unsigned char>(*pszValue)))
    2984             :         {
    2985      393690 :             bIsLastCharExponent = false;
    2986      393690 :             bFoundMantissa = true;
    2987             :         }
    2988       69175 :         else if (isspace(static_cast<unsigned char>(*pszValue)))
    2989             :         {
    2990         831 :             const char *pszTmp = pszValue;
    2991        1666 :             while (isspace(static_cast<unsigned char>(*pszTmp)))
    2992         835 :                 ++pszTmp;
    2993         831 :             if (*pszTmp == 0)
    2994          24 :                 break;
    2995             :             else
    2996         807 :                 return CPL_VALUE_STRING;
    2997             :         }
    2998       68344 :         else if (*pszValue == '-' || *pszValue == '+')
    2999             :         {
    3000         628 :             if (bIsLastCharExponent)
    3001             :             {
    3002             :                 // Do nothing.
    3003             :             }
    3004             :             else
    3005             :             {
    3006         365 :                 return CPL_VALUE_STRING;
    3007             :             }
    3008         263 :             bIsLastCharExponent = false;
    3009             :         }
    3010       67716 :         else if (*pszValue == '.')
    3011             :         {
    3012       15585 :             bIsReal = true;
    3013       15585 :             if (!bFoundDot && !bIsLastCharExponent)
    3014       15567 :                 bFoundDot = true;
    3015             :             else
    3016          18 :                 return CPL_VALUE_STRING;
    3017       15567 :             bIsLastCharExponent = false;
    3018             :         }
    3019       52131 :         else if (*pszValue == 'D' || *pszValue == 'd' || *pszValue == 'E' ||
    3020       47041 :                  *pszValue == 'e')
    3021             :         {
    3022        5367 :             if (!bFoundMantissa)
    3023        5094 :                 return CPL_VALUE_STRING;
    3024         273 :             if (!(pszValue[1] == '+' || pszValue[1] == '-' ||
    3025          10 :                   isdigit(static_cast<unsigned char>(pszValue[1]))))
    3026           2 :                 return CPL_VALUE_STRING;
    3027             : 
    3028         271 :             bIsReal = true;
    3029         271 :             if (!bFoundExponent)
    3030         270 :                 bFoundExponent = true;
    3031             :             else
    3032           1 :                 return CPL_VALUE_STRING;
    3033         270 :             pszAfterExponent = pszValue + 1;
    3034         270 :             bIsLastCharExponent = true;
    3035             :         }
    3036             :         else
    3037             :         {
    3038       46764 :             return CPL_VALUE_STRING;
    3039             :         }
    3040             :     }
    3041             : 
    3042      112624 :     if (bIsReal && pszAfterExponent && strlen(pszAfterExponent) > 3)
    3043             :     {
    3044             :         // cppcheck-suppress unreadVariable
    3045          15 :         const double dfVal = CPLAtof(pszValueInit);
    3046          15 :         if (std::isinf(dfVal))
    3047           1 :             return CPL_VALUE_STRING;
    3048             :     }
    3049             : 
    3050      112614 :     return bIsReal ? CPL_VALUE_REAL : CPL_VALUE_INTEGER;
    3051             : }
    3052             : 
    3053             : /************************************************************************/
    3054             : /*                             CPLStrlcpy()                             */
    3055             : /************************************************************************/
    3056             : 
    3057             : /**
    3058             :  * Copy source string to a destination buffer.
    3059             :  *
    3060             :  * This function ensures that the destination buffer is always NUL terminated
    3061             :  * (provided that its length is at least 1).
    3062             :  *
    3063             :  * This function is designed to be a safer, more consistent, and less error
    3064             :  * prone replacement for strncpy. Its contract is identical to libbsd's strlcpy.
    3065             :  *
    3066             :  * Truncation can be detected by testing if the return value of CPLStrlcpy
    3067             :  * is greater or equal to nDestSize.
    3068             : 
    3069             : \verbatim
    3070             : char szDest[5] = {};
    3071             : if( CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest) )
    3072             :     fprintf(stderr, "truncation occurred !\n");
    3073             : \endverbatim
    3074             : 
    3075             :  * @param pszDest   destination buffer
    3076             :  * @param pszSrc    source string. Must be NUL terminated
    3077             :  * @param nDestSize size of destination buffer (including space for the NUL
    3078             :  *     terminator character)
    3079             :  *
    3080             :  * @return the length of the source string (=strlen(pszSrc))
    3081             :  *
    3082             :  */
    3083       90262 : size_t CPLStrlcpy(char *pszDest, const char *pszSrc, size_t nDestSize)
    3084             : {
    3085       90262 :     if (nDestSize == 0)
    3086           0 :         return strlen(pszSrc);
    3087             : 
    3088       90262 :     char *pszDestIter = pszDest;
    3089       90262 :     const char *pszSrcIter = pszSrc;
    3090             : 
    3091       90262 :     --nDestSize;
    3092      883679 :     while (nDestSize != 0 && *pszSrcIter != '\0')
    3093             :     {
    3094      793417 :         *pszDestIter = *pszSrcIter;
    3095      793417 :         ++pszDestIter;
    3096      793417 :         ++pszSrcIter;
    3097      793417 :         --nDestSize;
    3098             :     }
    3099       90262 :     *pszDestIter = '\0';
    3100       90262 :     return pszSrcIter - pszSrc + strlen(pszSrcIter);
    3101             : }
    3102             : 
    3103             : /************************************************************************/
    3104             : /*                             CPLStrlcat()                             */
    3105             : /************************************************************************/
    3106             : 
    3107             : /**
    3108             :  * Appends a source string to a destination buffer.
    3109             :  *
    3110             :  * This function ensures that the destination buffer is always NUL terminated
    3111             :  * (provided that its length is at least 1 and that there is at least one byte
    3112             :  * free in pszDest, that is to say strlen(pszDest_before) < nDestSize)
    3113             :  *
    3114             :  * This function is designed to be a safer, more consistent, and less error
    3115             :  * prone replacement for strncat. Its contract is identical to libbsd's strlcat.
    3116             :  *
    3117             :  * Truncation can be detected by testing if the return value of CPLStrlcat
    3118             :  * is greater or equal to nDestSize.
    3119             : 
    3120             : \verbatim
    3121             : char szDest[5] = {};
    3122             : CPLStrlcpy(szDest, "ab", sizeof(szDest));
    3123             : if( CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest) )
    3124             :     fprintf(stderr, "truncation occurred !\n");
    3125             : \endverbatim
    3126             : 
    3127             :  * @param pszDest   destination buffer. Must be NUL terminated before
    3128             :  *         running CPLStrlcat
    3129             :  * @param pszSrc    source string. Must be NUL terminated
    3130             :  * @param nDestSize size of destination buffer (including space for the
    3131             :  *         NUL terminator character)
    3132             :  *
    3133             :  * @return the theoretical length of the destination string after concatenation
    3134             :  *         (=strlen(pszDest_before) + strlen(pszSrc)).
    3135             :  *         If strlen(pszDest_before) >= nDestSize, then it returns
    3136             :  *         nDestSize + strlen(pszSrc)
    3137             :  *
    3138             :  */
    3139         753 : size_t CPLStrlcat(char *pszDest, const char *pszSrc, size_t nDestSize)
    3140             : {
    3141         753 :     char *pszDestIter = pszDest;
    3142             : 
    3143       55921 :     while (nDestSize != 0 && *pszDestIter != '\0')
    3144             :     {
    3145       55168 :         ++pszDestIter;
    3146       55168 :         --nDestSize;
    3147             :     }
    3148             : 
    3149         753 :     return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize);
    3150             : }
    3151             : 
    3152             : /************************************************************************/
    3153             : /*                             CPLStrnlen()                             */
    3154             : /************************************************************************/
    3155             : 
    3156             : /**
    3157             :  * Returns the length of a NUL terminated string by reading at most
    3158             :  * the specified number of bytes.
    3159             :  *
    3160             :  * The CPLStrnlen() function returns min(strlen(pszStr), nMaxLen).
    3161             :  * Only the first nMaxLen bytes of the string will be read. Useful to
    3162             :  * test if a string contains at least nMaxLen characters without reading
    3163             :  * the full string up to the NUL terminating character.
    3164             :  *
    3165             :  * @param pszStr    a NUL terminated string
    3166             :  * @param nMaxLen   maximum number of bytes to read in pszStr
    3167             :  *
    3168             :  * @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise
    3169             :  * nMaxLen if the NUL character has not been found in the first nMaxLen bytes.
    3170             :  *
    3171             :  */
    3172             : 
    3173      525920 : size_t CPLStrnlen(const char *pszStr, size_t nMaxLen)
    3174             : {
    3175      525920 :     size_t nLen = 0;
    3176    29267100 :     while (nLen < nMaxLen && *pszStr != '\0')
    3177             :     {
    3178    28741200 :         ++nLen;
    3179    28741200 :         ++pszStr;
    3180             :     }
    3181      525920 :     return nLen;
    3182             : }
    3183             : 
    3184             : /************************************************************************/
    3185             : /*                        CSLParseCommandLine()                         */
    3186             : /************************************************************************/
    3187             : 
    3188             : /**
    3189             :  * Tokenize command line arguments in a list of strings.
    3190             :  *
    3191             :  * @param pszCommandLine  command line
    3192             :  *
    3193             :  * @return NULL terminated list of strings to free with CSLDestroy()
    3194             :  *
    3195             :  */
    3196        1018 : char **CSLParseCommandLine(const char *pszCommandLine)
    3197             : {
    3198        1018 :     return CSLTokenizeString(pszCommandLine);
    3199             : }
    3200             : 
    3201             : /************************************************************************/
    3202             : /*                             CPLToupper()                             */
    3203             : /************************************************************************/
    3204             : 
    3205             : /** Converts a (ASCII) lowercase character to uppercase.
    3206             :  *
    3207             :  * Same as standard toupper(), except that it is not locale sensitive.
    3208             :  *
    3209             :  * @since GDAL 3.9
    3210             :  */
    3211    29479400 : int CPLToupper(int c)
    3212             : {
    3213    29479400 :     return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
    3214             : }
    3215             : 
    3216             : /************************************************************************/
    3217             : /*                             CPLTolower()                             */
    3218             : /************************************************************************/
    3219             : 
    3220             : /** Converts a (ASCII) uppercase character to lowercase.
    3221             :  *
    3222             :  * Same as standard tolower(), except that it is not locale sensitive.
    3223             :  *
    3224             :  * @since GDAL 3.9
    3225             :  */
    3226    21946100 : int CPLTolower(int c)
    3227             : {
    3228    21946100 :     return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
    3229             : }
    3230             : 
    3231             : /************************************************************************/
    3232             : /*                        CPLRemoveSQLComments()                        */
    3233             : /************************************************************************/
    3234             : 
    3235             : /** Remove SQL comments from a string
    3236             :  *
    3237             :  * @param osInput Input string.
    3238             :  * @since GDAL 3.11
    3239             :  */
    3240          55 : std::string CPLRemoveSQLComments(const std::string &osInput)
    3241             : {
    3242             :     const CPLStringList aosLines(
    3243         110 :         CSLTokenizeStringComplex(osInput.c_str(), "\r\n", FALSE, FALSE));
    3244          55 :     std::string osSQL;
    3245         121 :     for (const char *pszLine : aosLines)
    3246             :     {
    3247          66 :         char chQuote = 0;
    3248          66 :         int i = 0;
    3249        1135 :         for (; pszLine[i] != '\0'; ++i)
    3250             :         {
    3251        1078 :             if (chQuote)
    3252             :             {
    3253          24 :                 if (pszLine[i] == chQuote)
    3254             :                 {
    3255             :                     // Deal with escaped quote character which is repeated,
    3256             :                     // so 'foo''bar' or "foo""bar"
    3257           7 :                     if (pszLine[i + 1] == chQuote)
    3258             :                     {
    3259           2 :                         i++;
    3260             :                     }
    3261             :                     else
    3262             :                     {
    3263           5 :                         chQuote = 0;
    3264             :                     }
    3265             :                 }
    3266             :             }
    3267        1054 :             else if (pszLine[i] == '\'' || pszLine[i] == '"')
    3268             :             {
    3269           5 :                 chQuote = pszLine[i];
    3270             :             }
    3271        1049 :             else if (pszLine[i] == '-' && pszLine[i + 1] == '-')
    3272             :             {
    3273           9 :                 break;
    3274             :             }
    3275             :         }
    3276          66 :         if (i > 0)
    3277             :         {
    3278          59 :             if (!osSQL.empty())
    3279           4 :                 osSQL += ' ';
    3280          59 :             osSQL.append(pszLine, i);
    3281             :         }
    3282             :     }
    3283         110 :     return osSQL;
    3284             : }
    3285             : 
    3286             : namespace cpl
    3287             : {
    3288             : 
    3289    83933500 : static bool CaseInsensitiveCompare(unsigned char c1, unsigned char c2)
    3290             : {
    3291    83933500 :     return toupper(c1) == toupper(c2);
    3292             : }
    3293             : 
    3294             : /** Check whether the start of one string is equivalent to another string,
    3295             :  *  considering case.
    3296             :  *
    3297             :  * @param str string to test
    3298             :  * @param prefix expected prefix
    3299             :  * @return true if the string starts with the prefix
    3300             :  *
    3301             :  * @since GDAL 3.11
    3302             :  */
    3303    17491800 : bool starts_with(std::string_view str, std::string_view prefix)
    3304             : {
    3305    25824000 :     return str.size() >= prefix.size() &&
    3306    25824000 :            str.compare(0, prefix.size(), prefix) == 0;
    3307             : }
    3308             : 
    3309             : /** Check whether the start of one string is equivalent to another string,
    3310             :  *  not considering case.
    3311             :  *
    3312             :  * @param str string to test
    3313             :  * @param prefix expected prefix
    3314             :  * @return true if the string starts with the prefix
    3315             :  *
    3316             :  * @since GDAL 3.14
    3317             :  */
    3318     8761630 : bool starts_with_ci(std::string_view str, std::string_view prefix)
    3319             : {
    3320    13665900 :     return str.size() >= prefix.size() &&
    3321     4904300 :            std::search(str.begin(), str.end(), prefix.begin(), prefix.end(),
    3322    13665900 :                        CaseInsensitiveCompare) != str.end();
    3323             : }
    3324             : 
    3325             : /** Check whether the end of one string is equivalent to another string,
    3326             :  *  considering case.
    3327             :  *
    3328             :  * @param str string to test
    3329             :  * @param suffix expected suffix
    3330             :  * @return true if the string ends with the suffix
    3331             :  *
    3332             :  * @since GDAL 3.11
    3333             :  */
    3334      255956 : bool ends_with(std::string_view str, std::string_view suffix)
    3335             : {
    3336      758428 :     return str.size() >= suffix.size() &&
    3337      502476 :            (suffix.empty() || str.compare(str.size() - suffix.size(),
    3338      255950 :                                           suffix.size(), suffix) == 0);
    3339             : }
    3340             : 
    3341             : /** Check whether the end of one string is equivalent to another string,
    3342             :  *  not considering case.
    3343             :  *
    3344             :  * @param str string to test
    3345             :  * @param suffix expected suffix
    3346             :  * @return true if the string ends with the suffix
    3347             :  *
    3348             :  * @since GDAL 3.14
    3349             :  */
    3350           3 : bool ends_with_ci(std::string_view str, std::string_view suffix)
    3351             : {
    3352           8 :     return str.size() >= suffix.size() &&
    3353           5 :            (suffix.empty() ||
    3354           2 :             std::search(str.end() - suffix.size(), str.end(), suffix.begin(),
    3355           5 :                         suffix.end(), CaseInsensitiveCompare) != str.end());
    3356             : }
    3357             : 
    3358             : /** Check whether two strings are equal, considering case.
    3359             :  *
    3360             :  * @param str1 first string to test
    3361             :  * @param str2 second string to test
    3362             :  * @return true if the strings are considered equal
    3363             :  *
    3364             :  * @since GDAL 3.14
    3365             :  */
    3366           6 : bool equals(std::string_view str1, std::string_view str2)
    3367             : {
    3368           6 :     return str1 == str2;
    3369             : }
    3370             : 
    3371             : /** Check whether two strings are equal, not considering case.
    3372             :  *
    3373             :  * @param str1 first string to test
    3374             :  * @param str2 second string to test
    3375             :  * @return true if the strings are considered equal
    3376             :  *
    3377             :  * @since GDAL 3.14
    3378             :  */
    3379           2 : bool equals_ci(std::string_view str1, std::string_view str2)
    3380             : {
    3381           3 :     return str1.size() == str2.size() &&
    3382           1 :            std::equal(str1.begin(), str1.end(), str2.begin(),
    3383           2 :                       CaseInsensitiveCompare);
    3384             : }
    3385             : 
    3386             : /** Remove leading and trailing whitespace from a string.
    3387             :  *  The returned string view will be a reference into the input.
    3388             :  *
    3389             :  * @param str string to trim
    3390             :  * @return trimmed string
    3391             :  *
    3392             :  * @since GDAL 3.14
    3393             :  */
    3394    15102900 : std::string_view trim(std::string_view str)
    3395             : {
    3396    15102900 :     if (str.empty())
    3397             :     {
    3398       11178 :         return str;
    3399             :     }
    3400             : 
    3401    15091700 :     size_t start = 0;
    3402    30343700 :     while (start < str.size() &&
    3403    15170200 :            isspace(static_cast<unsigned char>(str[start])))
    3404             :     {
    3405       81799 :         start++;
    3406             :     }
    3407             : 
    3408    15091700 :     if (start == str.size())
    3409             :     {
    3410        3254 :         return str.substr(start, 0);
    3411             :     }
    3412             : 
    3413    15088400 :     size_t stop = str.size();
    3414    15102600 :     while (stop > start && isspace(static_cast<unsigned char>(str[stop - 1])))
    3415             :     {
    3416       14208 :         stop--;
    3417             :     }
    3418             : 
    3419    15088400 :     return str.substr(start, stop - start);
    3420             : }
    3421             : 
    3422           1 : std::string_view trim(const char *pszStr)
    3423             : {
    3424           1 :     return trim(std::string_view(pszStr));
    3425             : }
    3426             : 
    3427             : /** Remove leading whitespace from a string.
    3428             :  *  The returned string view will be a reference into the input.
    3429             :  *
    3430             :  * @param str string to trim
    3431             :  * @return trimmed string
    3432             :  *
    3433             :  * @since GDAL 3.14
    3434             :  */
    3435       34812 : std::string_view ltrim(std::string_view str)
    3436             : {
    3437       34812 :     if (str.empty())
    3438             :     {
    3439        5282 :         return str;
    3440             :     }
    3441             : 
    3442       29530 :     size_t start = 0;
    3443       68473 :     while (start < str.size() &&
    3444       34235 :            isspace(static_cast<unsigned char>(str[start])))
    3445             :     {
    3446        4708 :         start++;
    3447             :     }
    3448             : 
    3449       29530 :     return str.substr(start);
    3450             : }
    3451             : 
    3452           1 : std::string_view ltrim(const char *pszStr)
    3453             : {
    3454           1 :     return ltrim(std::string_view(pszStr));
    3455             : }
    3456             : 
    3457             : /** Remove trailing whitespace from a string.
    3458             :  *  The returned string view will be a reference into the input.
    3459             :  *
    3460             :  * @param str string to trim
    3461             :  * @return trimmed string
    3462             :  *
    3463             :  * @since GDAL 3.14
    3464             :  */
    3465       34759 : std::string_view rtrim(std::string_view str)
    3466             : {
    3467       34759 :     if (str.empty())
    3468             :     {
    3469        5284 :         return str;
    3470             :     }
    3471             : 
    3472       29475 :     size_t stop = str.size();
    3473       29526 :     while (stop > 0 && isspace(static_cast<unsigned char>(str[stop - 1])))
    3474             :     {
    3475          51 :         stop--;
    3476             :     }
    3477             : 
    3478       29475 :     return str.substr(0, stop);
    3479             : }
    3480             : 
    3481           1 : std::string_view rtrim(const char *pszStr)
    3482             : {
    3483           1 :     return rtrim(std::string_view(pszStr));
    3484             : }
    3485             : 
    3486             : }  // namespace cpl

Generated by: LCOV version 1.14