LCOV - code coverage report
Current view: top level - port - cpl_string.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 986 1108 89.0 %
Date: 2026-06-01 01:54:01 Functions: 54 57 94.7 %

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

Generated by: LCOV version 1.14