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

Generated by: LCOV version 1.14