LCOV - code coverage report
Current view: top level - port - cpl_conv.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 890 1126 79.0 %
Date: 2026-08-21 03:01:58 Functions: 83 100 83.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Convenience functions.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, Frank Warmerdam
       9             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_config.h"
      15             : 
      16             : #if defined(HAVE_USELOCALE) && !defined(__FreeBSD__)
      17             : // For uselocale, define _XOPEN_SOURCE = 700
      18             : // and OpenBSD with libcxx 19.1.7 requires 800 for vasprintf
      19             : // (cf https://github.com/OSGeo/gdal/issues/12619)
      20             : // (not sure if the following is still up to date...) but on Solaris, we don't
      21             : // have uselocale and we cannot have std=c++11 with _XOPEN_SOURCE != 600
      22             : #if defined(__sun__) && __cplusplus >= 201103L
      23             : #if _XOPEN_SOURCE != 600
      24             : #ifdef _XOPEN_SOURCE
      25             : #undef _XOPEN_SOURCE
      26             : #endif
      27             : #define _XOPEN_SOURCE 600
      28             : #endif
      29             : #else
      30             : #ifdef _XOPEN_SOURCE
      31             : #undef _XOPEN_SOURCE
      32             : #endif
      33             : #define _XOPEN_SOURCE 800
      34             : #endif
      35             : #endif
      36             : 
      37             : // For atoll (at least for NetBSD)
      38             : #ifndef _ISOC99_SOURCE
      39             : #define _ISOC99_SOURCE
      40             : #endif
      41             : 
      42             : #ifdef MSVC_USE_VLD
      43             : #include <vld.h>
      44             : #endif
      45             : 
      46             : #include "cpl_conv.h"
      47             : 
      48             : #include <algorithm>
      49             : #include <atomic>
      50             : #include <cctype>
      51             : #include <cerrno>
      52             : #include <charconv>
      53             : #include <climits>
      54             : #include <clocale>
      55             : #include <cmath>
      56             : #include <cstdlib>
      57             : #include <cstring>
      58             : #include <ctime>
      59             : #include <limits>
      60             : #include <mutex>
      61             : #include <set>
      62             : 
      63             : #if HAVE_UNISTD_H
      64             : #include <unistd.h>
      65             : #endif
      66             : #if HAVE_XLOCALE_H
      67             : #include <xlocale.h>  // for LC_NUMERIC_MASK on MacOS
      68             : #endif
      69             : 
      70             : #include <sys/types.h>  // open
      71             : 
      72             : #if defined(__FreeBSD__)
      73             : #include <sys/user.h>  // must be after sys/types.h
      74             : #include <sys/sysctl.h>
      75             : #endif
      76             : 
      77             : #include <sys/stat.h>  // open
      78             : #include <fcntl.h>     // open, fcntl
      79             : 
      80             : #ifdef _WIN32
      81             : #include <io.h>  // _isatty, _wopen
      82             : #else
      83             : #include <unistd.h>  // isatty, fcntl
      84             : #if HAVE_GETRLIMIT
      85             : #include <sys/resource.h>  // getrlimit
      86             : #include <sys/time.h>      // getrlimit
      87             : #endif
      88             : #endif
      89             : 
      90             : #include <string>
      91             : 
      92             : #if __cplusplus >= 202002L
      93             : #include <bit>  // For std::endian
      94             : #endif
      95             : 
      96             : #include "cpl_config.h"
      97             : #include "cpl_multiproc.h"
      98             : #include "cpl_string.h"
      99             : #include "cpl_vsi.h"
     100             : #include "cpl_vsil_curl_priv.h"
     101             : #include "cpl_known_config_options.h"
     102             : 
     103             : #ifdef DEBUG
     104             : #define OGRAPISPY_ENABLED
     105             : #endif
     106             : #ifdef OGRAPISPY_ENABLED
     107             : // Keep in sync with ograpispy.cpp
     108             : void OGRAPISPYCPLSetConfigOption(const char *, const char *);
     109             : void OGRAPISPYCPLSetThreadLocalConfigOption(const char *, const char *);
     110             : #endif
     111             : 
     112             : // Uncomment to get list of options that have been fetched and set.
     113             : // #define DEBUG_CONFIG_OPTIONS
     114             : 
     115             : static CPLMutex *hConfigMutex = nullptr;
     116             : static volatile char **g_papszConfigOptions = nullptr;
     117             : static bool gbIgnoreEnvVariables =
     118             :     false;  // if true, only take into account configuration options set through
     119             :             // configuration file or
     120             :             // CPLSetConfigOption()/CPLSetThreadLocalConfigOption()
     121             : 
     122             : static std::vector<std::pair<CPLSetConfigOptionSubscriber, void *>>
     123             :     gSetConfigOptionSubscribers{};
     124             : 
     125             : // Used by CPLOpenShared() and friends.
     126             : static CPLMutex *hSharedFileMutex = nullptr;
     127             : static int nSharedFileCount = 0;
     128             : static CPLSharedFileInfo *pasSharedFileList = nullptr;
     129             : 
     130             : // Used by CPLsetlocale().
     131             : static CPLMutex *hSetLocaleMutex = nullptr;
     132             : 
     133             : // Note: ideally this should be added in CPLSharedFileInfo*
     134             : // but CPLSharedFileInfo is exposed in the API, hence that trick
     135             : // to hide this detail.
     136             : typedef struct
     137             : {
     138             :     GIntBig nPID;  // pid of opening thread.
     139             : } CPLSharedFileInfoExtra;
     140             : 
     141             : static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = nullptr;
     142             : 
     143             : static const char *
     144             : CPLGetThreadLocalConfigOption(const char *pszKey, const char *pszDefault,
     145             :                               bool bSubstituteNullValueMarkerWithNull);
     146             : 
     147             : static const char *
     148             : CPLGetGlobalConfigOption(const char *pszKey, const char *pszDefault,
     149             :                          bool bSubstituteNullValueMarkerWithNull);
     150             : 
     151             : /************************************************************************/
     152             : /*                             CPLCalloc()                              */
     153             : /************************************************************************/
     154             : 
     155             : /**
     156             :  * Safe version of calloc().
     157             :  *
     158             :  * This function is like the C library calloc(), but raises a CE_Fatal
     159             :  * error with CPLError() if it fails to allocate the desired memory.  It
     160             :  * should be used for small memory allocations that are unlikely to fail
     161             :  * and for which the application is unwilling to test for out of memory
     162             :  * conditions.  It uses VSICalloc() to get the memory, so any hooking of
     163             :  * VSICalloc() will apply to CPLCalloc() as well.  CPLFree() or VSIFree()
     164             :  * can be used free memory allocated by CPLCalloc().
     165             :  *
     166             :  * @param nCount number of objects to allocate.
     167             :  * @param nSize size (in bytes) of object to allocate.
     168             :  * @return pointer to newly allocated memory, only NULL if nSize * nCount is
     169             :  * NULL.
     170             :  */
     171             : 
     172     3580710 : void *CPLCalloc(size_t nCount, size_t nSize)
     173             : 
     174             : {
     175     3580710 :     if (nSize * nCount == 0)
     176        9182 :         return nullptr;
     177             : 
     178     3571530 :     void *pReturn = CPLMalloc(nCount * nSize);
     179     3571530 :     memset(pReturn, 0, nCount * nSize);
     180     3571530 :     return pReturn;
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /*                             CPLMalloc()                              */
     185             : /************************************************************************/
     186             : 
     187             : /**
     188             :  * Safe version of malloc().
     189             :  *
     190             :  * This function is like the C library malloc(), but raises a CE_Fatal
     191             :  * error with CPLError() if it fails to allocate the desired memory.  It
     192             :  * should be used for small memory allocations that are unlikely to fail
     193             :  * and for which the application is unwilling to test for out of memory
     194             :  * conditions.  It uses VSIMalloc() to get the memory, so any hooking of
     195             :  * VSIMalloc() will apply to CPLMalloc() as well.  CPLFree() or VSIFree()
     196             :  * can be used free memory allocated by CPLMalloc().
     197             :  *
     198             :  * @param nSize size (in bytes) of memory block to allocate.
     199             :  * @return pointer to newly allocated memory, only NULL if nSize is zero.
     200             :  */
     201             : 
     202    23701200 : void *CPLMalloc(size_t nSize)
     203             : 
     204             : {
     205    23701200 :     if (nSize == 0)
     206        6097 :         return nullptr;
     207             : 
     208    23695100 :     if ((nSize >> (8 * sizeof(nSize) - 1)) != 0)
     209             :     {
     210             :         // coverity[dead_error_begin]
     211           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     212             :                  "CPLMalloc(%ld): Silly size requested.",
     213             :                  static_cast<long>(nSize));
     214           0 :         return nullptr;
     215             :     }
     216             : 
     217    23695100 :     void *pReturn = VSIMalloc(nSize);
     218    23695000 :     if (pReturn == nullptr)
     219             :     {
     220           0 :         if (nSize < 2000)
     221             :         {
     222           0 :             CPLEmergencyError("CPLMalloc(): Out of memory allocating a small "
     223             :                               "number of bytes.");
     224             :         }
     225             : 
     226           0 :         CPLError(CE_Fatal, CPLE_OutOfMemory,
     227             :                  "CPLMalloc(): Out of memory allocating %ld bytes.",
     228             :                  static_cast<long>(nSize));
     229             :     }
     230             : 
     231    23695000 :     return pReturn;
     232             : }
     233             : 
     234             : /************************************************************************/
     235             : /*                             CPLRealloc()                             */
     236             : /************************************************************************/
     237             : 
     238             : /**
     239             :  * Safe version of realloc().
     240             :  *
     241             :  * This function is like the C library realloc(), but raises a CE_Fatal
     242             :  * error with CPLError() if it fails to allocate the desired memory.  It
     243             :  * should be used for small memory allocations that are unlikely to fail
     244             :  * and for which the application is unwilling to test for out of memory
     245             :  * conditions.  It uses VSIRealloc() to get the memory, so any hooking of
     246             :  * VSIRealloc() will apply to CPLRealloc() as well.  CPLFree() or VSIFree()
     247             :  * can be used free memory allocated by CPLRealloc().
     248             :  *
     249             :  * It is also safe to pass NULL in as the existing memory block for
     250             :  * CPLRealloc(), in which case it uses VSIMalloc() to allocate a new block.
     251             :  *
     252             :  * @param pData existing memory block which should be copied to the new block.
     253             :  * @param nNewSize new size (in bytes) of memory block to allocate.
     254             :  * @return pointer to allocated memory, only NULL if nNewSize is zero.
     255             :  */
     256             : 
     257     4310490 : void *CPLRealloc(void *pData, size_t nNewSize)
     258             : 
     259             : {
     260     4310490 :     if (nNewSize == 0)
     261             :     {
     262          45 :         VSIFree(pData);
     263          45 :         return nullptr;
     264             :     }
     265             : 
     266     4310450 :     if ((nNewSize >> (8 * sizeof(nNewSize) - 1)) != 0)
     267             :     {
     268             :         // coverity[dead_error_begin]
     269           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     270             :                  "CPLRealloc(%ld): Silly size requested.",
     271             :                  static_cast<long>(nNewSize));
     272           0 :         return nullptr;
     273             :     }
     274             : 
     275     4310450 :     void *pReturn = nullptr;
     276             : 
     277     4310450 :     if (pData == nullptr)
     278     3173570 :         pReturn = VSIMalloc(nNewSize);
     279             :     else
     280     1136880 :         pReturn = VSIRealloc(pData, nNewSize);
     281             : 
     282     4310740 :     if (pReturn == nullptr)
     283             :     {
     284           0 :         if (nNewSize < 2000)
     285             :         {
     286           0 :             char szSmallMsg[80] = {};
     287             : 
     288           0 :             snprintf(szSmallMsg, sizeof(szSmallMsg),
     289             :                      "CPLRealloc(): Out of memory allocating %ld bytes.",
     290             :                      static_cast<long>(nNewSize));
     291           0 :             CPLEmergencyError(szSmallMsg);
     292             :         }
     293             :         else
     294             :         {
     295           0 :             CPLError(CE_Fatal, CPLE_OutOfMemory,
     296             :                      "CPLRealloc(): Out of memory allocating %ld bytes.",
     297             :                      static_cast<long>(nNewSize));
     298             :         }
     299             :     }
     300             : 
     301     4311320 :     return pReturn;
     302             : }
     303             : 
     304             : /************************************************************************/
     305             : /*                             CPLStrdup()                              */
     306             : /************************************************************************/
     307             : 
     308             : /**
     309             :  * Safe version of strdup() function.
     310             :  *
     311             :  * This function is similar to the C library strdup() function, but if
     312             :  * the memory allocation fails it will issue a CE_Fatal error with
     313             :  * CPLError() instead of returning NULL. Memory
     314             :  * allocated with CPLStrdup() can be freed with CPLFree() or VSIFree().
     315             :  *
     316             :  * It is also safe to pass a NULL string into CPLStrdup().  CPLStrdup()
     317             :  * will allocate and return a zero length string (as opposed to a NULL
     318             :  * string).
     319             :  *
     320             :  * @param pszString input string to be duplicated.  May be NULL.
     321             :  * @return pointer to a newly allocated copy of the string.  Free with
     322             :  * CPLFree() or VSIFree().
     323             :  */
     324             : 
     325     8698240 : char *CPLStrdup(const char *pszString)
     326             : 
     327             : {
     328     8698240 :     if (pszString == nullptr)
     329     1308710 :         pszString = "";
     330             : 
     331     8698240 :     const size_t nLen = strlen(pszString);
     332     8698240 :     char *pszReturn = static_cast<char *>(CPLMalloc(nLen + 1));
     333     8698220 :     memcpy(pszReturn, pszString, nLen + 1);
     334     8698220 :     return (pszReturn);
     335             : }
     336             : 
     337             : /************************************************************************/
     338             : /*                             CPLStrlwr()                              */
     339             : /************************************************************************/
     340             : 
     341             : /**
     342             :  * Convert each characters of the string to lower case.
     343             :  *
     344             :  * For example, "ABcdE" will be converted to "abcde".
     345             :  * Starting with GDAL 3.9, this function is no longer locale dependent.
     346             :  *
     347             :  * @param pszString input string to be converted.
     348             :  * @return pointer to the same string, pszString.
     349             :  */
     350             : 
     351           3 : char *CPLStrlwr(char *pszString)
     352             : 
     353             : {
     354           3 :     if (pszString == nullptr)
     355           0 :         return nullptr;
     356             : 
     357           3 :     char *pszTemp = pszString;
     358             : 
     359          24 :     while (*pszTemp)
     360             :     {
     361          21 :         *pszTemp =
     362          21 :             static_cast<char>(CPLTolower(static_cast<unsigned char>(*pszTemp)));
     363          21 :         pszTemp++;
     364             :     }
     365             : 
     366           3 :     return pszString;
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                              CPLFGets()                              */
     371             : /*                                                                      */
     372             : /*      Note: LF = \n = ASCII 10                                        */
     373             : /*            CR = \r = ASCII 13                                        */
     374             : /************************************************************************/
     375             : 
     376             : // ASCII characters.
     377             : constexpr char knLF = 10;
     378             : constexpr char knCR = 13;
     379             : 
     380             : /**
     381             :  * Reads in at most one less than nBufferSize characters from the fp
     382             :  * stream and stores them into the buffer pointed to by pszBuffer.
     383             :  * Reading stops after an EOF or a newline. If a newline is read, it
     384             :  * is _not_ stored into the buffer. A '\\0' is stored after the last
     385             :  * character in the buffer. All three types of newline terminators
     386             :  * recognized by the CPLFGets(): single '\\r' and '\\n' and '\\r\\n'
     387             :  * combination.
     388             :  *
     389             :  * @param pszBuffer pointer to the targeting character buffer.
     390             :  * @param nBufferSize maximum size of the string to read (not including
     391             :  * terminating '\\0').
     392             :  * @param fp file pointer to read from.
     393             :  * @return pointer to the pszBuffer containing a string read
     394             :  * from the file or NULL if the error or end of file was encountered.
     395             :  */
     396             : 
     397           0 : char *CPLFGets(char *pszBuffer, int nBufferSize, FILE *fp)
     398             : 
     399             : {
     400           0 :     if (nBufferSize == 0 || pszBuffer == nullptr || fp == nullptr)
     401           0 :         return nullptr;
     402             : 
     403             :     /* -------------------------------------------------------------------- */
     404             :     /*      Let the OS level call read what it things is one line.  This    */
     405             :     /*      will include the newline.  On windows, if the file happens      */
     406             :     /*      to be in text mode, the CRLF will have been converted to        */
     407             :     /*      just the newline (LF).  If it is in binary mode it may well     */
     408             :     /*      have both.                                                      */
     409             :     /* -------------------------------------------------------------------- */
     410           0 :     const long nOriginalOffset = VSIFTell(fp);
     411           0 :     if (VSIFGets(pszBuffer, nBufferSize, fp) == nullptr)
     412           0 :         return nullptr;
     413             : 
     414           0 :     int nActuallyRead = static_cast<int>(strlen(pszBuffer));
     415           0 :     if (nActuallyRead == 0)
     416           0 :         return nullptr;
     417             : 
     418             :     /* -------------------------------------------------------------------- */
     419             :     /*      If we found \r and out buffer is full, it is possible there     */
     420             :     /*      is also a pending \n.  Check for it.                            */
     421             :     /* -------------------------------------------------------------------- */
     422           0 :     if (nBufferSize == nActuallyRead + 1 &&
     423           0 :         pszBuffer[nActuallyRead - 1] == knCR)
     424             :     {
     425           0 :         const int chCheck = fgetc(fp);
     426           0 :         if (chCheck != knLF)
     427             :         {
     428             :             // unget the character.
     429           0 :             if (VSIFSeek(fp, nOriginalOffset + nActuallyRead, SEEK_SET) == -1)
     430             :             {
     431           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     432             :                          "Unable to unget a character");
     433             :             }
     434             :         }
     435             :     }
     436             : 
     437             :     /* -------------------------------------------------------------------- */
     438             :     /*      Trim off \n, \r or \r\n if it appears at the end.  We don't     */
     439             :     /*      need to do any "seeking" since we want the newline eaten.       */
     440             :     /* -------------------------------------------------------------------- */
     441           0 :     if (nActuallyRead > 1 && pszBuffer[nActuallyRead - 1] == knLF &&
     442           0 :         pszBuffer[nActuallyRead - 2] == knCR)
     443             :     {
     444           0 :         pszBuffer[nActuallyRead - 2] = '\0';
     445             :     }
     446           0 :     else if (pszBuffer[nActuallyRead - 1] == knLF ||
     447           0 :              pszBuffer[nActuallyRead - 1] == knCR)
     448             :     {
     449           0 :         pszBuffer[nActuallyRead - 1] = '\0';
     450             :     }
     451             : 
     452             :     /* -------------------------------------------------------------------- */
     453             :     /*      Search within the string for a \r (MacOS convention             */
     454             :     /*      apparently), and if we find it we need to trim the string,      */
     455             :     /*      and seek back.                                                  */
     456             :     /* -------------------------------------------------------------------- */
     457           0 :     char *pszExtraNewline = strchr(pszBuffer, knCR);
     458             : 
     459           0 :     if (pszExtraNewline != nullptr)
     460             :     {
     461           0 :         nActuallyRead = static_cast<int>(pszExtraNewline - pszBuffer + 1);
     462             : 
     463           0 :         *pszExtraNewline = '\0';
     464           0 :         if (VSIFSeek(fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET) != 0)
     465           0 :             return nullptr;
     466             : 
     467             :         // This hackery is necessary to try and find our correct
     468             :         // spot on win32 systems with text mode line translation going
     469             :         // on.  Sometimes the fseek back overshoots, but it doesn't
     470             :         // "realize it" till a character has been read. Try to read till
     471             :         // we get to the right spot and get our CR.
     472           0 :         int chCheck = fgetc(fp);
     473           0 :         while ((chCheck != knCR && chCheck != EOF) ||
     474           0 :                VSIFTell(fp) < nOriginalOffset + nActuallyRead)
     475             :         {
     476             :             static bool bWarned = false;
     477             : 
     478           0 :             if (!bWarned)
     479             :             {
     480           0 :                 bWarned = true;
     481           0 :                 CPLDebug("CPL",
     482             :                          "CPLFGets() correcting for DOS text mode translation "
     483             :                          "seek problem.");
     484             :             }
     485           0 :             chCheck = fgetc(fp);
     486             :         }
     487             :     }
     488             : 
     489           0 :     return pszBuffer;
     490             : }
     491             : 
     492             : /************************************************************************/
     493             : /*                         CPLReadLineBuffer()                          */
     494             : /*                                                                      */
     495             : /*      Fetch readline buffer, and ensure it is the desired size,       */
     496             : /*      reallocating if needed.  Manages TLS (thread local storage)     */
     497             : /*      issues for the buffer.                                          */
     498             : /*      We use a special trick to track the actual size of the buffer   */
     499             : /*      The first 4 bytes are reserved to store it as a int, hence the  */
     500             : /*      -4 / +4 hacks with the size and pointer.                        */
     501             : /************************************************************************/
     502     4408570 : static char *CPLReadLineBuffer(int nRequiredSize)
     503             : 
     504             : {
     505             : 
     506             :     /* -------------------------------------------------------------------- */
     507             :     /*      A required size of -1 means the buffer should be freed.         */
     508             :     /* -------------------------------------------------------------------- */
     509     4408570 :     if (nRequiredSize == -1)
     510             :     {
     511        2550 :         int bMemoryError = FALSE;
     512        2550 :         void *pRet = CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError);
     513        2550 :         if (pRet != nullptr)
     514             :         {
     515        2289 :             CPLFree(pRet);
     516        2289 :             CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
     517             :         }
     518        2550 :         return nullptr;
     519             :     }
     520             : 
     521             :     /* -------------------------------------------------------------------- */
     522             :     /*      If the buffer doesn't exist yet, create it.                     */
     523             :     /* -------------------------------------------------------------------- */
     524     4406020 :     int bMemoryError = FALSE;
     525             :     GUInt32 *pnAlloc =
     526     4406020 :         static_cast<GUInt32 *>(CPLGetTLSEx(CTLS_RLBUFFERINFO, &bMemoryError));
     527     4406020 :     if (bMemoryError)
     528           0 :         return nullptr;
     529             : 
     530     4406020 :     if (pnAlloc == nullptr)
     531             :     {
     532        4005 :         pnAlloc = static_cast<GUInt32 *>(VSI_MALLOC_VERBOSE(200));
     533        4005 :         if (pnAlloc == nullptr)
     534           0 :             return nullptr;
     535        4005 :         *pnAlloc = 196;
     536        4005 :         CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
     537             :     }
     538             : 
     539             :     /* -------------------------------------------------------------------- */
     540             :     /*      If it is too small, grow it bigger.                             */
     541             :     /* -------------------------------------------------------------------- */
     542     4406020 :     if (static_cast<int>(*pnAlloc) - 1 < nRequiredSize)
     543             :     {
     544        2949 :         const int nNewSize = nRequiredSize + 4 + 500;
     545        2949 :         if (nNewSize <= 0)
     546             :         {
     547           0 :             VSIFree(pnAlloc);
     548           0 :             CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
     549           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     550             :                      "CPLReadLineBuffer(): Trying to allocate more than "
     551             :                      "2 GB.");
     552           0 :             return nullptr;
     553             :         }
     554             : 
     555             :         GUInt32 *pnAllocNew =
     556        2949 :             static_cast<GUInt32 *>(VSI_REALLOC_VERBOSE(pnAlloc, nNewSize));
     557        2949 :         if (pnAllocNew == nullptr)
     558             :         {
     559           0 :             VSIFree(pnAlloc);
     560           0 :             CPLSetTLS(CTLS_RLBUFFERINFO, nullptr, FALSE);
     561           0 :             return nullptr;
     562             :         }
     563        2949 :         pnAlloc = pnAllocNew;
     564             : 
     565        2949 :         *pnAlloc = nNewSize - 4;
     566        2949 :         CPLSetTLS(CTLS_RLBUFFERINFO, pnAlloc, TRUE);
     567             :     }
     568             : 
     569     4406020 :     return reinterpret_cast<char *>(pnAlloc + 1);
     570             : }
     571             : 
     572             : /************************************************************************/
     573             : /*                            CPLReadLine()                             */
     574             : /************************************************************************/
     575             : 
     576             : /**
     577             :  * Simplified line reading from text file.
     578             :  *
     579             :  * Read a line of text from the given file handle, taking care
     580             :  * to capture CR and/or LF and strip off ... equivalent of
     581             :  * DKReadLine().  Pointer to an internal buffer is returned.
     582             :  * The application shouldn't free it, or depend on its value
     583             :  * past the next call to CPLReadLine().
     584             :  *
     585             :  * Note that CPLReadLine() uses VSIFGets(), so any hooking of VSI file
     586             :  * services should apply to CPLReadLine() as well.
     587             :  *
     588             :  * CPLReadLine() maintains an internal buffer, which will appear as a
     589             :  * single block memory leak in some circumstances.  CPLReadLine() may
     590             :  * be called with a NULL FILE * at any time to free this working buffer.
     591             :  *
     592             :  * @param fp file pointer opened with VSIFOpen().
     593             :  *
     594             :  * @return pointer to an internal buffer containing a line of text read
     595             :  * from the file or NULL if the end of file was encountered.
     596             :  */
     597             : 
     598           5 : const char *CPLReadLine(FILE *fp)
     599             : 
     600             : {
     601             :     /* -------------------------------------------------------------------- */
     602             :     /*      Cleanup case.                                                   */
     603             :     /* -------------------------------------------------------------------- */
     604           5 :     if (fp == nullptr)
     605             :     {
     606           5 :         CPLReadLineBuffer(-1);
     607           5 :         return nullptr;
     608             :     }
     609             : 
     610             :     /* -------------------------------------------------------------------- */
     611             :     /*      Loop reading chunks of the line till we get to the end of       */
     612             :     /*      the line.                                                       */
     613             :     /* -------------------------------------------------------------------- */
     614           0 :     size_t nBytesReadThisTime = 0;
     615           0 :     char *pszRLBuffer = nullptr;
     616           0 :     size_t nReadSoFar = 0;
     617             : 
     618           0 :     do
     619             :     {
     620             :         /* --------------------------------------------------------------------
     621             :          */
     622             :         /*      Grow the working buffer if we have it nearly full.  Fail out */
     623             :         /*      of read line if we can't reallocate it big enough (for */
     624             :         /*      instance for a _very large_ file with no newlines). */
     625             :         /* --------------------------------------------------------------------
     626             :          */
     627           0 :         if (nReadSoFar > 100 * 1024 * 1024)
     628             :             // It is dubious that we need to read a line longer than 100 MB.
     629           0 :             return nullptr;
     630           0 :         pszRLBuffer = CPLReadLineBuffer(static_cast<int>(nReadSoFar) + 129);
     631           0 :         if (pszRLBuffer == nullptr)
     632           0 :             return nullptr;
     633             : 
     634             :         /* --------------------------------------------------------------------
     635             :          */
     636             :         /*      Do the actual read. */
     637             :         /* --------------------------------------------------------------------
     638             :          */
     639           0 :         if (CPLFGets(pszRLBuffer + nReadSoFar, 128, fp) == nullptr &&
     640             :             nReadSoFar == 0)
     641           0 :             return nullptr;
     642             : 
     643           0 :         nBytesReadThisTime = strlen(pszRLBuffer + nReadSoFar);
     644           0 :         nReadSoFar += nBytesReadThisTime;
     645           0 :     } while (nBytesReadThisTime >= 127 && pszRLBuffer[nReadSoFar - 1] != knCR &&
     646           0 :              pszRLBuffer[nReadSoFar - 1] != knLF);
     647             : 
     648           0 :     return pszRLBuffer;
     649             : }
     650             : 
     651             : /************************************************************************/
     652             : /*                            CPLReadLineL()                            */
     653             : /************************************************************************/
     654             : 
     655             : /**
     656             :  * Simplified line reading from text file.
     657             :  *
     658             :  * Similar to CPLReadLine(), but reading from a large file API handle.
     659             :  *
     660             :  * @param fp file pointer opened with VSIFOpenL().
     661             :  *
     662             :  * @return pointer to an internal buffer containing a line of text read
     663             :  * from the file or NULL if the end of file was encountered.
     664             :  */
     665             : 
     666      204860 : const char *CPLReadLineL(VSILFILE *fp)
     667             : {
     668      204860 :     return CPLReadLine2L(fp, -1, nullptr);
     669             : }
     670             : 
     671             : /************************************************************************/
     672             : /*                           CPLReadLine2L()                            */
     673             : /************************************************************************/
     674             : 
     675             : /**
     676             :  * Simplified line reading from text file.
     677             :  *
     678             :  * Similar to CPLReadLine(), but reading from a large file API handle.
     679             :  *
     680             :  * @param fp file pointer opened with VSIFOpenL().
     681             :  * @param nMaxCars  maximum number of characters allowed, or -1 for no limit.
     682             :  * @param papszOptions NULL-terminated array of options. Unused for now.
     683             : 
     684             :  * @return pointer to an internal buffer containing a line of text read
     685             :  * from the file or NULL if the end of file was encountered or the maximum
     686             :  * number of characters allowed reached.
     687             :  *
     688             :  */
     689             : 
     690     2757200 : const char *CPLReadLine2L(VSILFILE *fp, int nMaxCars,
     691             :                           CPL_UNUSED CSLConstList papszOptions)
     692             : 
     693             : {
     694             :     int nBufLength;
     695     5514410 :     return CPLReadLine3L(fp, nMaxCars, &nBufLength, papszOptions);
     696             : }
     697             : 
     698             : /************************************************************************/
     699             : /*                           CPLReadLine3L()                            */
     700             : /************************************************************************/
     701             : 
     702             : /**
     703             :  * Simplified line reading from text file.
     704             :  *
     705             :  * Similar to CPLReadLine(), but reading from a large file API handle.
     706             :  *
     707             :  * @param fp file pointer opened with VSIFOpenL().
     708             :  * @param nMaxCars  maximum number of characters allowed, or -1 for no limit.
     709             :  * @param papszOptions NULL-terminated array of options. Unused for now.
     710             :  * @param[out] pnBufLength size of output string (must be non-NULL)
     711             : 
     712             :  * @return pointer to an internal buffer containing a line of text read
     713             :  * from the file or NULL if the end of file was encountered or the maximum
     714             :  * number of characters allowed reached.
     715             :  *
     716             :  */
     717     2822280 : const char *CPLReadLine3L(VSILFILE *fp, int nMaxCars, int *pnBufLength,
     718             :                           CPL_UNUSED CSLConstList papszOptions)
     719             : {
     720             :     /* -------------------------------------------------------------------- */
     721             :     /*      Cleanup case.                                                   */
     722             :     /* -------------------------------------------------------------------- */
     723     2822280 :     if (fp == nullptr)
     724             :     {
     725        2545 :         CPLReadLineBuffer(-1);
     726        2545 :         return nullptr;
     727             :     }
     728             : 
     729             :     /* -------------------------------------------------------------------- */
     730             :     /*      Loop reading chunks of the line till we get to the end of       */
     731             :     /*      the line.                                                       */
     732             :     /* -------------------------------------------------------------------- */
     733     2819730 :     char *pszRLBuffer = nullptr;
     734     2819730 :     const size_t nChunkSize = 40;
     735     2819730 :     char szChunk[nChunkSize] = {};
     736     2819730 :     size_t nChunkBytesRead = 0;
     737     2819730 :     size_t nChunkBytesConsumed = 0;
     738             : 
     739     2819730 :     *pnBufLength = 0;
     740     2819730 :     szChunk[0] = 0;
     741             : 
     742             :     while (true)
     743             :     {
     744             :         /* --------------------------------------------------------------------
     745             :          */
     746             :         /*      Read a chunk from the input file. */
     747             :         /* --------------------------------------------------------------------
     748             :          */
     749     4406020 :         if (*pnBufLength > INT_MAX - static_cast<int>(nChunkSize) - 1)
     750             :         {
     751           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     752             :                      "Too big line : more than 2 billion characters!.");
     753           0 :             CPLReadLineBuffer(-1);
     754           0 :             return nullptr;
     755             :         }
     756             : 
     757             :         pszRLBuffer =
     758     4406020 :             CPLReadLineBuffer(static_cast<int>(*pnBufLength + nChunkSize + 1));
     759     4406020 :         if (pszRLBuffer == nullptr)
     760           0 :             return nullptr;
     761             : 
     762     4406020 :         if (nChunkBytesRead == nChunkBytesConsumed + 1)
     763             :         {
     764             : 
     765             :             // case where one character is left over from last read.
     766     1586280 :             szChunk[0] = szChunk[nChunkBytesConsumed];
     767             : 
     768     1586280 :             nChunkBytesConsumed = 0;
     769     1586280 :             nChunkBytesRead = VSIFReadL(szChunk + 1, 1, nChunkSize - 1, fp) + 1;
     770             :         }
     771             :         else
     772             :         {
     773     2819730 :             nChunkBytesConsumed = 0;
     774             : 
     775             :             // fresh read.
     776     2819730 :             nChunkBytesRead = VSIFReadL(szChunk, 1, nChunkSize, fp);
     777     2819730 :             if (nChunkBytesRead == 0)
     778             :             {
     779       17313 :                 if (*pnBufLength == 0)
     780       17313 :                     return nullptr;
     781             : 
     782           0 :                 break;
     783             :             }
     784             :         }
     785             : 
     786             :         /* --------------------------------------------------------------------
     787             :          */
     788             :         /*      copy over characters watching for end-of-line. */
     789             :         /* --------------------------------------------------------------------
     790             :          */
     791     4388700 :         bool bBreak = false;
     792   107448000 :         while (nChunkBytesConsumed < nChunkBytesRead - 1 && !bBreak)
     793             :         {
     794   103059000 :             if ((szChunk[nChunkBytesConsumed] == knCR &&
     795      608624 :                  szChunk[nChunkBytesConsumed + 1] == knLF) ||
     796   102451000 :                 (szChunk[nChunkBytesConsumed] == knLF &&
     797     2177900 :                  szChunk[nChunkBytesConsumed + 1] == knCR))
     798             :             {
     799      608328 :                 nChunkBytesConsumed += 2;
     800      608328 :                 bBreak = true;
     801             :             }
     802   102451000 :             else if (szChunk[nChunkBytesConsumed] == knLF ||
     803   100273000 :                      szChunk[nChunkBytesConsumed] == knCR)
     804             :             {
     805     2178190 :                 nChunkBytesConsumed += 1;
     806     2178190 :                 bBreak = true;
     807             :             }
     808             :             else
     809             :             {
     810   100273000 :                 pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
     811   100273000 :                 if (nMaxCars >= 0 && *pnBufLength == nMaxCars)
     812             :                 {
     813           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     814             :                              "Maximum number of characters allowed reached.");
     815           1 :                     return nullptr;
     816             :                 }
     817             :             }
     818             :         }
     819             : 
     820     4388700 :         if (bBreak)
     821     2786520 :             break;
     822             : 
     823             :         /* --------------------------------------------------------------------
     824             :          */
     825             :         /*      If there is a remaining character and it is not a newline */
     826             :         /*      consume it.  If it is a newline, but we are clearly at the */
     827             :         /*      end of the file then consume it. */
     828             :         /* --------------------------------------------------------------------
     829             :          */
     830     1602180 :         if (nChunkBytesConsumed == nChunkBytesRead - 1 &&
     831             :             nChunkBytesRead < nChunkSize)
     832             :         {
     833       15899 :             if (szChunk[nChunkBytesConsumed] == knLF ||
     834        2065 :                 szChunk[nChunkBytesConsumed] == knCR)
     835             :             {
     836       13834 :                 nChunkBytesConsumed++;
     837       13834 :                 break;
     838             :             }
     839             : 
     840        2065 :             pszRLBuffer[(*pnBufLength)++] = szChunk[nChunkBytesConsumed++];
     841        2065 :             break;
     842             :         }
     843     1586280 :     }
     844             : 
     845             :     /* -------------------------------------------------------------------- */
     846             :     /*      If we have left over bytes after breaking out, seek back to     */
     847             :     /*      ensure they remain to be read next time.                        */
     848             :     /* -------------------------------------------------------------------- */
     849     2802420 :     if (nChunkBytesConsumed < nChunkBytesRead)
     850             :     {
     851     2779500 :         const size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed;
     852             : 
     853     2779500 :         if (VSIFSeekL(fp, VSIFTellL(fp) - nBytesToPush, SEEK_SET) != 0)
     854           0 :             return nullptr;
     855             :     }
     856             : 
     857     2802420 :     pszRLBuffer[*pnBufLength] = '\0';
     858             : 
     859     2802420 :     return pszRLBuffer;
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                           CPLScanString()                            */
     864             : /************************************************************************/
     865             : 
     866             : /**
     867             :  * Scan up to a maximum number of characters from a given string,
     868             :  * allocate a buffer for a new string and fill it with scanned characters.
     869             :  *
     870             :  * @param pszString String containing characters to be scanned. It may be
     871             :  * terminated with a null character.
     872             :  *
     873             :  * @param nMaxLength The maximum number of character to read. Less
     874             :  * characters will be read if a null character is encountered.
     875             :  *
     876             :  * @param bTrimSpaces If TRUE, trim ending spaces from the input string.
     877             :  * Character considered as empty using isspace(3) function.
     878             :  *
     879             :  * @param bNormalize If TRUE, replace ':' symbol with the '_'. It is needed if
     880             :  * resulting string will be used in CPL dictionaries.
     881             :  *
     882             :  * @return Pointer to the resulting string buffer. Caller responsible to free
     883             :  * this buffer with CPLFree().
     884             :  */
     885             : 
     886        5342 : char *CPLScanString(const char *pszString, int nMaxLength, int bTrimSpaces,
     887             :                     int bNormalize)
     888             : {
     889        5342 :     if (!pszString)
     890           0 :         return nullptr;
     891             : 
     892        5342 :     if (!nMaxLength)
     893           2 :         return CPLStrdup("");
     894             : 
     895        5340 :     char *pszBuffer = static_cast<char *>(CPLMalloc(nMaxLength + 1));
     896        5340 :     if (!pszBuffer)
     897           0 :         return nullptr;
     898             : 
     899        5340 :     strncpy(pszBuffer, pszString, nMaxLength);
     900        5340 :     pszBuffer[nMaxLength] = '\0';
     901             : 
     902        5340 :     if (bTrimSpaces)
     903             :     {
     904        5340 :         size_t i = strlen(pszBuffer);
     905        6483 :         while (i > 0)
     906             :         {
     907        6449 :             i--;
     908        6449 :             if (!isspace(static_cast<unsigned char>(pszBuffer[i])))
     909        5306 :                 break;
     910        1143 :             pszBuffer[i] = '\0';
     911             :         }
     912             :     }
     913             : 
     914        5340 :     if (bNormalize)
     915             :     {
     916        5219 :         size_t i = strlen(pszBuffer);
     917       39506 :         while (i > 0)
     918             :         {
     919       34287 :             i--;
     920       34287 :             if (pszBuffer[i] == ':')
     921           0 :                 pszBuffer[i] = '_';
     922             :         }
     923             :     }
     924             : 
     925        5340 :     return pszBuffer;
     926             : }
     927             : 
     928             : /************************************************************************/
     929             : /*                            CPLScanLong()                             */
     930             : /************************************************************************/
     931             : 
     932             : /**
     933             :  * Scan up to a maximum number of characters from a string and convert
     934             :  * the result to a long.
     935             :  *
     936             :  * @param pszString String containing characters to be scanned. It may be
     937             :  * terminated with a null character.
     938             :  *
     939             :  * @param nMaxLength The maximum number of character to consider as part
     940             :  * of the number. Less characters will be considered if a null character
     941             :  * is encountered.
     942             :  *
     943             :  * @return Long value, converted from its ASCII form.
     944             :  */
     945             : 
     946         551 : long CPLScanLong(const char *pszString, int nMaxLength)
     947             : {
     948         551 :     CPLAssert(nMaxLength >= 0);
     949         551 :     if (pszString == nullptr)
     950           0 :         return 0;
     951         551 :     const size_t nLength = CPLStrnlen(pszString, nMaxLength);
     952        1102 :     const std::string osValue(pszString, nLength);
     953         551 :     return atol(osValue.c_str());
     954             : }
     955             : 
     956             : /************************************************************************/
     957             : /*                            CPLScanULong()                            */
     958             : /************************************************************************/
     959             : 
     960             : /**
     961             :  * Scan up to a maximum number of characters from a string and convert
     962             :  * the result to a unsigned long.
     963             :  *
     964             :  * @param pszString String containing characters to be scanned. It may be
     965             :  * terminated with a null character.
     966             :  *
     967             :  * @param nMaxLength The maximum number of character to consider as part
     968             :  * of the number. Less characters will be considered if a null character
     969             :  * is encountered.
     970             :  *
     971             :  * @return Unsigned long value, converted from its ASCII form.
     972             :  */
     973             : 
     974           0 : unsigned long CPLScanULong(const char *pszString, int nMaxLength)
     975             : {
     976           0 :     CPLAssert(nMaxLength >= 0);
     977           0 :     if (pszString == nullptr)
     978           0 :         return 0;
     979           0 :     const size_t nLength = CPLStrnlen(pszString, nMaxLength);
     980           0 :     const std::string osValue(pszString, nLength);
     981           0 :     return strtoul(osValue.c_str(), nullptr, 10);
     982             : }
     983             : 
     984             : /************************************************************************/
     985             : /*                           CPLScanUIntBig()                           */
     986             : /************************************************************************/
     987             : 
     988             : /**
     989             :  * Extract big integer from string.
     990             :  *
     991             :  * Scan up to a maximum number of characters from a string and convert
     992             :  * the result to a GUIntBig.
     993             :  *
     994             :  * @param pszString String containing characters to be scanned. It may be
     995             :  * terminated with a null character.
     996             :  *
     997             :  * @param nMaxLength The maximum number of character to consider as part
     998             :  * of the number. Less characters will be considered if a null character
     999             :  * is encountered.
    1000             :  *
    1001             :  * @return GUIntBig value, converted from its ASCII form.
    1002             :  */
    1003             : 
    1004       15482 : GUIntBig CPLScanUIntBig(const char *pszString, int nMaxLength)
    1005             : {
    1006       15482 :     CPLAssert(nMaxLength >= 0);
    1007       15482 :     if (pszString == nullptr)
    1008           0 :         return 0;
    1009       15482 :     const size_t nLength = CPLStrnlen(pszString, nMaxLength);
    1010       30964 :     const std::string osValue(pszString, nLength);
    1011             : 
    1012             :     /* -------------------------------------------------------------------- */
    1013             :     /*      Fetch out the result                                            */
    1014             :     /* -------------------------------------------------------------------- */
    1015       15482 :     return strtoull(osValue.c_str(), nullptr, 10);
    1016             : }
    1017             : 
    1018             : /************************************************************************/
    1019             : /*                           CPLAtoGIntBig()                            */
    1020             : /************************************************************************/
    1021             : 
    1022             : /**
    1023             :  * Convert a string to a 64 bit signed integer.
    1024             :  *
    1025             :  * @param pszString String containing 64 bit signed integer.
    1026             :  * @return 64 bit signed integer.
    1027             :  */
    1028             : 
    1029       55294 : GIntBig CPLAtoGIntBig(const char *pszString)
    1030             : {
    1031       55294 :     return atoll(pszString);
    1032             : }
    1033             : 
    1034             : #if defined(__MINGW32__) || defined(__sun__)
    1035             : 
    1036             : // mingw atoll() doesn't return ERANGE in case of overflow
    1037             : static int CPLAtoGIntBigExHasOverflow(const char *pszString, GIntBig nVal)
    1038             : {
    1039             :     if (strlen(pszString) <= 18)
    1040             :         return FALSE;
    1041             :     while (*pszString == ' ')
    1042             :         pszString++;
    1043             :     if (*pszString == '+')
    1044             :         pszString++;
    1045             :     char szBuffer[32] = {};
    1046             : /* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
    1047             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
    1048             : #pragma GCC diagnostic push
    1049             : #pragma GCC diagnostic ignored "-Wformat"
    1050             : #endif
    1051             :     snprintf(szBuffer, sizeof(szBuffer), CPL_FRMT_GIB, nVal);
    1052             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
    1053             : #pragma GCC diagnostic pop
    1054             : #endif
    1055             :     return strcmp(szBuffer, pszString) != 0;
    1056             : }
    1057             : 
    1058             : #endif
    1059             : 
    1060             : /************************************************************************/
    1061             : /*                          CPLAtoGIntBigEx()                           */
    1062             : /************************************************************************/
    1063             : 
    1064             : /**
    1065             :  * Convert a string to a 64 bit signed integer.
    1066             :  *
    1067             :  * @param pszString String containing 64 bit signed integer.
    1068             :  * @param bWarn Issue a warning if an overflow occurs during conversion
    1069             :  * @param pbOverflow Pointer to an integer to store if an overflow occurred, or
    1070             :  *        NULL
    1071             :  * @return 64 bit signed integer.
    1072             :  */
    1073             : 
    1074      108731 : GIntBig CPLAtoGIntBigEx(const char *pszString, int bWarn, int *pbOverflow)
    1075             : {
    1076      108731 :     errno = 0;
    1077      108731 :     GIntBig nVal = strtoll(pszString, nullptr, 10);
    1078      108731 :     if (errno == ERANGE
    1079             : #if defined(__MINGW32__) || defined(__sun__)
    1080             :         || CPLAtoGIntBigExHasOverflow(pszString, nVal)
    1081             : #endif
    1082             :     )
    1083             :     {
    1084           4 :         if (pbOverflow)
    1085           2 :             *pbOverflow = TRUE;
    1086           4 :         if (bWarn)
    1087             :         {
    1088           2 :             CPLError(CE_Warning, CPLE_AppDefined,
    1089             :                      "64 bit integer overflow when converting %s", pszString);
    1090             :         }
    1091           4 :         while (*pszString == ' ')
    1092           0 :             pszString++;
    1093           4 :         return (*pszString == '-') ? GINTBIG_MIN : GINTBIG_MAX;
    1094             :     }
    1095      108727 :     else if (pbOverflow)
    1096             :     {
    1097        5428 :         *pbOverflow = FALSE;
    1098             :     }
    1099      108727 :     return nVal;
    1100             : }
    1101             : 
    1102             : /************************************************************************/
    1103             : /*                           CPLScanPointer()                           */
    1104             : /************************************************************************/
    1105             : 
    1106             : /**
    1107             :  * Extract pointer from string.
    1108             :  *
    1109             :  * Scan up to a maximum number of characters from a string and convert
    1110             :  * the result to a pointer.
    1111             :  *
    1112             :  * @param pszString String containing characters to be scanned. It may be
    1113             :  * terminated with a null character.
    1114             :  *
    1115             :  * @param nMaxLength The maximum number of character to consider as part
    1116             :  * of the number. Less characters will be considered if a null character
    1117             :  * is encountered.
    1118             :  *
    1119             :  * @return pointer value, converted from its ASCII form.
    1120             :  */
    1121             : 
    1122         643 : void *CPLScanPointer(const char *pszString, int nMaxLength)
    1123             : {
    1124         643 :     char szTemp[128] = {};
    1125             : 
    1126             :     /* -------------------------------------------------------------------- */
    1127             :     /*      Compute string into local buffer, and terminate it.             */
    1128             :     /* -------------------------------------------------------------------- */
    1129         643 :     if (nMaxLength > static_cast<int>(sizeof(szTemp)) - 1)
    1130           0 :         nMaxLength = sizeof(szTemp) - 1;
    1131             : 
    1132         643 :     strncpy(szTemp, pszString, nMaxLength);
    1133         643 :     szTemp[nMaxLength] = '\0';
    1134             : 
    1135             :     /* -------------------------------------------------------------------- */
    1136             :     /*      On MSVC we have to scanf pointer values without the 0x          */
    1137             :     /*      prefix.                                                         */
    1138             :     /* -------------------------------------------------------------------- */
    1139         643 :     if (STARTS_WITH_CI(szTemp, "0x"))
    1140             :     {
    1141         643 :         void *pResult = nullptr;
    1142             : 
    1143             : #if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
    1144             :         // cppcheck-suppress invalidscanf
    1145             :         sscanf(szTemp + 2, "%p", &pResult);
    1146             : #else
    1147             :         // cppcheck-suppress invalidscanf
    1148         643 :         sscanf(szTemp, "%p", &pResult);
    1149             : 
    1150             :         // Solaris actually behaves like MSVCRT.
    1151         643 :         if (pResult == nullptr)
    1152             :         {
    1153             :             // cppcheck-suppress invalidscanf
    1154           0 :             sscanf(szTemp + 2, "%p", &pResult);
    1155             :         }
    1156             : #endif
    1157         643 :         return pResult;
    1158             :     }
    1159             : 
    1160             : #if SIZEOF_VOIDP == 8
    1161           0 :     return reinterpret_cast<void *>(CPLScanUIntBig(szTemp, nMaxLength));
    1162             : #else
    1163             :     return reinterpret_cast<void *>(CPLScanULong(szTemp, nMaxLength));
    1164             : #endif
    1165             : }
    1166             : 
    1167             : /************************************************************************/
    1168             : /*                           CPLScanDouble()                            */
    1169             : /************************************************************************/
    1170             : 
    1171             : /**
    1172             :  * Extract double from string.
    1173             :  *
    1174             :  * Scan up to a maximum number of characters from a string and convert the
    1175             :  * result to a double. This function uses CPLAtof() to convert string to
    1176             :  * double value, so it uses a comma as a decimal delimiter.
    1177             :  *
    1178             :  * @param pszString String containing characters to be scanned. It may be
    1179             :  * terminated with a null character.
    1180             :  *
    1181             :  * @param nMaxLength The maximum number of character to consider as part
    1182             :  * of the number. Less characters will be considered if a null character
    1183             :  * is encountered.
    1184             :  *
    1185             :  * @return Double value, converted from its ASCII form.
    1186             :  */
    1187             : 
    1188         317 : double CPLScanDouble(const char *pszString, int nMaxLength)
    1189             : {
    1190         317 :     char szValue[32] = {};
    1191         317 :     char *pszValue = nullptr;
    1192             : 
    1193         317 :     if (nMaxLength + 1 < static_cast<int>(sizeof(szValue)))
    1194         317 :         pszValue = szValue;
    1195             :     else
    1196           0 :         pszValue = static_cast<char *>(CPLMalloc(nMaxLength + 1));
    1197             : 
    1198             :     /* -------------------------------------------------------------------- */
    1199             :     /*      Compute string into local buffer, and terminate it.             */
    1200             :     /* -------------------------------------------------------------------- */
    1201         317 :     strncpy(pszValue, pszString, nMaxLength);
    1202         317 :     pszValue[nMaxLength] = '\0';
    1203             : 
    1204             :     /* -------------------------------------------------------------------- */
    1205             :     /*      Make a pass through converting 'D's to 'E's.                    */
    1206             :     /* -------------------------------------------------------------------- */
    1207        6436 :     for (int i = 0; i < nMaxLength; i++)
    1208        6119 :         if (pszValue[i] == 'd' || pszValue[i] == 'D')
    1209          45 :             pszValue[i] = 'E';
    1210             : 
    1211             :     /* -------------------------------------------------------------------- */
    1212             :     /*      The conversion itself.                                          */
    1213             :     /* -------------------------------------------------------------------- */
    1214         317 :     const double dfValue = CPLAtof(pszValue);
    1215             : 
    1216         317 :     if (pszValue != szValue)
    1217           0 :         CPLFree(pszValue);
    1218         317 :     return dfValue;
    1219             : }
    1220             : 
    1221             : /************************************************************************/
    1222             : /*                           CPLPrintString()                           */
    1223             : /************************************************************************/
    1224             : 
    1225             : /**
    1226             :  * Copy the string pointed to by pszSrc, NOT including the terminating
    1227             :  * `\\0' character, to the array pointed to by pszDest.
    1228             :  *
    1229             :  * @param pszDest Pointer to the destination string buffer. Should be
    1230             :  * large enough to hold the resulting string.
    1231             :  *
    1232             :  * @param pszSrc Pointer to the source buffer.
    1233             :  *
    1234             :  * @param nMaxLen Maximum length of the resulting string. If string length
    1235             :  * is greater than nMaxLen, it will be truncated.
    1236             :  *
    1237             :  * @return Number of characters printed.
    1238             :  */
    1239             : 
    1240       11953 : int CPLPrintString(char *pszDest, const char *pszSrc, int nMaxLen)
    1241             : {
    1242       11953 :     if (!pszDest)
    1243           0 :         return 0;
    1244             : 
    1245       11953 :     if (!pszSrc)
    1246             :     {
    1247           0 :         *pszDest = '\0';
    1248           0 :         return 1;
    1249             :     }
    1250             : 
    1251       11953 :     int nChars = 0;
    1252       11953 :     char *pszTemp = pszDest;
    1253             : 
    1254      179812 :     while (nChars < nMaxLen && *pszSrc)
    1255             :     {
    1256      167859 :         *pszTemp++ = *pszSrc++;
    1257      167859 :         nChars++;
    1258             :     }
    1259             : 
    1260       11953 :     return nChars;
    1261             : }
    1262             : 
    1263             : /************************************************************************/
    1264             : /*                         CPLPrintStringFill()                         */
    1265             : /************************************************************************/
    1266             : 
    1267             : /**
    1268             :  * Copy the string pointed to by pszSrc, NOT including the terminating
    1269             :  * `\\0' character, to the array pointed to by pszDest. Remainder of the
    1270             :  * destination string will be filled with space characters. This is only
    1271             :  * difference from the PrintString().
    1272             :  *
    1273             :  * @param pszDest Pointer to the destination string buffer. Should be
    1274             :  * large enough to hold the resulting string.
    1275             :  *
    1276             :  * @param pszSrc Pointer to the source buffer.
    1277             :  *
    1278             :  * @param nMaxLen Maximum length of the resulting string. If string length
    1279             :  * is greater than nMaxLen, it will be truncated.
    1280             :  *
    1281             :  * @return Number of characters printed.
    1282             :  */
    1283             : 
    1284         212 : int CPLPrintStringFill(char *pszDest, const char *pszSrc, int nMaxLen)
    1285             : {
    1286         212 :     if (!pszDest)
    1287           0 :         return 0;
    1288             : 
    1289         212 :     if (!pszSrc)
    1290             :     {
    1291           0 :         memset(pszDest, ' ', nMaxLen);
    1292           0 :         return nMaxLen;
    1293             :     }
    1294             : 
    1295         212 :     char *pszTemp = pszDest;
    1296        1271 :     while (nMaxLen && *pszSrc)
    1297             :     {
    1298        1059 :         *pszTemp++ = *pszSrc++;
    1299        1059 :         nMaxLen--;
    1300             :     }
    1301             : 
    1302         212 :     if (nMaxLen)
    1303          72 :         memset(pszTemp, ' ', nMaxLen);
    1304             : 
    1305         212 :     return nMaxLen;
    1306             : }
    1307             : 
    1308             : /************************************************************************/
    1309             : /*                           CPLPrintInt32()                            */
    1310             : /************************************************************************/
    1311             : 
    1312             : /**
    1313             :  * Print GInt32 value into specified string buffer. This string will not
    1314             :  * be NULL-terminated.
    1315             :  *
    1316             :  * @param pszBuffer Pointer to the destination string buffer. Should be
    1317             :  * large enough to hold the resulting string. Note, that the string will
    1318             :  * not be NULL-terminated, so user should do this himself, if needed.
    1319             :  *
    1320             :  * @param iValue Numerical value to print.
    1321             :  *
    1322             :  * @param nMaxLen Maximum length of the resulting string. If string length
    1323             :  * is greater than nMaxLen, it will be truncated.
    1324             :  *
    1325             :  * @return Number of characters printed.
    1326             :  */
    1327             : 
    1328          10 : int CPLPrintInt32(char *pszBuffer, GInt32 iValue, int nMaxLen)
    1329             : {
    1330          10 :     if (!pszBuffer)
    1331           0 :         return 0;
    1332             : 
    1333          10 :     if (nMaxLen >= 64)
    1334           0 :         nMaxLen = 63;
    1335             : 
    1336          10 :     char szTemp[64] = {};
    1337             : 
    1338             : #if UINT_MAX == 65535
    1339             :     snprintf(szTemp, sizeof(szTemp), "%*ld", nMaxLen, iValue);
    1340             : #else
    1341          10 :     snprintf(szTemp, sizeof(szTemp), "%*d", nMaxLen, iValue);
    1342             : #endif
    1343             : 
    1344          10 :     return CPLPrintString(pszBuffer, szTemp, nMaxLen);
    1345             : }
    1346             : 
    1347             : /************************************************************************/
    1348             : /*                          CPLPrintUIntBig()                           */
    1349             : /************************************************************************/
    1350             : 
    1351             : /**
    1352             :  * Print GUIntBig value into specified string buffer. This string will not
    1353             :  * be NULL-terminated.
    1354             :  *
    1355             :  * @param pszBuffer Pointer to the destination string buffer. Should be
    1356             :  * large enough to hold the resulting string. Note, that the string will
    1357             :  * not be NULL-terminated, so user should do this himself, if needed.
    1358             :  *
    1359             :  * @param iValue Numerical value to print.
    1360             :  *
    1361             :  * @param nMaxLen Maximum length of the resulting string. If string length
    1362             :  * is greater than nMaxLen, it will be truncated.
    1363             :  *
    1364             :  * @return Number of characters printed.
    1365             :  */
    1366             : 
    1367          24 : int CPLPrintUIntBig(char *pszBuffer, GUIntBig iValue, int nMaxLen)
    1368             : {
    1369          24 :     if (!pszBuffer)
    1370           0 :         return 0;
    1371             : 
    1372          24 :     if (nMaxLen >= 64)
    1373           0 :         nMaxLen = 63;
    1374             : 
    1375          24 :     char szTemp[64] = {};
    1376             : 
    1377             : #if defined(__MSVCRT__) || (defined(_WIN32) && defined(_MSC_VER))
    1378             : /* x86_64-w64-mingw32-g++ (GCC) 4.8.2 annoyingly warns */
    1379             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
    1380             : #pragma GCC diagnostic push
    1381             : #pragma GCC diagnostic ignored "-Wformat"
    1382             : #pragma GCC diagnostic ignored "-Wformat-extra-args"
    1383             : #endif
    1384             :     snprintf(szTemp, sizeof(szTemp), "%*I64u", nMaxLen, iValue);
    1385             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
    1386             : #pragma GCC diagnostic pop
    1387             : #endif
    1388             : #else
    1389          24 :     snprintf(szTemp, sizeof(szTemp), "%*llu", nMaxLen, iValue);
    1390             : #endif
    1391             : 
    1392          24 :     return CPLPrintString(pszBuffer, szTemp, nMaxLen);
    1393             : }
    1394             : 
    1395             : /************************************************************************/
    1396             : /*                          CPLPrintPointer()                           */
    1397             : /************************************************************************/
    1398             : 
    1399             : /**
    1400             :  * Print pointer value into specified string buffer. This string will not
    1401             :  * be NULL-terminated.
    1402             :  *
    1403             :  * @param pszBuffer Pointer to the destination string buffer. Should be
    1404             :  * large enough to hold the resulting string. Note, that the string will
    1405             :  * not be NULL-terminated, so user should do this himself, if needed.
    1406             :  *
    1407             :  * @param pValue Pointer to ASCII encode.
    1408             :  *
    1409             :  * @param nMaxLen Maximum length of the resulting string. If string length
    1410             :  * is greater than nMaxLen, it will be truncated.
    1411             :  *
    1412             :  * @return Number of characters printed.
    1413             :  */
    1414             : 
    1415       11886 : int CPLPrintPointer(char *pszBuffer, void *pValue, int nMaxLen)
    1416             : {
    1417       11886 :     if (!pszBuffer)
    1418           0 :         return 0;
    1419             : 
    1420       11886 :     if (nMaxLen >= 64)
    1421       11250 :         nMaxLen = 63;
    1422             : 
    1423       11886 :     char szTemp[64] = {};
    1424             : 
    1425       11886 :     snprintf(szTemp, sizeof(szTemp), "%p", pValue);
    1426             : 
    1427             :     // On windows, and possibly some other platforms the sprintf("%p")
    1428             :     // does not prefix things with 0x so it is hard to know later if the
    1429             :     // value is hex encoded.  Fix this up here.
    1430             : 
    1431       11886 :     if (!STARTS_WITH_CI(szTemp, "0x"))
    1432           0 :         snprintf(szTemp, sizeof(szTemp), "0x%p", pValue);
    1433             : 
    1434       11886 :     return CPLPrintString(pszBuffer, szTemp, nMaxLen);
    1435             : }
    1436             : 
    1437             : /************************************************************************/
    1438             : /*                           CPLPrintDouble()                           */
    1439             : /************************************************************************/
    1440             : 
    1441             : /**
    1442             :  * Print double value into specified string buffer. Exponential character
    1443             :  * flag 'E' (or 'e') will be replaced with 'D', as in Fortran. Resulting
    1444             :  * string will not to be NULL-terminated.
    1445             :  *
    1446             :  * @param pszBuffer Pointer to the destination string buffer. Should be
    1447             :  * large enough to hold the resulting string. Note, that the string will
    1448             :  * not be NULL-terminated, so user should do this himself, if needed.
    1449             :  *
    1450             :  * @param pszFormat Format specifier (for example, "%16.9E").
    1451             :  *
    1452             :  * @param dfValue Numerical value to print.
    1453             :  *
    1454             :  * @param pszLocale Unused.
    1455             :  *
    1456             :  * @return Number of characters printed.
    1457             :  */
    1458             : 
    1459           0 : int CPLPrintDouble(char *pszBuffer, const char *pszFormat, double dfValue,
    1460             :                    CPL_UNUSED const char *pszLocale)
    1461             : {
    1462           0 :     if (!pszBuffer)
    1463           0 :         return 0;
    1464             : 
    1465           0 :     const int knDoubleBufferSize = 64;
    1466           0 :     char szTemp[knDoubleBufferSize] = {};
    1467             : 
    1468           0 :     CPLsnprintf(szTemp, knDoubleBufferSize, pszFormat, dfValue);
    1469           0 :     szTemp[knDoubleBufferSize - 1] = '\0';
    1470             : 
    1471           0 :     for (int i = 0; szTemp[i] != '\0'; i++)
    1472             :     {
    1473           0 :         if (szTemp[i] == 'E' || szTemp[i] == 'e')
    1474           0 :             szTemp[i] = 'D';
    1475             :     }
    1476             : 
    1477           0 :     return CPLPrintString(pszBuffer, szTemp, 64);
    1478             : }
    1479             : 
    1480             : /************************************************************************/
    1481             : /*                            CPLPrintTime()                            */
    1482             : /************************************************************************/
    1483             : 
    1484             : /**
    1485             :  * Print specified time value accordingly to the format options and
    1486             :  * specified locale name. This function does following:
    1487             :  *
    1488             :  *  - if locale parameter is not NULL, the current locale setting will be
    1489             :  *  stored and replaced with the specified one;
    1490             :  *  - format time value with the strftime(3) function;
    1491             :  *  - restore back current locale, if was saved.
    1492             :  *
    1493             :  * @param pszBuffer Pointer to the destination string buffer. Should be
    1494             :  * large enough to hold the resulting string. Note, that the string will
    1495             :  * not be NULL-terminated, so user should do this himself, if needed.
    1496             :  *
    1497             :  * @param nMaxLen Maximum length of the resulting string. If string length is
    1498             :  * greater than nMaxLen, it will be truncated.
    1499             :  *
    1500             :  * @param pszFormat Controls the output format. Options are the same as
    1501             :  * for strftime(3) function.
    1502             :  *
    1503             :  * @param poBrokenTime Pointer to the broken-down time structure. May be
    1504             :  * requested with the VSIGMTime() and VSILocalTime() functions.
    1505             :  *
    1506             :  * @param pszLocale Pointer to a character string containing locale name
    1507             :  * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not
    1508             :  * manipulate with locale settings and current process locale will be used for
    1509             :  * printing. Be aware that it may be unsuitable to use current locale for
    1510             :  * printing time, because all names will be printed in your native language,
    1511             :  * as well as time format settings also may be adjusted differently from the
    1512             :  * C/POSIX defaults. To solve these problems this option was introduced.
    1513             :  *
    1514             :  * @return Number of characters printed.
    1515             :  */
    1516             : 
    1517          33 : int CPLPrintTime(char *pszBuffer, int nMaxLen, const char *pszFormat,
    1518             :                  const struct tm *poBrokenTime, const char *pszLocale)
    1519             : {
    1520             :     char *pszTemp =
    1521          33 :         static_cast<char *>(CPLMalloc((nMaxLen + 1) * sizeof(char)));
    1522             : 
    1523          33 :     if (pszLocale && EQUAL(pszLocale, "C") &&
    1524          33 :         strcmp(pszFormat, "%a, %d %b %Y %H:%M:%S GMT") == 0)
    1525             :     {
    1526             :         // Particular case when formatting RFC822 datetime, to avoid locale
    1527             :         // change
    1528             :         static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
    1529             :                                                   "May", "Jun", "Jul", "Aug",
    1530             :                                                   "Sep", "Oct", "Nov", "Dec"};
    1531             :         static const char *const aszDayOfWeek[] = {"Sun", "Mon", "Tue", "Wed",
    1532             :                                                    "Thu", "Fri", "Sat"};
    1533          66 :         snprintf(pszTemp, nMaxLen + 1, "%s, %02d %s %04d %02d:%02d:%02d GMT",
    1534          33 :                  aszDayOfWeek[std::max(0, std::min(6, poBrokenTime->tm_wday))],
    1535          33 :                  poBrokenTime->tm_mday,
    1536          33 :                  aszMonthStr[std::max(0, std::min(11, poBrokenTime->tm_mon))],
    1537          33 :                  poBrokenTime->tm_year + 1900, poBrokenTime->tm_hour,
    1538          66 :                  poBrokenTime->tm_min, poBrokenTime->tm_sec);
    1539             :     }
    1540             :     else
    1541             :     {
    1542             : #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
    1543             :         char *pszCurLocale = NULL;
    1544             : 
    1545             :         if (pszLocale || EQUAL(pszLocale, ""))
    1546             :         {
    1547             :             // Save the current locale.
    1548             :             pszCurLocale = CPLsetlocale(LC_ALL, NULL);
    1549             :             // Set locale to the specified value.
    1550             :             CPLsetlocale(LC_ALL, pszLocale);
    1551             :         }
    1552             : #else
    1553             :         (void)pszLocale;
    1554             : #endif
    1555             : 
    1556           0 :         if (!strftime(pszTemp, nMaxLen + 1, pszFormat, poBrokenTime))
    1557           0 :             memset(pszTemp, 0, nMaxLen + 1);
    1558             : 
    1559             : #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
    1560             :         // Restore stored locale back.
    1561             :         if (pszCurLocale)
    1562             :             CPLsetlocale(LC_ALL, pszCurLocale);
    1563             : #endif
    1564             :     }
    1565             : 
    1566          33 :     const int nChars = CPLPrintString(pszBuffer, pszTemp, nMaxLen);
    1567             : 
    1568          33 :     CPLFree(pszTemp);
    1569             : 
    1570          33 :     return nChars;
    1571             : }
    1572             : 
    1573             : /************************************************************************/
    1574             : /*                       CPLVerifyConfiguration()                       */
    1575             : /************************************************************************/
    1576             : 
    1577           0 : void CPLVerifyConfiguration()
    1578             : 
    1579             : {
    1580             :     /* -------------------------------------------------------------------- */
    1581             :     /*      Verify data types.                                              */
    1582             :     /* -------------------------------------------------------------------- */
    1583             :     static_assert(sizeof(short) == 2);   // We unfortunately rely on this
    1584             :     static_assert(sizeof(int) == 4);     // We unfortunately rely on this
    1585             :     static_assert(sizeof(float) == 4);   // We unfortunately rely on this
    1586             :     static_assert(sizeof(double) == 8);  // We unfortunately rely on this
    1587             :     static_assert(sizeof(GInt64) == 8);
    1588             :     static_assert(sizeof(GInt32) == 4);
    1589             :     static_assert(sizeof(GInt16) == 2);
    1590             :     static_assert(sizeof(GByte) == 1);
    1591             : 
    1592             :     /* -------------------------------------------------------------------- */
    1593             :     /*      Verify byte order                                               */
    1594             :     /* -------------------------------------------------------------------- */
    1595             : #ifdef CPL_LSB
    1596             : #if __cplusplus >= 202002L
    1597             :     static_assert(std::endian::native == std::endian::little);
    1598             : #elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
    1599             :     static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
    1600             : #endif
    1601             : #elif defined(CPL_MSB)
    1602             : #if __cplusplus >= 202002L
    1603             :     static_assert(std::endian::native == std::endian::big);
    1604             : #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
    1605             :     static_assert(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
    1606             : #endif
    1607             : #else
    1608             : #error "CPL_LSB or CPL_MSB must be defined"
    1609             : #endif
    1610           0 : }
    1611             : 
    1612             : #ifdef DEBUG_CONFIG_OPTIONS
    1613             : 
    1614             : static CPLMutex *hRegisterConfigurationOptionMutex = nullptr;
    1615             : static std::set<CPLString> *paoGetKeys = nullptr;
    1616             : static std::set<CPLString> *paoSetKeys = nullptr;
    1617             : 
    1618             : /************************************************************************/
    1619             : /*                       CPLShowAccessedOptions()                       */
    1620             : /************************************************************************/
    1621             : 
    1622             : static void CPLShowAccessedOptions()
    1623             : {
    1624             :     std::set<CPLString>::iterator aoIter;
    1625             : 
    1626             :     printf("Configuration options accessed in reading : "); /*ok*/
    1627             :     aoIter = paoGetKeys->begin();
    1628             :     while (aoIter != paoGetKeys->end())
    1629             :     {
    1630             :         printf("%s, ", (*aoIter).c_str()); /*ok*/
    1631             :         ++aoIter;
    1632             :     }
    1633             :     printf("\n"); /*ok*/
    1634             : 
    1635             :     printf("Configuration options accessed in writing : "); /*ok*/
    1636             :     aoIter = paoSetKeys->begin();
    1637             :     while (aoIter != paoSetKeys->end())
    1638             :     {
    1639             :         printf("%s, ", (*aoIter).c_str()); /*ok*/
    1640             :         ++aoIter;
    1641             :     }
    1642             :     printf("\n"); /*ok*/
    1643             : 
    1644             :     delete paoGetKeys;
    1645             :     delete paoSetKeys;
    1646             :     paoGetKeys = nullptr;
    1647             :     paoSetKeys = nullptr;
    1648             : }
    1649             : 
    1650             : /************************************************************************/
    1651             : /*                       CPLAccessConfigOption()                        */
    1652             : /************************************************************************/
    1653             : 
    1654             : static void CPLAccessConfigOption(const char *pszKey, bool bGet)
    1655             : {
    1656             :     CPLMutexHolderD(&hRegisterConfigurationOptionMutex);
    1657             :     if (paoGetKeys == nullptr)
    1658             :     {
    1659             :         paoGetKeys = new std::set<CPLString>;
    1660             :         paoSetKeys = new std::set<CPLString>;
    1661             :         atexit(CPLShowAccessedOptions);
    1662             :     }
    1663             :     if (bGet)
    1664             :         paoGetKeys->insert(pszKey);
    1665             :     else
    1666             :         paoSetKeys->insert(pszKey);
    1667             : }
    1668             : #endif
    1669             : 
    1670             : /************************************************************************/
    1671             : /*                         CPLGetConfigOption()                         */
    1672             : /************************************************************************/
    1673             : 
    1674             : /**
    1675             :  * Get the value of a configuration option.
    1676             :  *
    1677             :  * The value is the value of a (key, value) option set with
    1678             :  * CPLSetConfigOption(), or CPLSetThreadLocalConfigOption() of the same
    1679             :  * thread. If the given option was no defined with
    1680             :  * CPLSetConfigOption(), it tries to find it in environment variables.
    1681             :  *
    1682             :  * Note: the string returned by CPLGetConfigOption() might be short-lived, and
    1683             :  * in particular it will become invalid after a call to CPLSetConfigOption()
    1684             :  * with the same key.
    1685             :  *
    1686             :  * To override temporary a potentially existing option with a new value, you
    1687             :  * can use the following snippet :
    1688             :  * \code{.cpp}
    1689             :  *     // backup old value
    1690             :  *     const char* pszOldValTmp = CPLGetConfigOption(pszKey, NULL);
    1691             :  *     char* pszOldVal = pszOldValTmp ? CPLStrdup(pszOldValTmp) : NULL;
    1692             :  *     // override with new value
    1693             :  *     CPLSetConfigOption(pszKey, pszNewVal);
    1694             :  *     // do something useful
    1695             :  *     // restore old value
    1696             :  *     CPLSetConfigOption(pszKey, pszOldVal);
    1697             :  *     CPLFree(pszOldVal);
    1698             :  * \endcode
    1699             :  *
    1700             :  * @param pszKey the key of the option to retrieve
    1701             :  * @param pszDefault a default value if the key does not match existing defined
    1702             :  *     options (may be NULL)
    1703             :  * @return the value associated to the key, or the default value if not found
    1704             :  *
    1705             :  * @see CPLSetConfigOption(), https://gdal.org/user/configoptions.html
    1706             :  */
    1707     7370680 : const char *CPL_STDCALL CPLGetConfigOption(const char *pszKey,
    1708             :                                            const char *pszDefault)
    1709             : 
    1710             : {
    1711     7370680 :     const char *pszResult = CPLGetThreadLocalConfigOption(
    1712             :         pszKey, nullptr, /* bSubstituteNullValueMarkerWithNull = */ false);
    1713             : 
    1714     7370060 :     if (pszResult == nullptr)
    1715             :     {
    1716     7326120 :         pszResult = CPLGetGlobalConfigOption(
    1717             :             pszKey, nullptr, /* bSubstituteNullValueMarkerWithNull = */ false);
    1718             :     }
    1719             : 
    1720     7371450 :     if (gbIgnoreEnvVariables)
    1721             :     {
    1722           6 :         const char *pszEnvVar = getenv(pszKey);
    1723             :         // Skipping for CPL_DEBUG to avoid infinite recursion since CPLvDebug()
    1724             :         // calls CPLGetConfigOption()...
    1725           6 :         if (pszEnvVar != nullptr && !EQUAL(pszKey, "CPL_DEBUG"))
    1726             :         {
    1727           1 :             CPLDebug("CPL",
    1728             :                      "Ignoring environment variable %s=%s because of "
    1729             :                      "ignore-env-vars=yes setting in configuration file",
    1730             :                      pszKey, pszEnvVar);
    1731             :         }
    1732             :     }
    1733     7371440 :     else if (pszResult == nullptr)
    1734             :     {
    1735     7314860 :         pszResult = getenv(pszKey);
    1736             :     }
    1737             : 
    1738     7371430 :     if (pszResult == nullptr || strcmp(pszResult, CPL_NULL_VALUE) == 0)
    1739     7303120 :         return pszDefault;
    1740             : 
    1741       68315 :     return pszResult;
    1742             : }
    1743             : 
    1744             : /************************************************************************/
    1745             : /*                        CPLGetConfigOptions()                         */
    1746             : /************************************************************************/
    1747             : 
    1748             : /**
    1749             :  * Return the list of configuration options as KEY=VALUE pairs.
    1750             :  *
    1751             :  * The list is the one set through the CPLSetConfigOption() API.
    1752             :  *
    1753             :  * Options that through environment variables or with
    1754             :  * CPLSetThreadLocalConfigOption() will *not* be listed.
    1755             :  *
    1756             :  * @return a copy of the list, to be freed with CSLDestroy().
    1757             :  */
    1758          57 : char **CPLGetConfigOptions(void)
    1759             : {
    1760         114 :     CPLMutexHolderD(&hConfigMutex);
    1761         114 :     return CSLDuplicate(const_cast<char **>(g_papszConfigOptions));
    1762             : }
    1763             : 
    1764             : /************************************************************************/
    1765             : /*                        CPLSetConfigOptions()                         */
    1766             : /************************************************************************/
    1767             : 
    1768             : /**
    1769             :  * Replace the full list of configuration options with the passed list of
    1770             :  * KEY=VALUE pairs.
    1771             :  *
    1772             :  * This has the same effect of clearing the existing list, and setting
    1773             :  * individually each pair with the CPLSetConfigOption() API.
    1774             :  *
    1775             :  * This does not affect options set through environment variables or with
    1776             :  * CPLSetThreadLocalConfigOption().
    1777             :  *
    1778             :  * The passed list is copied by the function.
    1779             :  *
    1780             :  * @param papszConfigOptions the new list (or NULL).
    1781             :  *
    1782             :  */
    1783         111 : void CPLSetConfigOptions(const char *const *papszConfigOptions)
    1784             : {
    1785         111 :     CPLMutexHolderD(&hConfigMutex);
    1786         111 :     CSLDestroy(const_cast<char **>(g_papszConfigOptions));
    1787         111 :     g_papszConfigOptions = const_cast<volatile char **>(
    1788         111 :         CSLDuplicate(const_cast<char **>(papszConfigOptions)));
    1789         111 : }
    1790             : 
    1791             : /************************************************************************/
    1792             : /*                   CPLGetThreadLocalConfigOption()                    */
    1793             : /************************************************************************/
    1794             : 
    1795             : /** Same as CPLGetConfigOption() but only with options set with
    1796             :  * CPLSetThreadLocalConfigOption() */
    1797       30861 : const char *CPL_STDCALL CPLGetThreadLocalConfigOption(const char *pszKey,
    1798             :                                                       const char *pszDefault)
    1799             : 
    1800             : {
    1801       30861 :     return CPLGetThreadLocalConfigOption(pszKey, pszDefault, true);
    1802             : }
    1803             : 
    1804             : static const char *
    1805     7401220 : CPLGetThreadLocalConfigOption(const char *pszKey, const char *pszDefault,
    1806             :                               bool bSubstituteNullValueMarkerWithNull)
    1807             : {
    1808             : #ifdef DEBUG_CONFIG_OPTIONS
    1809             :     CPLAccessConfigOption(pszKey, TRUE);
    1810             : #endif
    1811             : 
    1812     7401220 :     const char *pszResult = nullptr;
    1813             : 
    1814     7401220 :     int bMemoryError = FALSE;
    1815             :     char **papszTLConfigOptions = reinterpret_cast<char **>(
    1816     7401220 :         CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
    1817     7400950 :     if (papszTLConfigOptions != nullptr)
    1818     6883950 :         pszResult = CSLFetchNameValue(papszTLConfigOptions, pszKey);
    1819             : 
    1820     7400970 :     if (pszResult == nullptr || (bSubstituteNullValueMarkerWithNull &&
    1821         698 :                                  strcmp(pszResult, CPL_NULL_VALUE) == 0))
    1822     7356350 :         return pszDefault;
    1823             : 
    1824       44626 :     return pszResult;
    1825             : }
    1826             : 
    1827             : /************************************************************************/
    1828             : /*                      CPLGetGlobalConfigOption()                      */
    1829             : /************************************************************************/
    1830             : 
    1831             : /** Same as CPLGetConfigOption() but excludes environment variables and
    1832             :  *  options set with CPLSetThreadLocalConfigOption().
    1833             :  *  This function should generally not be used by applications, which should
    1834             :  *  use CPLGetConfigOption() instead.
    1835             :  *  @since 3.8 */
    1836        2359 : const char *CPL_STDCALL CPLGetGlobalConfigOption(const char *pszKey,
    1837             :                                                  const char *pszDefault)
    1838             : {
    1839        2359 :     return CPLGetGlobalConfigOption(
    1840        2359 :         pszKey, pszDefault, /* bSubstituteNullValueMarkerWithNull = */ true);
    1841             : }
    1842             : 
    1843             : static const char *
    1844     7328640 : CPLGetGlobalConfigOption(const char *pszKey, const char *pszDefault,
    1845             :                          bool bSubstituteNullValueMarkerWithNull)
    1846             : {
    1847             : 
    1848             : #ifdef DEBUG_CONFIG_OPTIONS
    1849             :     CPLAccessConfigOption(pszKey, TRUE);
    1850             : #endif
    1851             : 
    1852    14658500 :     CPLMutexHolderD(&hConfigMutex);
    1853             : 
    1854             :     const char *pszResult =
    1855     7329890 :         CSLFetchNameValue(const_cast<char **>(g_papszConfigOptions), pszKey);
    1856             : 
    1857     7329890 :     if (pszResult == nullptr || (bSubstituteNullValueMarkerWithNull &&
    1858         244 :                                  strcmp(pszResult, CPL_NULL_VALUE) == 0))
    1859     7317020 :         return pszDefault;
    1860             : 
    1861       12872 :     return pszResult;
    1862             : }
    1863             : 
    1864             : /************************************************************************/
    1865             : /*                   CPLSubscribeToSetConfigOption()                    */
    1866             : /************************************************************************/
    1867             : 
    1868             : /**
    1869             :  * Install a callback that will be notified of calls to CPLSetConfigOption()/
    1870             :  * CPLSetThreadLocalConfigOption()
    1871             :  *
    1872             :  * @param pfnCallback Callback. Must not be NULL
    1873             :  * @param pUserData Callback user data. May be NULL.
    1874             :  * @return subscriber ID that can be used with CPLUnsubscribeToSetConfigOption()
    1875             :  * @since GDAL 3.7
    1876             :  */
    1877             : 
    1878        1412 : int CPLSubscribeToSetConfigOption(CPLSetConfigOptionSubscriber pfnCallback,
    1879             :                                   void *pUserData)
    1880             : {
    1881        2824 :     CPLMutexHolderD(&hConfigMutex);
    1882        1417 :     for (int nId = 0;
    1883        1417 :          nId < static_cast<int>(gSetConfigOptionSubscribers.size()); ++nId)
    1884             :     {
    1885           6 :         if (!gSetConfigOptionSubscribers[nId].first)
    1886             :         {
    1887           1 :             gSetConfigOptionSubscribers[nId].first = pfnCallback;
    1888           1 :             gSetConfigOptionSubscribers[nId].second = pUserData;
    1889           1 :             return nId;
    1890             :         }
    1891             :     }
    1892        1411 :     int nId = static_cast<int>(gSetConfigOptionSubscribers.size());
    1893        1411 :     gSetConfigOptionSubscribers.push_back(
    1894        1411 :         std::pair<CPLSetConfigOptionSubscriber, void *>(pfnCallback,
    1895             :                                                         pUserData));
    1896        1411 :     return nId;
    1897             : }
    1898             : 
    1899             : /************************************************************************/
    1900             : /*                  CPLUnsubscribeToSetConfigOption()                   */
    1901             : /************************************************************************/
    1902             : 
    1903             : /**
    1904             :  * Remove a subscriber installed with CPLSubscribeToSetConfigOption()
    1905             :  *
    1906             :  * @param nId Subscriber id returned by CPLSubscribeToSetConfigOption()
    1907             :  * @since GDAL 3.7
    1908             :  */
    1909             : 
    1910           4 : void CPLUnsubscribeToSetConfigOption(int nId)
    1911             : {
    1912           8 :     CPLMutexHolderD(&hConfigMutex);
    1913           4 :     if (nId == static_cast<int>(gSetConfigOptionSubscribers.size()) - 1)
    1914             :     {
    1915           3 :         gSetConfigOptionSubscribers.resize(gSetConfigOptionSubscribers.size() -
    1916             :                                            1);
    1917             :     }
    1918           2 :     else if (nId >= 0 &&
    1919           1 :              nId < static_cast<int>(gSetConfigOptionSubscribers.size()))
    1920             :     {
    1921           1 :         gSetConfigOptionSubscribers[nId].first = nullptr;
    1922             :     }
    1923           4 : }
    1924             : 
    1925             : /************************************************************************/
    1926             : /*              NotifyOtherComponentsConfigOptionChanged()              */
    1927             : /************************************************************************/
    1928             : 
    1929       73307 : static void NotifyOtherComponentsConfigOptionChanged(const char *pszKey,
    1930             :                                                      const char *pszValue,
    1931             :                                                      bool bThreadLocal)
    1932             : {
    1933             :     // When changing authentication parameters of virtual file systems,
    1934             :     // partially invalidate cached state about file availability.
    1935       73307 :     if (STARTS_WITH_CI(pszKey, "AWS_") || STARTS_WITH_CI(pszKey, "GS_") ||
    1936       69834 :         STARTS_WITH_CI(pszKey, "GOOGLE_") ||
    1937       69777 :         STARTS_WITH_CI(pszKey, "GDAL_HTTP_HEADER_FILE") ||
    1938       69757 :         STARTS_WITH_CI(pszKey, "AZURE_") ||
    1939       69602 :         (STARTS_WITH_CI(pszKey, "SWIFT_") && !EQUAL(pszKey, "SWIFT_MAX_KEYS")))
    1940             :     {
    1941        3809 :         VSICurlAuthParametersChanged();
    1942             :     }
    1943             : 
    1944      145819 :     for (const auto &[pfnCallback, pUserData] : gSetConfigOptionSubscribers)
    1945             :     {
    1946       72544 :         if (pfnCallback)
    1947       72558 :             pfnCallback(pszKey, pszValue, bThreadLocal, pUserData);
    1948             :     }
    1949       73247 : }
    1950             : 
    1951             : /************************************************************************/
    1952             : /*                         CPLIsDebugEnabled()                          */
    1953             : /************************************************************************/
    1954             : 
    1955             : static int gnDebug = -1;
    1956             : 
    1957             : /** Returns whether CPL_DEBUG is enabled.
    1958             :  *
    1959             :  * @since 3.11
    1960             :  */
    1961       78883 : bool CPLIsDebugEnabled()
    1962             : {
    1963       78883 :     if (gnDebug < 0)
    1964             :     {
    1965             :         // Check that apszKnownConfigOptions is correctly sorted with
    1966             :         // STRCASECMP() criterion.
    1967      538974 :         for (size_t i = 1; i < CPL_ARRAYSIZE(apszKnownConfigOptions); ++i)
    1968             :         {
    1969      538488 :             if (STRCASECMP(apszKnownConfigOptions[i - 1],
    1970             :                            apszKnownConfigOptions[i]) >= 0)
    1971             :             {
    1972           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1973             :                          "ERROR: apszKnownConfigOptions[] isn't correctly "
    1974             :                          "sorted: %s >= %s",
    1975           0 :                          apszKnownConfigOptions[i - 1],
    1976           0 :                          apszKnownConfigOptions[i]);
    1977             :             }
    1978             :         }
    1979         486 :         gnDebug = CPLTestBool(CPLGetConfigOption("CPL_DEBUG", "OFF"));
    1980             :     }
    1981             : 
    1982       78868 :     return gnDebug != 0;
    1983             : }
    1984             : 
    1985             : /************************************************************************/
    1986             : /*                    CPLDeclareKnownConfigOption()                     */
    1987             : /************************************************************************/
    1988             : 
    1989             : static std::mutex goMutexDeclaredKnownConfigOptions;
    1990             : static std::set<CPLString> goSetKnownConfigOptions;
    1991             : 
    1992             : /** Declare that the specified configuration option is known.
    1993             :  *
    1994             :  * This is useful to avoid a warning to be emitted on unknown configuration
    1995             :  * options when CPL_DEBUG is enabled.
    1996             :  *
    1997             :  * @param pszKey Name of the configuration option to declare.
    1998             :  * @param pszDefinition Unused for now. Must be set to nullptr.
    1999             :  * @since 3.11
    2000             :  */
    2001           1 : void CPLDeclareKnownConfigOption(const char *pszKey,
    2002             :                                  [[maybe_unused]] const char *pszDefinition)
    2003             : {
    2004           1 :     std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
    2005           1 :     goSetKnownConfigOptions.insert(CPLString(pszKey).toupper());
    2006           1 : }
    2007             : 
    2008             : /************************************************************************/
    2009             : /*                      CPLGetKnownConfigOptions()                      */
    2010             : /************************************************************************/
    2011             : 
    2012             : /** Return the list of known configuration options.
    2013             :  *
    2014             :  * Must be freed with CSLDestroy().
    2015             :  * @since 3.11
    2016             :  */
    2017           4 : char **CPLGetKnownConfigOptions()
    2018             : {
    2019           8 :     std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
    2020           8 :     CPLStringList aosList;
    2021        4440 :     for (const char *pszKey : apszKnownConfigOptions)
    2022        4436 :         aosList.AddString(pszKey);
    2023           5 :     for (const auto &osKey : goSetKnownConfigOptions)
    2024           1 :         aosList.AddString(osKey);
    2025           8 :     return aosList.StealList();
    2026             : }
    2027             : 
    2028             : /************************************************************************/
    2029             : /*            CPLSetConfigOptionDetectUnknownConfigOption()             */
    2030             : /************************************************************************/
    2031             : 
    2032       73316 : static void CPLSetConfigOptionDetectUnknownConfigOption(const char *pszKey,
    2033             :                                                         const char *pszValue)
    2034             : {
    2035       73316 :     if (EQUAL(pszKey, "CPL_DEBUG"))
    2036             :     {
    2037         130 :         gnDebug = pszValue ? CPLTestBool(pszValue) : false;
    2038             :     }
    2039       73186 :     else if (CPLIsDebugEnabled())
    2040             :     {
    2041         272 :         if (!std::binary_search(std::begin(apszKnownConfigOptions),
    2042             :                                 std::end(apszKnownConfigOptions), pszKey,
    2043        3033 :                                 [](const char *a, const char *b)
    2044        3033 :                                 { return STRCASECMP(a, b) < 0; }))
    2045             :         {
    2046             :             bool bFound;
    2047             :             {
    2048           5 :                 std::lock_guard oLock(goMutexDeclaredKnownConfigOptions);
    2049          10 :                 bFound = cpl::contains(goSetKnownConfigOptions,
    2050           5 :                                        CPLString(pszKey).toupper());
    2051             :             }
    2052           5 :             if (!bFound)
    2053             :             {
    2054           3 :                 const char *pszOldValue = CPLGetConfigOption(pszKey, nullptr);
    2055           3 :                 if (!((!pszValue && !pszOldValue) ||
    2056           1 :                       (pszValue && pszOldValue &&
    2057           0 :                        EQUAL(pszValue, pszOldValue))))
    2058             :                 {
    2059           3 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2060             :                              "Unknown configuration option '%s'.", pszKey);
    2061             :                 }
    2062             :             }
    2063             :         }
    2064             :     }
    2065       73299 : }
    2066             : 
    2067             : /************************************************************************/
    2068             : /*                         CPLSetConfigOption()                         */
    2069             : /************************************************************************/
    2070             : 
    2071             : /**
    2072             :  * Set a configuration option for GDAL/OGR use.
    2073             :  *
    2074             :  * Those options are defined as a (key, value) couple. The value corresponding
    2075             :  * to a key can be got later with the CPLGetConfigOption() method.
    2076             :  *
    2077             :  * This mechanism is similar to environment variables, but options set with
    2078             :  * CPLSetConfigOption() overrides, for CPLGetConfigOption() point of view,
    2079             :  * values defined in the environment.
    2080             :  *
    2081             :  * If CPLSetConfigOption() is called several times with the same key, the
    2082             :  * value provided during the last call will be used.
    2083             :  *
    2084             :  * Options can also be passed on the command line of most GDAL utilities
    2085             :  * with '\--config KEY VALUE' (or '\--config KEY=VALUE' since GDAL 3.10).
    2086             :  * For example, ogrinfo \--config CPL_DEBUG ON ~/data/test/point.shp
    2087             :  *
    2088             :  * This function can also be used to clear a setting by passing NULL as the
    2089             :  * value (note: passing NULL will not unset an existing environment variable;
    2090             :  * it will just unset a value previously set by CPLSetConfigOption()).
    2091             :  *
    2092             :  * Note that setting the GDAL_CACHEMAX configuration option after at least one
    2093             :  * raster has been read will be without effect. Use GDALSetCacheMax64()
    2094             :  * instead.
    2095             :  *
    2096             :  * Starting with GDAL 3.11, if CPL_DEBUG is enabled prior to this call, and
    2097             :  * CPLSetConfigOption() is called with a key that is neither a known
    2098             :  * configuration option of GDAL itself, or one that has been declared with
    2099             :  * CPLDeclareKnownConfigOption(), a warning will be emitted.
    2100             :  *
    2101             :  * Starting with GDAL 3.13, the CPL_NULL_VALUE macro can be used as the value
    2102             :  * to indicate that callers of CPLGetConfigOption() should see the default value,
    2103             :  * instead of the value of the corresponding environment variable.
    2104             :  *
    2105             :  * @param pszKey the key of the option
    2106             :  * @param pszValue the value of the option, NULL to clear a setting, or
    2107             :  *                 macro CPL_NULL_VALUE.
    2108             :  * @see https://gdal.org/user/configoptions.html
    2109             :  */
    2110        6045 : void CPL_STDCALL CPLSetConfigOption(const char *pszKey, const char *pszValue)
    2111             : 
    2112             : {
    2113             : #ifdef DEBUG_CONFIG_OPTIONS
    2114             :     CPLAccessConfigOption(pszKey, FALSE);
    2115             : #endif
    2116       12090 :     CPLMutexHolderD(&hConfigMutex);
    2117             : 
    2118             : #ifdef OGRAPISPY_ENABLED
    2119        6045 :     OGRAPISPYCPLSetConfigOption(pszKey, pszValue);
    2120             : #endif
    2121             : 
    2122        6045 :     CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
    2123             : 
    2124        6045 :     g_papszConfigOptions = const_cast<volatile char **>(CSLSetNameValue(
    2125             :         const_cast<char **>(g_papszConfigOptions), pszKey, pszValue));
    2126             : 
    2127        6045 :     NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
    2128             :                                              /*bTheadLocal=*/false);
    2129        6045 : }
    2130             : 
    2131             : /************************************************************************/
    2132             : /*                    CPLSetThreadLocalTLSFreeFunc()                    */
    2133             : /************************************************************************/
    2134             : 
    2135             : /* non-stdcall wrapper function for CSLDestroy() (#5590) */
    2136          25 : static void CPLSetThreadLocalTLSFreeFunc(void *pData)
    2137             : {
    2138          25 :     CSLDestroy(reinterpret_cast<char **>(pData));
    2139          25 : }
    2140             : 
    2141             : /************************************************************************/
    2142             : /*                   CPLSetThreadLocalConfigOption()                    */
    2143             : /************************************************************************/
    2144             : 
    2145             : /**
    2146             :  * Set a configuration option for GDAL/OGR use.
    2147             :  *
    2148             :  * Those options are defined as a (key, value) couple. The value corresponding
    2149             :  * to a key can be got later with the CPLGetConfigOption() method.
    2150             :  *
    2151             :  * This function sets the configuration option that only applies in the
    2152             :  * current thread, as opposed to CPLSetConfigOption() which sets an option
    2153             :  * that applies on all threads. CPLSetThreadLocalConfigOption() will override
    2154             :  * the effect of CPLSetConfigOption) for the current thread.
    2155             :  *
    2156             :  * This function can also be used to clear a setting by passing NULL as the
    2157             :  * value (note: passing NULL will not unset an existing environment variable or
    2158             :  * a value set through CPLSetConfigOption();
    2159             :  * it will just unset a value previously set by
    2160             :  * CPLSetThreadLocalConfigOption()).
    2161             :  *
    2162             :  * Note that setting the GDAL_CACHEMAX configuration option after at least one
    2163             :  * raster has been read will be without effect. Use GDALSetCacheMax64()
    2164             :  * instead.
    2165             :  *
    2166             :  * Starting with GDAL 3.13, the CPL_NULL_VALUE macro can be used as the value
    2167             :  * to indicate that callers of CPLGetConfigOption() should see the default value,
    2168             :  * instead of the value of the corresponding environment variable.
    2169             :  *
    2170             :  * @param pszKey the key of the option
    2171             :  * @param pszValue the value of the option, NULL to clear a setting, or
    2172             :  *                 macro CPL_NULL_VALUE.
    2173             :  */
    2174             : 
    2175       67275 : void CPL_STDCALL CPLSetThreadLocalConfigOption(const char *pszKey,
    2176             :                                                const char *pszValue)
    2177             : 
    2178             : {
    2179             : #ifdef DEBUG_CONFIG_OPTIONS
    2180             :     CPLAccessConfigOption(pszKey, FALSE);
    2181             : #endif
    2182             : 
    2183             : #ifdef OGRAPISPY_ENABLED
    2184       67275 :     OGRAPISPYCPLSetThreadLocalConfigOption(pszKey, pszValue);
    2185             : #endif
    2186             : 
    2187       67272 :     int bMemoryError = FALSE;
    2188             :     char **papszTLConfigOptions = reinterpret_cast<char **>(
    2189       67272 :         CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
    2190       67267 :     if (bMemoryError)
    2191           0 :         return;
    2192             : 
    2193       67267 :     CPLSetConfigOptionDetectUnknownConfigOption(pszKey, pszValue);
    2194             : 
    2195             :     papszTLConfigOptions =
    2196       67255 :         CSLSetNameValue(papszTLConfigOptions, pszKey, pszValue);
    2197             : 
    2198       67242 :     CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
    2199             :                           CPLSetThreadLocalTLSFreeFunc);
    2200             : 
    2201       67218 :     NotifyOtherComponentsConfigOptionChanged(pszKey, pszValue,
    2202             :                                              /*bTheadLocal=*/true);
    2203             : }
    2204             : 
    2205             : /************************************************************************/
    2206             : /*                   CPLGetThreadLocalConfigOptions()                   */
    2207             : /************************************************************************/
    2208             : 
    2209             : /**
    2210             :  * Return the list of thread local configuration options as KEY=VALUE pairs.
    2211             :  *
    2212             :  * Options that through environment variables or with
    2213             :  * CPLSetConfigOption() will *not* be listed.
    2214             :  *
    2215             :  * @return a copy of the list, to be freed with CSLDestroy().
    2216             :  */
    2217      748521 : char **CPLGetThreadLocalConfigOptions(void)
    2218             : {
    2219      748521 :     int bMemoryError = FALSE;
    2220             :     char **papszTLConfigOptions = reinterpret_cast<char **>(
    2221      748521 :         CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
    2222      748640 :     if (bMemoryError)
    2223           0 :         return nullptr;
    2224      748640 :     return CSLDuplicate(papszTLConfigOptions);
    2225             : }
    2226             : 
    2227             : /************************************************************************/
    2228             : /*                   CPLSetThreadLocalConfigOptions()                   */
    2229             : /************************************************************************/
    2230             : 
    2231             : /**
    2232             :  * Replace the full list of thread local configuration options with the
    2233             :  * passed list of KEY=VALUE pairs.
    2234             :  *
    2235             :  * This has the same effect of clearing the existing list, and setting
    2236             :  * individually each pair with the CPLSetThreadLocalConfigOption() API.
    2237             :  *
    2238             :  * This does not affect options set through environment variables or with
    2239             :  * CPLSetConfigOption().
    2240             :  *
    2241             :  * The passed list is copied by the function.
    2242             :  *
    2243             :  * @param papszConfigOptions the new list (or NULL).
    2244             :  *
    2245             :  */
    2246     1495420 : void CPLSetThreadLocalConfigOptions(const char *const *papszConfigOptions)
    2247             : {
    2248     1495420 :     int bMemoryError = FALSE;
    2249             :     char **papszTLConfigOptions = reinterpret_cast<char **>(
    2250     1495420 :         CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
    2251     1491780 :     if (bMemoryError)
    2252           0 :         return;
    2253     1491780 :     CSLDestroy(papszTLConfigOptions);
    2254             :     papszTLConfigOptions =
    2255     1491210 :         CSLDuplicate(const_cast<char **>(papszConfigOptions));
    2256     1489960 :     CPLSetTLSWithFreeFunc(CTLS_CONFIGOPTIONS, papszTLConfigOptions,
    2257             :                           CPLSetThreadLocalTLSFreeFunc);
    2258             : }
    2259             : 
    2260             : /************************************************************************/
    2261             : /*                           CPLFreeConfig()                            */
    2262             : /************************************************************************/
    2263             : 
    2264        1874 : void CPL_STDCALL CPLFreeConfig()
    2265             : 
    2266             : {
    2267             :     {
    2268        3748 :         CPLMutexHolderD(&hConfigMutex);
    2269             : 
    2270        1874 :         CSLDestroy(const_cast<char **>(g_papszConfigOptions));
    2271        1874 :         g_papszConfigOptions = nullptr;
    2272             : 
    2273        1874 :         int bMemoryError = FALSE;
    2274             :         char **papszTLConfigOptions = reinterpret_cast<char **>(
    2275        1874 :             CPLGetTLSEx(CTLS_CONFIGOPTIONS, &bMemoryError));
    2276        1874 :         if (papszTLConfigOptions != nullptr)
    2277             :         {
    2278         268 :             CSLDestroy(papszTLConfigOptions);
    2279         268 :             CPLSetTLS(CTLS_CONFIGOPTIONS, nullptr, FALSE);
    2280             :         }
    2281             :     }
    2282        1874 :     CPLDestroyMutex(hConfigMutex);
    2283        1874 :     hConfigMutex = nullptr;
    2284        1874 : }
    2285             : 
    2286             : /************************************************************************/
    2287             : /*                    CPLLoadConfigOptionsFromFile()                    */
    2288             : /************************************************************************/
    2289             : 
    2290             : /** Load configuration from a given configuration file.
    2291             : 
    2292             : A configuration file is a text file in a .ini style format, that lists
    2293             : configuration options and their values.
    2294             : Lines starting with # are comment lines.
    2295             : 
    2296             : Example:
    2297             : \verbatim
    2298             : [configoptions]
    2299             : # set BAR as the value of configuration option FOO
    2300             : FOO=BAR
    2301             : \endverbatim
    2302             : 
    2303             : Starting with GDAL 3.5, a configuration file can also contain credentials
    2304             : (or more generally options related to a virtual file system) for a given path
    2305             : prefix, that can also be set with VSISetPathSpecificOption(). Credentials should
    2306             : be put under a [credentials] section, and for each path prefix, under a relative
    2307             : subsection whose name starts with "[." (e.g. "[.some_arbitrary_name]"), and
    2308             : whose first key is "path".
    2309             : 
    2310             : Example:
    2311             : \verbatim
    2312             : [credentials]
    2313             : 
    2314             : [.private_bucket]
    2315             : path=/vsis3/my_private_bucket
    2316             : AWS_SECRET_ACCESS_KEY=...
    2317             : AWS_ACCESS_KEY_ID=...
    2318             : 
    2319             : [.sentinel_s2_l1c]
    2320             : path=/vsis3/sentinel-s2-l1c
    2321             : AWS_REQUEST_PAYER=requester
    2322             : \endverbatim
    2323             : 
    2324             : Starting with GDAL 3.6, a leading [directives] section might be added with
    2325             : a "ignore-env-vars=yes" setting to indicate that, starting with that point,
    2326             : all environment variables should be ignored, and only configuration options
    2327             : defined in the [configoptions] sections or through the CPLSetConfigOption() /
    2328             : CPLSetThreadLocalConfigOption() functions should be taken into account.
    2329             : 
    2330             : This function is typically called by CPLLoadConfigOptionsFromPredefinedFiles()
    2331             : 
    2332             : @param pszFilename File where to load configuration from.
    2333             : @param bOverrideEnvVars Whether configuration options from the configuration
    2334             :                         file should override environment variables.
    2335             : @since GDAL 3.3
    2336             :  */
    2337        3764 : void CPLLoadConfigOptionsFromFile(const char *pszFilename, int bOverrideEnvVars)
    2338             : {
    2339        3764 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    2340        3764 :     if (fp == nullptr)
    2341        3755 :         return;
    2342           9 :     CPLDebug("CPL", "Loading configuration from %s", pszFilename);
    2343             :     const char *pszLine;
    2344             :     enum class Section
    2345             :     {
    2346             :         NONE,
    2347             :         GENERAL,
    2348             :         CONFIG_OPTIONS,
    2349             :         CREDENTIALS,
    2350             :     };
    2351           9 :     Section eCurrentSection = Section::NONE;
    2352           9 :     bool bInSubsection = false;
    2353          18 :     std::string osPath;
    2354           9 :     int nSectionCounter = 0;
    2355             : 
    2356          56 :     const auto IsSpaceOnly = [](const char *pszStr)
    2357             :     {
    2358          56 :         for (; *pszStr; ++pszStr)
    2359             :         {
    2360          47 :             if (!isspace(static_cast<unsigned char>(*pszStr)))
    2361          41 :                 return false;
    2362             :         }
    2363           9 :         return true;
    2364             :     };
    2365             : 
    2366          59 :     while ((pszLine = CPLReadLine2L(fp, -1, nullptr)) != nullptr)
    2367             :     {
    2368          50 :         if (IsSpaceOnly(pszLine))
    2369             :         {
    2370             :             // Blank line
    2371             :         }
    2372          41 :         else if (pszLine[0] == '#')
    2373             :         {
    2374             :             // Comment line
    2375             :         }
    2376          35 :         else if (strcmp(pszLine, "[configoptions]") == 0)
    2377             :         {
    2378           6 :             nSectionCounter++;
    2379           6 :             eCurrentSection = Section::CONFIG_OPTIONS;
    2380             :         }
    2381          29 :         else if (strcmp(pszLine, "[credentials]") == 0)
    2382             :         {
    2383           4 :             nSectionCounter++;
    2384           4 :             eCurrentSection = Section::CREDENTIALS;
    2385           4 :             bInSubsection = false;
    2386           4 :             osPath.clear();
    2387             :         }
    2388          25 :         else if (strcmp(pszLine, "[directives]") == 0)
    2389             :         {
    2390           2 :             nSectionCounter++;
    2391           2 :             if (nSectionCounter != 1)
    2392             :             {
    2393           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2394             :                          "The [directives] section should be the first one in "
    2395             :                          "the file, otherwise some its settings might not be "
    2396             :                          "used correctly.");
    2397             :             }
    2398           2 :             eCurrentSection = Section::GENERAL;
    2399             :         }
    2400          23 :         else if (eCurrentSection == Section::GENERAL)
    2401             :         {
    2402           2 :             char *pszKey = nullptr;
    2403           2 :             const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
    2404           2 :             if (pszKey && pszValue)
    2405             :             {
    2406           2 :                 if (strcmp(pszKey, "ignore-env-vars") == 0)
    2407             :                 {
    2408           2 :                     gbIgnoreEnvVariables = CPLTestBool(pszValue);
    2409             :                 }
    2410             :                 else
    2411             :                 {
    2412           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2413             :                              "Ignoring %s line in [directives] section",
    2414             :                              pszLine);
    2415             :                 }
    2416             :             }
    2417           2 :             CPLFree(pszKey);
    2418             :         }
    2419          21 :         else if (eCurrentSection == Section::CREDENTIALS)
    2420             :         {
    2421          15 :             if (strncmp(pszLine, "[.", 2) == 0)
    2422             :             {
    2423           4 :                 bInSubsection = true;
    2424           4 :                 osPath.clear();
    2425             :             }
    2426          11 :             else if (bInSubsection)
    2427             :             {
    2428          10 :                 char *pszKey = nullptr;
    2429          10 :                 const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
    2430          10 :                 if (pszKey && pszValue)
    2431             :                 {
    2432          10 :                     if (strcmp(pszKey, "path") == 0)
    2433             :                     {
    2434           4 :                         if (!osPath.empty())
    2435             :                         {
    2436           1 :                             CPLError(
    2437             :                                 CE_Warning, CPLE_AppDefined,
    2438             :                                 "Duplicated 'path' key in the same subsection. "
    2439             :                                 "Ignoring %s=%s",
    2440             :                                 pszKey, pszValue);
    2441             :                         }
    2442             :                         else
    2443             :                         {
    2444           3 :                             osPath = pszValue;
    2445             :                         }
    2446             :                     }
    2447           6 :                     else if (osPath.empty())
    2448             :                     {
    2449           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2450             :                                  "First entry in a credentials subsection "
    2451             :                                  "should be 'path'.");
    2452             :                     }
    2453             :                     else
    2454             :                     {
    2455           5 :                         VSISetPathSpecificOption(osPath.c_str(), pszKey,
    2456             :                                                  pszValue);
    2457             :                     }
    2458             :                 }
    2459          10 :                 CPLFree(pszKey);
    2460             :             }
    2461           1 :             else if (pszLine[0] == '[')
    2462             :             {
    2463           0 :                 eCurrentSection = Section::NONE;
    2464             :             }
    2465             :             else
    2466             :             {
    2467           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2468             :                          "Ignoring content in [credential] section that is not "
    2469             :                          "in a [.xxxxx] subsection");
    2470             :             }
    2471             :         }
    2472           6 :         else if (pszLine[0] == '[')
    2473             :         {
    2474           0 :             eCurrentSection = Section::NONE;
    2475             :         }
    2476           6 :         else if (eCurrentSection == Section::CONFIG_OPTIONS)
    2477             :         {
    2478           6 :             char *pszKey = nullptr;
    2479           6 :             const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
    2480           6 :             if (pszKey && pszValue)
    2481             :             {
    2482          11 :                 if (bOverrideEnvVars || gbIgnoreEnvVariables ||
    2483           5 :                     getenv(pszKey) == nullptr)
    2484             :                 {
    2485           5 :                     CPLDebugOnly("CPL", "Setting configuration option %s=%s",
    2486             :                                  pszKey, pszValue);
    2487           5 :                     CPLSetConfigOption(pszKey, pszValue);
    2488             :                 }
    2489             :                 else
    2490             :                 {
    2491           1 :                     CPLDebug("CPL",
    2492             :                              "Ignoring configuration option %s=%s from "
    2493             :                              "configuration file as it is already set "
    2494             :                              "as an environment variable",
    2495             :                              pszKey, pszValue);
    2496             :                 }
    2497             :             }
    2498           6 :             CPLFree(pszKey);
    2499             :         }
    2500             :     }
    2501           9 :     VSIFCloseL(fp);
    2502             : }
    2503             : 
    2504             : /************************************************************************/
    2505             : /*              CPLLoadConfigOptionsFromPredefinedFiles()               */
    2506             : /************************************************************************/
    2507             : 
    2508             : /** Load configuration from a set of predefined files.
    2509             :  *
    2510             :  * If the environment variable (or configuration option) GDAL_CONFIG_FILE is
    2511             :  * set, then CPLLoadConfigOptionsFromFile() will be called with the value of
    2512             :  * this configuration option as the file location.
    2513             :  *
    2514             :  * Otherwise, for Unix builds, CPLLoadConfigOptionsFromFile() will be called
    2515             :  * with ${sysconfdir}/gdal/gdalrc first where ${sysconfdir} evaluates
    2516             :  * to ${prefix}/etc, unless the \--sysconfdir switch of configure has been
    2517             :  * invoked.
    2518             :  *
    2519             :  * Then CPLLoadConfigOptionsFromFile() will be called with ${HOME}/.gdal/gdalrc
    2520             :  * on Unix builds (potentially overriding what was loaded with the sysconfdir)
    2521             :  * or ${USERPROFILE}/.gdal/gdalrc on Windows builds.
    2522             :  *
    2523             :  * CPLLoadConfigOptionsFromFile() will be called with bOverrideEnvVars = false,
    2524             :  * that is the value of environment variables previously set will be used
    2525             :  * instead of the value set in the configuration files (unless the configuration
    2526             :  * file contains a leading [directives] section with a "ignore-env-vars=yes"
    2527             :  * setting).
    2528             :  *
    2529             :  * This function is automatically called by GDALDriverManager() constructor
    2530             :  *
    2531             :  * @since GDAL 3.3
    2532             :  */
    2533        1879 : void CPLLoadConfigOptionsFromPredefinedFiles()
    2534             : {
    2535        1879 :     const char *pszFile = CPLGetConfigOption("GDAL_CONFIG_FILE", nullptr);
    2536        1879 :     if (pszFile != nullptr)
    2537             :     {
    2538           2 :         CPLLoadConfigOptionsFromFile(pszFile, false);
    2539             :     }
    2540             :     else
    2541             :     {
    2542             : #ifdef SYSCONFDIR
    2543        1877 :         CPLLoadConfigOptionsFromFile(
    2544        3754 :             CPLFormFilenameSafe(
    2545        3754 :                 CPLFormFilenameSafe(SYSCONFDIR, "gdal", nullptr).c_str(),
    2546             :                 "gdalrc", nullptr)
    2547             :                 .c_str(),
    2548             :             false);
    2549             : #endif
    2550             : 
    2551             : #ifdef _WIN32
    2552             :         const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
    2553             : #else
    2554        1877 :         const char *pszHome = CPLGetConfigOption("HOME", nullptr);
    2555             : #endif
    2556        1877 :         if (pszHome != nullptr)
    2557             :         {
    2558        1877 :             CPLLoadConfigOptionsFromFile(
    2559        3754 :                 CPLFormFilenameSafe(
    2560        3754 :                     CPLFormFilenameSafe(pszHome, ".gdal", nullptr).c_str(),
    2561             :                     "gdalrc", nullptr)
    2562             :                     .c_str(),
    2563             :                 false);
    2564             :         }
    2565             :     }
    2566        1879 : }
    2567             : 
    2568             : /************************************************************************/
    2569             : /*                              CPLStat()                               */
    2570             : /************************************************************************/
    2571             : 
    2572             : /** Same as VSIStat() except it works on "C:" as if it were "C:\". */
    2573             : 
    2574           0 : int CPLStat(const char *pszPath, VSIStatBuf *psStatBuf)
    2575             : 
    2576             : {
    2577           0 :     if (strlen(pszPath) == 2 && pszPath[1] == ':')
    2578             :     {
    2579           0 :         char szAltPath[4] = {pszPath[0], pszPath[1], '\\', '\0'};
    2580           0 :         return VSIStat(szAltPath, psStatBuf);
    2581             :     }
    2582             : 
    2583           0 :     return VSIStat(pszPath, psStatBuf);
    2584             : }
    2585             : 
    2586             : /************************************************************************/
    2587             : /*                            proj_strtod()                             */
    2588             : /************************************************************************/
    2589          18 : static double proj_strtod(char *nptr, char **endptr)
    2590             : 
    2591             : {
    2592          18 :     char c = '\0';
    2593          18 :     char *cp = nptr;
    2594             : 
    2595             :     // Scan for characters which cause problems with VC++ strtod().
    2596          84 :     while ((c = *cp) != '\0')
    2597             :     {
    2598          72 :         if (c == 'd' || c == 'D')
    2599             :         {
    2600             :             // Found one, so NUL it out, call strtod(),
    2601             :             // then restore it and return.
    2602           6 :             *cp = '\0';
    2603           6 :             const double result = CPLStrtod(nptr, endptr);
    2604           6 :             *cp = c;
    2605           6 :             return result;
    2606             :         }
    2607          66 :         ++cp;
    2608             :     }
    2609             : 
    2610             :     // No offending characters, just handle normally.
    2611             : 
    2612          12 :     return CPLStrtod(nptr, endptr);
    2613             : }
    2614             : 
    2615             : /************************************************************************/
    2616             : /*                            CPLDMSToDec()                             */
    2617             : /************************************************************************/
    2618             : 
    2619             : static const char *sym = "NnEeSsWw";
    2620             : constexpr double vm[] = {1.0, 0.0166666666667, 0.00027777778};
    2621             : 
    2622             : /** CPLDMSToDec */
    2623           6 : double CPLDMSToDec(const char *is)
    2624             : 
    2625             : {
    2626             :     // Copy string into work space.
    2627           6 :     while (isspace(static_cast<unsigned char>(*is)))
    2628           0 :         ++is;
    2629             : 
    2630           6 :     const char *p = is;
    2631           6 :     char work[64] = {};
    2632           6 :     char *s = work;
    2633           6 :     int n = sizeof(work);
    2634          60 :     for (; isgraph(*p) && --n;)
    2635          54 :         *s++ = *p++;
    2636           6 :     *s = '\0';
    2637             :     // It is possible that a really odd input (like lots of leading
    2638             :     // zeros) could be truncated in copying into work.  But...
    2639           6 :     s = work;
    2640           6 :     int sign = *s;
    2641             : 
    2642           6 :     if (sign == '+' || sign == '-')
    2643           0 :         s++;
    2644             :     else
    2645           6 :         sign = '+';
    2646             : 
    2647           6 :     int nl = 0;
    2648           6 :     double v = 0.0;
    2649          24 :     for (; nl < 3; nl = n + 1)
    2650             :     {
    2651          18 :         if (!(isdigit(static_cast<unsigned char>(*s)) || *s == '.'))
    2652           0 :             break;
    2653          18 :         const double tv = proj_strtod(s, &s);
    2654          18 :         if (tv == HUGE_VAL)
    2655           0 :             return tv;
    2656          18 :         switch (*s)
    2657             :         {
    2658           6 :             case 'D':
    2659             :             case 'd':
    2660           6 :                 n = 0;
    2661           6 :                 break;
    2662           6 :             case '\'':
    2663           6 :                 n = 1;
    2664           6 :                 break;
    2665           6 :             case '"':
    2666           6 :                 n = 2;
    2667           6 :                 break;
    2668           0 :             case 'r':
    2669             :             case 'R':
    2670           0 :                 if (nl)
    2671             :                 {
    2672           0 :                     return 0.0;
    2673             :                 }
    2674           0 :                 ++s;
    2675           0 :                 v = tv;
    2676           0 :                 goto skip;
    2677           0 :             default:
    2678           0 :                 v += tv * vm[nl];
    2679           0 :             skip:
    2680           0 :                 n = 4;
    2681           0 :                 continue;
    2682             :         }
    2683          18 :         if (n < nl)
    2684             :         {
    2685           0 :             return 0.0;
    2686             :         }
    2687          18 :         v += tv * vm[n];
    2688          18 :         ++s;
    2689             :     }
    2690             :     // Postfix sign.
    2691           6 :     if (*s && ((p = strchr(sym, *s))) != nullptr)
    2692             :     {
    2693           0 :         sign = (p - sym) >= 4 ? '-' : '+';
    2694           0 :         ++s;
    2695             :     }
    2696           6 :     if (sign == '-')
    2697           0 :         v = -v;
    2698             : 
    2699           6 :     return v;
    2700             : }
    2701             : 
    2702             : /************************************************************************/
    2703             : /*                            CPLDecToDMS()                             */
    2704             : /************************************************************************/
    2705             : 
    2706             : /** Translate a decimal degrees value to a DMS string with hemisphere. */
    2707             : 
    2708         630 : const char *CPLDecToDMS(double dfAngle, const char *pszAxis, int nPrecision)
    2709             : 
    2710             : {
    2711         630 :     VALIDATE_POINTER1(pszAxis, "CPLDecToDMS", "");
    2712             : 
    2713         630 :     if (std::isnan(dfAngle))
    2714           0 :         return "Invalid angle";
    2715             : 
    2716         630 :     const double dfEpsilon = (0.5 / 3600.0) * pow(0.1, nPrecision);
    2717         630 :     const double dfABSAngle = std::abs(dfAngle) + dfEpsilon;
    2718         630 :     if (dfABSAngle > 361.0)
    2719             :     {
    2720           0 :         return "Invalid angle";
    2721             :     }
    2722             : 
    2723         630 :     const int nDegrees = static_cast<int>(dfABSAngle);
    2724         630 :     const int nMinutes = static_cast<int>((dfABSAngle - nDegrees) * 60);
    2725         630 :     double dfSeconds = dfABSAngle * 3600 - nDegrees * 3600 - nMinutes * 60;
    2726             : 
    2727         630 :     if (dfSeconds > dfEpsilon * 3600.0)
    2728         624 :         dfSeconds -= dfEpsilon * 3600.0;
    2729             : 
    2730         630 :     const char *pszHemisphere = nullptr;
    2731         630 :     if (EQUAL(pszAxis, "Long") && dfAngle < 0.0)
    2732         278 :         pszHemisphere = "W";
    2733         352 :     else if (EQUAL(pszAxis, "Long"))
    2734          37 :         pszHemisphere = "E";
    2735         315 :     else if (dfAngle < 0.0)
    2736          22 :         pszHemisphere = "S";
    2737             :     else
    2738         293 :         pszHemisphere = "N";
    2739             : 
    2740         630 :     char szFormat[30] = {};
    2741         630 :     CPLsnprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
    2742             :                 nPrecision + 3, nPrecision, pszHemisphere);
    2743             : 
    2744             :     static CPL_THREADLOCAL char szBuffer[50] = {};
    2745         630 :     CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes,
    2746             :                 dfSeconds);
    2747             : 
    2748         630 :     return szBuffer;
    2749             : }
    2750             : 
    2751             : /************************************************************************/
    2752             : /*                         CPLPackedDMSToDec()                          */
    2753             : /************************************************************************/
    2754             : 
    2755             : /**
    2756             :  * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
    2757             :  *
    2758             :  * This function converts a packed DMS angle to seconds. The standard
    2759             :  * packed DMS format is:
    2760             :  *
    2761             :  *  degrees * 1000000 + minutes * 1000 + seconds
    2762             :  *
    2763             :  * Example:     angle = 120025045.25 yields
    2764             :  *              deg = 120
    2765             :  *              min = 25
    2766             :  *              sec = 45.25
    2767             :  *
    2768             :  * The algorithm used for the conversion is as follows:
    2769             :  *
    2770             :  * 1.  The absolute value of the angle is used.
    2771             :  *
    2772             :  * 2.  The degrees are separated out:
    2773             :  *     deg = angle/1000000                    (fractional portion truncated)
    2774             :  *
    2775             :  * 3.  The minutes are separated out:
    2776             :  *     min = (angle - deg * 1000000) / 1000   (fractional portion truncated)
    2777             :  *
    2778             :  * 4.  The seconds are then computed:
    2779             :  *     sec = angle - deg * 1000000 - min * 1000
    2780             :  *
    2781             :  * 5.  The total angle in seconds is computed:
    2782             :  *     sec = deg * 3600.0 + min * 60.0 + sec
    2783             :  *
    2784             :  * 6.  The sign of sec is set to that of the input angle.
    2785             :  *
    2786             :  * Packed DMS values used by the USGS GCTP package and probably by other
    2787             :  * software.
    2788             :  *
    2789             :  * NOTE: This code does not validate input value. If you give the wrong
    2790             :  * value, you will get the wrong result.
    2791             :  *
    2792             :  * @param dfPacked Angle in packed DMS format.
    2793             :  *
    2794             :  * @return Angle in decimal degrees.
    2795             :  *
    2796             :  */
    2797             : 
    2798          55 : double CPLPackedDMSToDec(double dfPacked)
    2799             : {
    2800          55 :     const double dfSign = dfPacked < 0.0 ? -1 : 1;
    2801             : 
    2802          55 :     double dfSeconds = std::abs(dfPacked);
    2803          55 :     double dfDegrees = floor(dfSeconds / 1000000.0);
    2804          55 :     dfSeconds -= dfDegrees * 1000000.0;
    2805          55 :     const double dfMinutes = floor(dfSeconds / 1000.0);
    2806          55 :     dfSeconds -= dfMinutes * 1000.0;
    2807          55 :     dfSeconds = dfSign * (dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds);
    2808          55 :     dfDegrees = dfSeconds / 3600.0;
    2809             : 
    2810          55 :     return dfDegrees;
    2811             : }
    2812             : 
    2813             : /************************************************************************/
    2814             : /*                         CPLDecToPackedDMS()                          */
    2815             : /************************************************************************/
    2816             : /**
    2817             :  * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
    2818             :  *
    2819             :  * This function converts a value, specified in decimal degrees into
    2820             :  * packed DMS angle. The standard packed DMS format is:
    2821             :  *
    2822             :  *  degrees * 1000000 + minutes * 1000 + seconds
    2823             :  *
    2824             :  * See also CPLPackedDMSToDec().
    2825             :  *
    2826             :  * @param dfDec Angle in decimal degrees.
    2827             :  *
    2828             :  * @return Angle in packed DMS format.
    2829             :  *
    2830             :  */
    2831             : 
    2832           8 : double CPLDecToPackedDMS(double dfDec)
    2833             : {
    2834           8 :     const double dfSign = dfDec < 0.0 ? -1 : 1;
    2835             : 
    2836           8 :     dfDec = std::abs(dfDec);
    2837           8 :     const double dfDegrees = floor(dfDec);
    2838           8 :     const double dfMinutes = floor((dfDec - dfDegrees) * 60.0);
    2839           8 :     const double dfSeconds = (dfDec - dfDegrees) * 3600.0 - dfMinutes * 60.0;
    2840             : 
    2841           8 :     return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds);
    2842             : }
    2843             : 
    2844             : /************************************************************************/
    2845             : /*                         CPLStringToComplex()                         */
    2846             : /************************************************************************/
    2847             : 
    2848             : /** Fetch the real and imaginary part of a serialized complex number */
    2849        4707 : CPLErr CPL_DLL CPLStringToComplex(const char *pszString, double *pdfReal,
    2850             :                                   double *pdfImag)
    2851             : 
    2852             : {
    2853        4707 :     while (*pszString == ' ')
    2854           1 :         pszString++;
    2855             : 
    2856             :     char *end;
    2857        4706 :     *pdfReal = CPLStrtod(pszString, &end);
    2858             : 
    2859        4706 :     int iPlus = -1;
    2860        4706 :     int iImagEnd = -1;
    2861             : 
    2862        4706 :     if (pszString == end)
    2863             :     {
    2864           5 :         goto error;
    2865             :     }
    2866             : 
    2867        4701 :     *pdfImag = 0.0;
    2868             : 
    2869        4755 :     for (int i = static_cast<int>(end - pszString);
    2870        4755 :          i < 100 && pszString[i] != '\0' && pszString[i] != ' '; i++)
    2871             :     {
    2872          56 :         if (pszString[i] == '+')
    2873             :         {
    2874           8 :             if (iPlus != -1)
    2875           0 :                 goto error;
    2876           8 :             iPlus = i;
    2877             :         }
    2878          56 :         if (pszString[i] == '-')
    2879             :         {
    2880           2 :             if (iPlus != -1)
    2881           1 :                 goto error;
    2882           1 :             iPlus = i;
    2883             :         }
    2884          55 :         if (pszString[i] == 'i')
    2885             :         {
    2886           9 :             if (iPlus == -1)
    2887           1 :                 goto error;
    2888           8 :             iImagEnd = i;
    2889             :         }
    2890             :     }
    2891             : 
    2892             :     // If we have a "+" or "-" we must also have an "i"
    2893        4699 :     if ((iPlus == -1) != (iImagEnd == -1))
    2894             :     {
    2895           1 :         goto error;
    2896             :     }
    2897             : 
    2898             :     // Parse imaginary component, if any
    2899        4698 :     if (iPlus > -1)
    2900             :     {
    2901           7 :         *pdfImag = CPLStrtod(pszString + iPlus, &end);
    2902             :     }
    2903             : 
    2904             :     // Check everything remaining is whitespace
    2905        4703 :     for (; *end != '\0'; end++)
    2906             :     {
    2907          11 :         if (!isspace(*end) && end - pszString != iImagEnd)
    2908             :         {
    2909           6 :             goto error;
    2910             :         }
    2911             :     }
    2912             : 
    2913        4692 :     return CE_None;
    2914             : 
    2915          14 : error:
    2916          14 :     CPLError(CE_Failure, CPLE_AppDefined, "Failed to parse number: %s",
    2917             :              pszString);
    2918          14 :     return CE_Failure;
    2919             : }
    2920             : 
    2921             : /************************************************************************/
    2922             : /*                           CPLOpenShared()                            */
    2923             : /************************************************************************/
    2924             : 
    2925             : /**
    2926             :  * Open a shared file handle.
    2927             :  *
    2928             :  * Some operating systems have limits on the number of file handles that can
    2929             :  * be open at one time.  This function attempts to maintain a registry of
    2930             :  * already open file handles, and reuse existing ones if the same file
    2931             :  * is requested by another part of the application.
    2932             :  *
    2933             :  * Note that access is only shared for access types "r", "rb", "r+" and
    2934             :  * "rb+".  All others will just result in direct VSIOpen() calls.  Keep in
    2935             :  * mind that a file is only reused if the file name is exactly the same.
    2936             :  * Different names referring to the same file will result in different
    2937             :  * handles.
    2938             :  *
    2939             :  * The VSIFOpen() or VSIFOpenL() function is used to actually open the file,
    2940             :  * when an existing file handle can't be shared.
    2941             :  *
    2942             :  * @param pszFilename the name of the file to open.
    2943             :  * @param pszAccess the normal fopen()/VSIFOpen() style access string.
    2944             :  * @param bLargeIn If TRUE VSIFOpenL() (for large files) will be used instead of
    2945             :  * VSIFOpen().
    2946             :  *
    2947             :  * @return a file handle or NULL if opening fails.
    2948             :  */
    2949             : 
    2950          39 : FILE *CPLOpenShared(const char *pszFilename, const char *pszAccess,
    2951             :                     int bLargeIn)
    2952             : 
    2953             : {
    2954          39 :     const bool bLarge = CPL_TO_BOOL(bLargeIn);
    2955          78 :     CPLMutexHolderD(&hSharedFileMutex);
    2956          39 :     const GIntBig nPID = CPLGetPID();
    2957             : 
    2958             :     /* -------------------------------------------------------------------- */
    2959             :     /*      Is there an existing file we can use?                           */
    2960             :     /* -------------------------------------------------------------------- */
    2961          39 :     const bool bReuse = EQUAL(pszAccess, "rb") || EQUAL(pszAccess, "rb+");
    2962             : 
    2963          43 :     for (int i = 0; bReuse && i < nSharedFileCount; i++)
    2964             :     {
    2965          20 :         if (strcmp(pasSharedFileList[i].pszFilename, pszFilename) == 0 &&
    2966           4 :             !bLarge == !pasSharedFileList[i].bLarge &&
    2967          16 :             EQUAL(pasSharedFileList[i].pszAccess, pszAccess) &&
    2968           4 :             nPID == pasSharedFileListExtra[i].nPID)
    2969             :         {
    2970           4 :             pasSharedFileList[i].nRefCount++;
    2971           4 :             return pasSharedFileList[i].fp;
    2972             :         }
    2973             :     }
    2974             : 
    2975             :     /* -------------------------------------------------------------------- */
    2976             :     /*      Open the file.                                                  */
    2977             :     /* -------------------------------------------------------------------- */
    2978             :     FILE *fp = bLarge
    2979          35 :                    ? reinterpret_cast<FILE *>(VSIFOpenL(pszFilename, pszAccess))
    2980           0 :                    : VSIFOpen(pszFilename, pszAccess);
    2981             : 
    2982          35 :     if (fp == nullptr)
    2983           9 :         return nullptr;
    2984             : 
    2985             :     /* -------------------------------------------------------------------- */
    2986             :     /*      Add an entry to the list.                                       */
    2987             :     /* -------------------------------------------------------------------- */
    2988          26 :     nSharedFileCount++;
    2989             : 
    2990          26 :     pasSharedFileList = static_cast<CPLSharedFileInfo *>(
    2991          52 :         CPLRealloc(const_cast<CPLSharedFileInfo *>(pasSharedFileList),
    2992          26 :                    sizeof(CPLSharedFileInfo) * nSharedFileCount));
    2993          26 :     pasSharedFileListExtra = static_cast<CPLSharedFileInfoExtra *>(
    2994          52 :         CPLRealloc(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra),
    2995          26 :                    sizeof(CPLSharedFileInfoExtra) * nSharedFileCount));
    2996             : 
    2997          26 :     pasSharedFileList[nSharedFileCount - 1].fp = fp;
    2998          26 :     pasSharedFileList[nSharedFileCount - 1].nRefCount = 1;
    2999          26 :     pasSharedFileList[nSharedFileCount - 1].bLarge = bLarge;
    3000          52 :     pasSharedFileList[nSharedFileCount - 1].pszFilename =
    3001          26 :         CPLStrdup(pszFilename);
    3002          26 :     pasSharedFileList[nSharedFileCount - 1].pszAccess = CPLStrdup(pszAccess);
    3003          26 :     pasSharedFileListExtra[nSharedFileCount - 1].nPID = nPID;
    3004             : 
    3005          26 :     return fp;
    3006             : }
    3007             : 
    3008             : /************************************************************************/
    3009             : /*                           CPLCloseShared()                           */
    3010             : /************************************************************************/
    3011             : 
    3012             : /**
    3013             :  * Close shared file.
    3014             :  *
    3015             :  * Dereferences the indicated file handle, and closes it if the reference
    3016             :  * count has dropped to zero.  A CPLError() is issued if the file is not
    3017             :  * in the shared file list.
    3018             :  *
    3019             :  * @param fp file handle from CPLOpenShared() to deaccess.
    3020             :  */
    3021             : 
    3022          30 : void CPLCloseShared(FILE *fp)
    3023             : 
    3024             : {
    3025          30 :     CPLMutexHolderD(&hSharedFileMutex);
    3026             : 
    3027             :     /* -------------------------------------------------------------------- */
    3028             :     /*      Search for matching information.                                */
    3029             :     /* -------------------------------------------------------------------- */
    3030          30 :     int i = 0;
    3031          32 :     for (; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++)
    3032             :     {
    3033             :     }
    3034             : 
    3035          30 :     if (i == nSharedFileCount)
    3036             :     {
    3037           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3038             :                  "Unable to find file handle %p in CPLCloseShared().", fp);
    3039           0 :         return;
    3040             :     }
    3041             : 
    3042             :     /* -------------------------------------------------------------------- */
    3043             :     /*      Dereference and return if there are still some references.      */
    3044             :     /* -------------------------------------------------------------------- */
    3045          30 :     if (--pasSharedFileList[i].nRefCount > 0)
    3046           4 :         return;
    3047             : 
    3048             :     /* -------------------------------------------------------------------- */
    3049             :     /*      Close the file, and remove the information.                     */
    3050             :     /* -------------------------------------------------------------------- */
    3051          26 :     if (pasSharedFileList[i].bLarge)
    3052             :     {
    3053          26 :         if (VSIFCloseL(reinterpret_cast<VSILFILE *>(pasSharedFileList[i].fp)) !=
    3054             :             0)
    3055             :         {
    3056           0 :             CPLError(CE_Failure, CPLE_FileIO, "Error while closing %s",
    3057           0 :                      pasSharedFileList[i].pszFilename);
    3058             :         }
    3059             :     }
    3060             :     else
    3061             :     {
    3062           0 :         VSIFClose(pasSharedFileList[i].fp);
    3063             :     }
    3064             : 
    3065          26 :     CPLFree(pasSharedFileList[i].pszFilename);
    3066          26 :     CPLFree(pasSharedFileList[i].pszAccess);
    3067             : 
    3068          26 :     nSharedFileCount--;
    3069          26 :     memmove(
    3070          26 :         const_cast<CPLSharedFileInfo *>(pasSharedFileList + i),
    3071          26 :         const_cast<CPLSharedFileInfo *>(pasSharedFileList + nSharedFileCount),
    3072             :         sizeof(CPLSharedFileInfo));
    3073          26 :     memmove(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra + i),
    3074          26 :             const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra +
    3075          26 :                                                  nSharedFileCount),
    3076             :             sizeof(CPLSharedFileInfoExtra));
    3077             : 
    3078          26 :     if (nSharedFileCount == 0)
    3079             :     {
    3080          23 :         CPLFree(const_cast<CPLSharedFileInfo *>(pasSharedFileList));
    3081          23 :         pasSharedFileList = nullptr;
    3082          23 :         CPLFree(const_cast<CPLSharedFileInfoExtra *>(pasSharedFileListExtra));
    3083          23 :         pasSharedFileListExtra = nullptr;
    3084             :     }
    3085             : }
    3086             : 
    3087             : /************************************************************************/
    3088             : /*                     CPLCleanupSharedFileMutex()                      */
    3089             : /************************************************************************/
    3090             : 
    3091        1303 : void CPLCleanupSharedFileMutex()
    3092             : {
    3093        1303 :     if (hSharedFileMutex != nullptr)
    3094             :     {
    3095           0 :         CPLDestroyMutex(hSharedFileMutex);
    3096           0 :         hSharedFileMutex = nullptr;
    3097             :     }
    3098        1303 : }
    3099             : 
    3100             : /************************************************************************/
    3101             : /*                          CPLGetSharedList()                          */
    3102             : /************************************************************************/
    3103             : 
    3104             : /**
    3105             :  * Fetch list of open shared files.
    3106             :  *
    3107             :  * @param pnCount place to put the count of entries.
    3108             :  *
    3109             :  * @return the pointer to the first in the array of shared file info
    3110             :  * structures.
    3111             :  */
    3112             : 
    3113           0 : CPLSharedFileInfo *CPLGetSharedList(int *pnCount)
    3114             : 
    3115             : {
    3116           0 :     if (pnCount != nullptr)
    3117           0 :         *pnCount = nSharedFileCount;
    3118             : 
    3119           0 :     return const_cast<CPLSharedFileInfo *>(pasSharedFileList);
    3120             : }
    3121             : 
    3122             : /************************************************************************/
    3123             : /*                         CPLDumpSharedList()                          */
    3124             : /************************************************************************/
    3125             : 
    3126             : /**
    3127             :  * Report open shared files.
    3128             :  *
    3129             :  * Dumps all open shared files to the indicated file handle.  If the
    3130             :  * file handle is NULL information is sent via the CPLDebug() call.
    3131             :  *
    3132             :  * @param fp File handle to write to.
    3133             :  */
    3134             : 
    3135         103 : void CPLDumpSharedList(FILE *fp)
    3136             : 
    3137             : {
    3138         103 :     if (nSharedFileCount > 0)
    3139             :     {
    3140           0 :         if (fp == nullptr)
    3141           0 :             CPLDebug("CPL", "%d Shared files open.", nSharedFileCount);
    3142             :         else
    3143           0 :             fprintf(fp, "%d Shared files open.", nSharedFileCount);
    3144             :     }
    3145             : 
    3146         103 :     for (int i = 0; i < nSharedFileCount; i++)
    3147             :     {
    3148           0 :         if (fp == nullptr)
    3149           0 :             CPLDebug("CPL", "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
    3150           0 :                      pasSharedFileList[i].bLarge,
    3151           0 :                      pasSharedFileList[i].pszAccess,
    3152           0 :                      pasSharedFileList[i].pszFilename);
    3153             :         else
    3154           0 :             fprintf(fp, "%2d %d %4s %s", pasSharedFileList[i].nRefCount,
    3155           0 :                     pasSharedFileList[i].bLarge, pasSharedFileList[i].pszAccess,
    3156           0 :                     pasSharedFileList[i].pszFilename);
    3157             :     }
    3158         103 : }
    3159             : 
    3160             : /************************************************************************/
    3161             : /*                           CPLUnlinkTree()                            */
    3162             : /************************************************************************/
    3163             : 
    3164             : /** Recursively unlink a directory.
    3165             :  *
    3166             :  * @return 0 on successful completion, -1 if function fails.
    3167             :  */
    3168             : 
    3169          55 : int CPLUnlinkTree(const char *pszPath)
    3170             : 
    3171             : {
    3172             :     /* -------------------------------------------------------------------- */
    3173             :     /*      First, ensure there is such a file.                             */
    3174             :     /* -------------------------------------------------------------------- */
    3175             :     VSIStatBufL sStatBuf;
    3176             : 
    3177          55 :     if (VSIStatL(pszPath, &sStatBuf) != 0)
    3178             :     {
    3179           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    3180             :                  "It seems no file system object called '%s' exists.", pszPath);
    3181             : 
    3182           2 :         return -1;
    3183             :     }
    3184             : 
    3185             :     /* -------------------------------------------------------------------- */
    3186             :     /*      If it is a simple file, just delete it.                         */
    3187             :     /* -------------------------------------------------------------------- */
    3188          53 :     if (VSI_ISREG(sStatBuf.st_mode))
    3189             :     {
    3190          36 :         if (VSIUnlink(pszPath) != 0)
    3191             :         {
    3192           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
    3193             :                      pszPath);
    3194             : 
    3195           0 :             return -1;
    3196             :         }
    3197             : 
    3198          36 :         return 0;
    3199             :     }
    3200             : 
    3201             :     /* -------------------------------------------------------------------- */
    3202             :     /*      If it is a directory recurse then unlink the directory.         */
    3203             :     /* -------------------------------------------------------------------- */
    3204          17 :     else if (VSI_ISDIR(sStatBuf.st_mode))
    3205             :     {
    3206          17 :         char **papszItems = VSIReadDir(pszPath);
    3207             : 
    3208          34 :         for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
    3209             :         {
    3210          17 :             if (papszItems[i][0] == '\0' || EQUAL(papszItems[i], ".") ||
    3211          17 :                 EQUAL(papszItems[i], ".."))
    3212           0 :                 continue;
    3213             : 
    3214             :             const std::string osSubPath =
    3215          17 :                 CPLFormFilenameSafe(pszPath, papszItems[i], nullptr);
    3216             : 
    3217          17 :             const int nErr = CPLUnlinkTree(osSubPath.c_str());
    3218             : 
    3219          17 :             if (nErr != 0)
    3220             :             {
    3221           0 :                 CSLDestroy(papszItems);
    3222           0 :                 return nErr;
    3223             :             }
    3224             :         }
    3225             : 
    3226          17 :         CSLDestroy(papszItems);
    3227             : 
    3228          17 :         if (VSIRmdir(pszPath) != 0)
    3229             :         {
    3230           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to unlink %s.",
    3231             :                      pszPath);
    3232             : 
    3233           0 :             return -1;
    3234             :         }
    3235             : 
    3236          17 :         return 0;
    3237             :     }
    3238             : 
    3239             :     /* -------------------------------------------------------------------- */
    3240             :     /*      otherwise report an error.                                      */
    3241             :     /* -------------------------------------------------------------------- */
    3242           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    3243             :              "Failed to unlink %s.\nUnrecognised filesystem object.", pszPath);
    3244           0 :     return 1000;
    3245             : }
    3246             : 
    3247             : /************************************************************************/
    3248             : /*                            CPLCopyFile()                             */
    3249             : /************************************************************************/
    3250             : 
    3251             : /** Copy a file */
    3252        2300 : int CPLCopyFile(const char *pszNewPath, const char *pszOldPath)
    3253             : 
    3254             : {
    3255        2300 :     return VSICopyFile(pszOldPath, pszNewPath, nullptr,
    3256             :                        static_cast<vsi_l_offset>(-1), nullptr, nullptr,
    3257        2300 :                        nullptr);
    3258             : }
    3259             : 
    3260             : /************************************************************************/
    3261             : /*                            CPLCopyTree()                             */
    3262             : /************************************************************************/
    3263             : 
    3264             : /** Recursively copy a tree */
    3265           4 : int CPLCopyTree(const char *pszNewPath, const char *pszOldPath)
    3266             : 
    3267             : {
    3268             :     VSIStatBufL sStatBuf;
    3269           4 :     if (VSIStatL(pszNewPath, &sStatBuf) == 0)
    3270             :     {
    3271           1 :         CPLError(
    3272             :             CE_Failure, CPLE_AppDefined,
    3273             :             "It seems that a file system object called '%s' already exists.",
    3274             :             pszNewPath);
    3275             : 
    3276           1 :         return -1;
    3277             :     }
    3278             : 
    3279           3 :     if (VSIStatL(pszOldPath, &sStatBuf) != 0)
    3280             :     {
    3281           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3282             :                  "It seems no file system object called '%s' exists.",
    3283             :                  pszOldPath);
    3284             : 
    3285           1 :         return -1;
    3286             :     }
    3287             : 
    3288           2 :     if (VSI_ISDIR(sStatBuf.st_mode))
    3289             :     {
    3290           1 :         if (VSIMkdir(pszNewPath, 0755) != 0)
    3291             :         {
    3292           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3293             :                      "Cannot create directory '%s'.", pszNewPath);
    3294             : 
    3295           0 :             return -1;
    3296             :         }
    3297             : 
    3298           1 :         char **papszItems = VSIReadDir(pszOldPath);
    3299             : 
    3300           4 :         for (int i = 0; papszItems != nullptr && papszItems[i] != nullptr; i++)
    3301             :         {
    3302           3 :             if (EQUAL(papszItems[i], ".") || EQUAL(papszItems[i], ".."))
    3303           2 :                 continue;
    3304             : 
    3305             :             const std::string osNewSubPath =
    3306           1 :                 CPLFormFilenameSafe(pszNewPath, papszItems[i], nullptr);
    3307             :             const std::string osOldSubPath =
    3308           1 :                 CPLFormFilenameSafe(pszOldPath, papszItems[i], nullptr);
    3309             : 
    3310             :             const int nErr =
    3311           1 :                 CPLCopyTree(osNewSubPath.c_str(), osOldSubPath.c_str());
    3312             : 
    3313           1 :             if (nErr != 0)
    3314             :             {
    3315           0 :                 CSLDestroy(papszItems);
    3316           0 :                 return nErr;
    3317             :             }
    3318             :         }
    3319           1 :         CSLDestroy(papszItems);
    3320             : 
    3321           1 :         return 0;
    3322             :     }
    3323           1 :     else if (VSI_ISREG(sStatBuf.st_mode))
    3324             :     {
    3325           1 :         return CPLCopyFile(pszNewPath, pszOldPath);
    3326             :     }
    3327             :     else
    3328             :     {
    3329           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3330             :                  "Unrecognized filesystem object : '%s'.", pszOldPath);
    3331           0 :         return -1;
    3332             :     }
    3333             : }
    3334             : 
    3335             : /************************************************************************/
    3336             : /*                            CPLMoveFile()                             */
    3337             : /************************************************************************/
    3338             : 
    3339             : /** Move a file */
    3340         191 : int CPLMoveFile(const char *pszNewPath, const char *pszOldPath)
    3341             : 
    3342             : {
    3343         191 :     if (VSIRename(pszOldPath, pszNewPath) == 0)
    3344         188 :         return 0;
    3345             : 
    3346           3 :     const int nRet = CPLCopyFile(pszNewPath, pszOldPath);
    3347             : 
    3348           3 :     if (nRet == 0)
    3349             :     {
    3350           3 :         if (VSIUnlink(pszOldPath) != 0)
    3351             :         {
    3352           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Cannot delete '%s'",
    3353             :                      pszOldPath);
    3354             :         }
    3355             :     }
    3356           3 :     return nRet;
    3357             : }
    3358             : 
    3359             : /************************************************************************/
    3360             : /*                             CPLSymlink()                             */
    3361             : /************************************************************************/
    3362             : 
    3363             : /** Create a symbolic link */
    3364             : #ifdef _WIN32
    3365             : int CPLSymlink(const char *, const char *, CSLConstList)
    3366             : {
    3367             :     return -1;
    3368             : }
    3369             : #else
    3370           0 : int CPLSymlink(const char *pszOldPath, const char *pszNewPath,
    3371             :                CSLConstList /* papszOptions */)
    3372             : {
    3373           0 :     return symlink(pszOldPath, pszNewPath);
    3374             : }
    3375             : #endif
    3376             : 
    3377             : /************************************************************************/
    3378             : /* ==================================================================== */
    3379             : /*                              CPLLocaleC                              */
    3380             : /* ==================================================================== */
    3381             : /************************************************************************/
    3382             : 
    3383             : //! @cond Doxygen_Suppress
    3384             : /************************************************************************/
    3385             : /*                             CPLLocaleC()                             */
    3386             : /************************************************************************/
    3387             : 
    3388         139 : CPLLocaleC::CPLLocaleC() : pszOldLocale(nullptr)
    3389             : {
    3390         139 :     if (CPLTestBool(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC", "NO")))
    3391           0 :         return;
    3392             : 
    3393         139 :     pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr));
    3394         139 :     if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
    3395           0 :         CPLsetlocale(LC_NUMERIC, "C") == nullptr)
    3396             :     {
    3397         139 :         CPLFree(pszOldLocale);
    3398         139 :         pszOldLocale = nullptr;
    3399             :     }
    3400             : }
    3401             : 
    3402             : /************************************************************************/
    3403             : /*                            ~CPLLocaleC()                             */
    3404             : /************************************************************************/
    3405             : 
    3406           0 : CPLLocaleC::~CPLLocaleC()
    3407             : 
    3408             : {
    3409         139 :     if (pszOldLocale == nullptr)
    3410         139 :         return;
    3411             : 
    3412           0 :     CPLsetlocale(LC_NUMERIC, pszOldLocale);
    3413           0 :     CPLFree(pszOldLocale);
    3414         139 : }
    3415             : 
    3416             : /************************************************************************/
    3417             : /*                       CPLThreadLocaleCPrivate                        */
    3418             : /************************************************************************/
    3419             : 
    3420             : #ifdef HAVE_USELOCALE
    3421             : 
    3422             : class CPLThreadLocaleCPrivate
    3423             : {
    3424             :     locale_t nNewLocale;
    3425             :     locale_t nOldLocale;
    3426             : 
    3427             :     CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
    3428             : 
    3429             :   public:
    3430             :     CPLThreadLocaleCPrivate();
    3431             :     ~CPLThreadLocaleCPrivate();
    3432             : };
    3433             : 
    3434           0 : CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
    3435           0 :     : nNewLocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)),
    3436           0 :       nOldLocale(uselocale(nNewLocale))
    3437             : {
    3438           0 : }
    3439             : 
    3440           0 : CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
    3441             : {
    3442           0 :     uselocale(nOldLocale);
    3443           0 :     freelocale(nNewLocale);
    3444           0 : }
    3445             : 
    3446             : #elif defined(_MSC_VER)
    3447             : 
    3448             : class CPLThreadLocaleCPrivate
    3449             : {
    3450             :     int nOldValConfigThreadLocale;
    3451             :     char *pszOldLocale;
    3452             : 
    3453             :     CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
    3454             : 
    3455             :   public:
    3456             :     CPLThreadLocaleCPrivate();
    3457             :     ~CPLThreadLocaleCPrivate();
    3458             : };
    3459             : 
    3460             : CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
    3461             : {
    3462             :     nOldValConfigThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
    3463             :     pszOldLocale = setlocale(LC_NUMERIC, "C");
    3464             :     if (pszOldLocale)
    3465             :         pszOldLocale = CPLStrdup(pszOldLocale);
    3466             : }
    3467             : 
    3468             : CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
    3469             : {
    3470             :     if (pszOldLocale != nullptr)
    3471             :     {
    3472             :         setlocale(LC_NUMERIC, pszOldLocale);
    3473             :         CPLFree(pszOldLocale);
    3474             :     }
    3475             :     _configthreadlocale(nOldValConfigThreadLocale);
    3476             : }
    3477             : 
    3478             : #else
    3479             : 
    3480             : class CPLThreadLocaleCPrivate
    3481             : {
    3482             :     char *pszOldLocale;
    3483             : 
    3484             :     CPL_DISALLOW_COPY_ASSIGN(CPLThreadLocaleCPrivate)
    3485             : 
    3486             :   public:
    3487             :     CPLThreadLocaleCPrivate();
    3488             :     ~CPLThreadLocaleCPrivate();
    3489             : };
    3490             : 
    3491             : CPLThreadLocaleCPrivate::CPLThreadLocaleCPrivate()
    3492             :     : pszOldLocale(CPLStrdup(CPLsetlocale(LC_NUMERIC, nullptr)))
    3493             : {
    3494             :     if (EQUAL(pszOldLocale, "C") || EQUAL(pszOldLocale, "POSIX") ||
    3495             :         CPLsetlocale(LC_NUMERIC, "C") == nullptr)
    3496             :     {
    3497             :         CPLFree(pszOldLocale);
    3498             :         pszOldLocale = nullptr;
    3499             :     }
    3500             : }
    3501             : 
    3502             : CPLThreadLocaleCPrivate::~CPLThreadLocaleCPrivate()
    3503             : {
    3504             :     if (pszOldLocale != nullptr)
    3505             :     {
    3506             :         CPLsetlocale(LC_NUMERIC, pszOldLocale);
    3507             :         CPLFree(pszOldLocale);
    3508             :     }
    3509             : }
    3510             : 
    3511             : #endif
    3512             : 
    3513             : /************************************************************************/
    3514             : /*                          CPLThreadLocaleC()                          */
    3515             : /************************************************************************/
    3516             : 
    3517           0 : CPLThreadLocaleC::CPLThreadLocaleC() : m_private(new CPLThreadLocaleCPrivate)
    3518             : {
    3519           0 : }
    3520             : 
    3521             : /************************************************************************/
    3522             : /*                         ~CPLThreadLocaleC()                          */
    3523             : /************************************************************************/
    3524             : 
    3525           0 : CPLThreadLocaleC::~CPLThreadLocaleC()
    3526             : 
    3527             : {
    3528           0 :     delete m_private;
    3529           0 : }
    3530             : 
    3531             : //! @endcond
    3532             : 
    3533             : /************************************************************************/
    3534             : /*                            CPLsetlocale()                            */
    3535             : /************************************************************************/
    3536             : 
    3537             : /**
    3538             :  * Prevents parallel executions of setlocale().
    3539             :  *
    3540             :  * Calling setlocale() concurrently from two or more threads is a
    3541             :  * potential data race. A mutex is used to provide a critical region so
    3542             :  * that only one thread at a time can be executing setlocale().
    3543             :  *
    3544             :  * The return should not be freed, and copied quickly as it may be invalidated
    3545             :  * by a following next call to CPLsetlocale().
    3546             :  *
    3547             :  * @param category See your compiler's documentation on setlocale.
    3548             :  * @param locale See your compiler's documentation on setlocale.
    3549             :  *
    3550             :  * @return See your compiler's documentation on setlocale.
    3551             :  */
    3552         141 : char *CPLsetlocale(int category, const char *locale)
    3553             : {
    3554         282 :     CPLMutexHolder oHolder(&hSetLocaleMutex);
    3555         141 :     char *pszRet = setlocale(category, locale);
    3556         141 :     if (pszRet == nullptr)
    3557           0 :         return pszRet;
    3558             : 
    3559             :     // Make it thread-locale storage.
    3560         141 :     return const_cast<char *>(CPLSPrintf("%s", pszRet));
    3561             : }
    3562             : 
    3563             : /************************************************************************/
    3564             : /*                      CPLCleanupSetlocaleMutex()                      */
    3565             : /************************************************************************/
    3566             : 
    3567        1303 : void CPLCleanupSetlocaleMutex(void)
    3568             : {
    3569        1303 :     if (hSetLocaleMutex != nullptr)
    3570           5 :         CPLDestroyMutex(hSetLocaleMutex);
    3571        1303 :     hSetLocaleMutex = nullptr;
    3572        1303 : }
    3573             : 
    3574             : /************************************************************************/
    3575             : /*                            IsPowerOfTwo()                            */
    3576             : /************************************************************************/
    3577             : 
    3578         161 : int CPLIsPowerOfTwo(unsigned int i)
    3579             : {
    3580         161 :     if (i == 0)
    3581           0 :         return FALSE;
    3582         161 :     return (i & (i - 1)) == 0 ? TRUE : FALSE;
    3583             : }
    3584             : 
    3585             : /************************************************************************/
    3586             : /*                          CPLCheckForFile()                           */
    3587             : /************************************************************************/
    3588             : 
    3589             : /**
    3590             :  * Check for file existence.
    3591             :  *
    3592             :  * The function checks if a named file exists in the filesystem, hopefully
    3593             :  * in an efficient fashion if a sibling file list is available.   It exists
    3594             :  * primarily to do faster file checking for functions like GDAL open methods
    3595             :  * that get a list of files from the target directory.
    3596             :  *
    3597             :  * If the sibling file list exists (is not NULL) it is assumed to be a list
    3598             :  * of files in the same directory as the target file, and it will be checked
    3599             :  * (case insensitively) for a match.  If a match is found, pszFilename is
    3600             :  * updated with the correct case and TRUE is returned.
    3601             :  *
    3602             :  * If papszSiblingFiles is NULL, a VSIStatL() is used to test for the files
    3603             :  * existence, and no case insensitive testing is done.
    3604             :  *
    3605             :  * @param pszFilename name of file to check for - filename case updated in
    3606             :  * some cases.
    3607             :  * @param papszSiblingFiles a list of files in the same directory as
    3608             :  * pszFilename if available, or NULL. This list should have no path components.
    3609             :  *
    3610             :  * @return TRUE if a match is found, or FALSE if not.
    3611             :  */
    3612             : 
    3613      174064 : int CPLCheckForFile(char *pszFilename, CSLConstList papszSiblingFiles)
    3614             : 
    3615             : {
    3616             :     /* -------------------------------------------------------------------- */
    3617             :     /*      Fallback case if we don't have a sibling file list.             */
    3618             :     /* -------------------------------------------------------------------- */
    3619      174064 :     if (papszSiblingFiles == nullptr)
    3620             :     {
    3621             :         VSIStatBufL sStatBuf;
    3622             : 
    3623       12189 :         return VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
    3624             :     }
    3625             : 
    3626             :     /* -------------------------------------------------------------------- */
    3627             :     /*      We have sibling files, compare the non-path filename portion    */
    3628             :     /*      of pszFilename too all entries.                                 */
    3629             :     /* -------------------------------------------------------------------- */
    3630      323750 :     const CPLString osFileOnly = CPLGetFilename(pszFilename);
    3631             : 
    3632    17703600 :     for (int i = 0; papszSiblingFiles[i] != nullptr; i++)
    3633             :     {
    3634    17541800 :         if (EQUAL(papszSiblingFiles[i], osFileOnly))
    3635             :         {
    3636         308 :             strcpy(pszFilename + strlen(pszFilename) - osFileOnly.size(),
    3637         154 :                    papszSiblingFiles[i]);
    3638         154 :             return TRUE;
    3639             :         }
    3640             :     }
    3641             : 
    3642      161721 :     return FALSE;
    3643             : }
    3644             : 
    3645             : /************************************************************************/
    3646             : /*      Stub implementation of zip services if we don't have libz.      */
    3647             : /************************************************************************/
    3648             : 
    3649             : #if !defined(HAVE_LIBZ)
    3650             : 
    3651             : void *CPLCreateZip(const char *, char **)
    3652             : 
    3653             : {
    3654             :     CPLError(CE_Failure, CPLE_NotSupported,
    3655             :              "This GDAL/OGR build does not include zlib and zip services.");
    3656             :     return nullptr;
    3657             : }
    3658             : 
    3659             : CPLErr CPLCreateFileInZip(void *, const char *, char **)
    3660             : {
    3661             :     return CE_Failure;
    3662             : }
    3663             : 
    3664             : CPLErr CPLWriteFileInZip(void *, const void *, int)
    3665             : {
    3666             :     return CE_Failure;
    3667             : }
    3668             : 
    3669             : CPLErr CPLCloseFileInZip(void *)
    3670             : {
    3671             :     return CE_Failure;
    3672             : }
    3673             : 
    3674             : CPLErr CPLCloseZip(void *)
    3675             : {
    3676             :     return CE_Failure;
    3677             : }
    3678             : 
    3679             : void *CPLZLibDeflate(const void *, size_t, int, void *, size_t,
    3680             :                      size_t *pnOutBytes)
    3681             : {
    3682             :     if (pnOutBytes != nullptr)
    3683             :         *pnOutBytes = 0;
    3684             :     return nullptr;
    3685             : }
    3686             : 
    3687             : void *CPLZLibInflate(const void *, size_t, void *, size_t, size_t *pnOutBytes)
    3688             : {
    3689             :     if (pnOutBytes != nullptr)
    3690             :         *pnOutBytes = 0;
    3691             :     return nullptr;
    3692             : }
    3693             : 
    3694             : #endif /* !defined(HAVE_LIBZ) */
    3695             : 
    3696             : /************************************************************************/
    3697             : /* ==================================================================== */
    3698             : /*                          CPLConfigOptionSetter                       */
    3699             : /* ==================================================================== */
    3700             : /************************************************************************/
    3701             : 
    3702             : //! @cond Doxygen_Suppress
    3703             : /************************************************************************/
    3704             : /*                       CPLConfigOptionSetter()                        */
    3705             : /************************************************************************/
    3706             : 
    3707       27247 : CPLConfigOptionSetter::CPLConfigOptionSetter(const char *pszKey,
    3708             :                                              const char *pszValue,
    3709       27247 :                                              bool bSetOnlyIfUndefined)
    3710       27247 :     : m_pszKey(CPLStrdup(pszKey)), m_pszOldValue(nullptr),
    3711       27246 :       m_bRestoreOldValue(false)
    3712             : {
    3713       27246 :     const char *pszOldValue = CPLGetThreadLocalConfigOption(pszKey, nullptr);
    3714       43698 :     if ((bSetOnlyIfUndefined &&
    3715       38044 :          CPLGetConfigOption(pszKey, nullptr) == nullptr) ||
    3716       10807 :         !bSetOnlyIfUndefined)
    3717             :     {
    3718       27244 :         m_bRestoreOldValue = true;
    3719       27244 :         if (pszOldValue)
    3720         671 :             m_pszOldValue = CPLStrdup(pszOldValue);
    3721       27244 :         CPLSetThreadLocalConfigOption(pszKey,
    3722             :                                       pszValue ? pszValue : CPL_NULL_VALUE);
    3723             :     }
    3724       27215 : }
    3725             : 
    3726             : /************************************************************************/
    3727             : /*                       ~CPLConfigOptionSetter()                       */
    3728             : /************************************************************************/
    3729             : 
    3730       54459 : CPLConfigOptionSetter::~CPLConfigOptionSetter()
    3731             : {
    3732       27233 :     if (m_bRestoreOldValue)
    3733             :     {
    3734       27214 :         CPLSetThreadLocalConfigOption(m_pszKey, m_pszOldValue);
    3735       27222 :         CPLFree(m_pszOldValue);
    3736             :     }
    3737       27232 :     CPLFree(m_pszKey);
    3738       27226 : }
    3739             : 
    3740             : //! @endcond
    3741             : 
    3742             : /************************************************************************/
    3743             : /*                          CPLIsInteractive()                          */
    3744             : /************************************************************************/
    3745             : 
    3746             : /** Returns whether the provided file refers to a terminal.
    3747             :  *
    3748             :  * This function is a wrapper of the ``isatty()`` POSIX function.
    3749             :  *
    3750             :  * @param f File to test. Typically stdin, stdout or stderr
    3751             :  * @return true if it is an open file referring to a terminal.
    3752             :  * @since GDAL 3.11
    3753             :  */
    3754         669 : bool CPLIsInteractive(FILE *f)
    3755             : {
    3756             : #ifndef _WIN32
    3757         669 :     return CPL_TO_BOOL(isatty(static_cast<int>(fileno(f))));
    3758             : #else
    3759             :     return CPL_TO_BOOL(_isatty(_fileno(f)));
    3760             : #endif
    3761             : }
    3762             : 
    3763             : /************************************************************************/
    3764             : /*                          CPLLockFileStruct                           */
    3765             : /************************************************************************/
    3766             : 
    3767             : //! @cond Doxygen_Suppress
    3768             : struct CPLLockFileStruct
    3769             : {
    3770             :     std::string osLockFilename{};
    3771             :     std::atomic<bool> bStop = false;
    3772             :     CPLJoinableThread *hThread = nullptr;
    3773             : };
    3774             : 
    3775             : //! @endcond
    3776             : 
    3777             : /************************************************************************/
    3778             : /*                           CPLLockFileEx()                            */
    3779             : /************************************************************************/
    3780             : 
    3781             : /** Create and acquire a lock file.
    3782             :  *
    3783             :  * Only one caller can acquire the lock file at a time. The O_CREAT|O_EXCL
    3784             :  * flags of open() are used for that purpose (there might be limitations for
    3785             :  * network file systems).
    3786             :  *
    3787             :  * The lock file is continuously touched by a thread started by this function,
    3788             :  * to indicate it is still alive. If an existing lock file is found that has
    3789             :  * not been recently refreshed it will be considered stalled, and will be
    3790             :  * deleted before attempting to recreate it.
    3791             :  *
    3792             :  * This function must be paired with CPLUnlockFileEx().
    3793             :  *
    3794             :  * Available options are:
    3795             :  * <ul>
    3796             :  * <li>WAIT_TIME=value_in_sec/inf: Maximum amount of time in second that this
    3797             :  *     function can spend waiting for the lock. If not set, default to infinity.
    3798             :  * </li>
    3799             :  * <li>STALLED_DELAY=value_in_sec: Delay in second to consider that an existing
    3800             :  * lock file that has not been touched since STALLED_DELAY is stalled, and can
    3801             :  * be re-acquired. Defaults to 10 seconds.
    3802             :  * </li>
    3803             :  * <li>VERBOSE_WAIT_MESSAGE=YES/NO: Whether to emit a CE_Warning message while
    3804             :  * waiting for a busy lock. Default to NO.
    3805             :  * </li>
    3806             :  * </ul>
    3807             : 
    3808             :  * @param pszLockFileName Lock file name. The directory must already exist.
    3809             :  *                        Must not be NULL.
    3810             :  * @param[out] phLockFileHandle Pointer to at location where to store the lock
    3811             :  *                              handle that must be passed to CPLUnlockFileEx().
    3812             :  *                              *phLockFileHandle will be null if the return
    3813             :  *                              code of that function is not CLFS_OK.
    3814             :  * @param papszOptions NULL terminated list of strings, or NULL.
    3815             :  *
    3816             :  * @return lock file status.
    3817             :  *
    3818             :  * @since 3.11
    3819             :  */
    3820          15 : CPLLockFileStatus CPLLockFileEx(const char *pszLockFileName,
    3821             :                                 CPLLockFileHandle *phLockFileHandle,
    3822             :                                 CSLConstList papszOptions)
    3823             : {
    3824          15 :     if (!pszLockFileName || !phLockFileHandle)
    3825           2 :         return CLFS_API_MISUSE;
    3826             : 
    3827          13 :     *phLockFileHandle = nullptr;
    3828             : 
    3829             :     const double dfWaitTime =
    3830          13 :         CPLAtof(CSLFetchNameValueDef(papszOptions, "WAIT_TIME", "inf"));
    3831             :     const double dfStalledDelay =
    3832          13 :         CPLAtof(CSLFetchNameValueDef(papszOptions, "STALLED_DELAY", "10"));
    3833             :     const bool bVerboseWait =
    3834          13 :         CPLFetchBool(papszOptions, "VERBOSE_WAIT_MESSAGE", false);
    3835             : 
    3836          14 :     for (int i = 0; i < 2; ++i)
    3837             :     {
    3838             : #ifdef _WIN32
    3839             :         wchar_t *pwszFilename =
    3840             :             CPLRecodeToWChar(pszLockFileName, CPL_ENC_UTF8, CPL_ENC_UCS2);
    3841             :         int fd = _wopen(pwszFilename, _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
    3842             :         CPLFree(pwszFilename);
    3843             : #else
    3844          14 :         int fd = open(pszLockFileName, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
    3845             : #endif
    3846          14 :         if (fd == -1)
    3847             :         {
    3848           3 :             if (errno != EEXIST || i == 1)
    3849             :             {
    3850           0 :                 return CLFS_CANNOT_CREATE_LOCK;
    3851             :             }
    3852             :             else
    3853             :             {
    3854             :                 // Wait for the .lock file to have been removed or
    3855             :                 // not refreshed since dfStalledDelay seconds.
    3856           3 :                 double dfCurWaitTime = dfWaitTime;
    3857             :                 VSIStatBufL sStat;
    3858          17 :                 while (VSIStatL(pszLockFileName, &sStat) == 0 &&
    3859           8 :                        static_cast<double>(sStat.st_mtime) + dfStalledDelay >
    3860           8 :                            static_cast<double>(time(nullptr)))
    3861             :                 {
    3862           7 :                     if (dfCurWaitTime <= 1e-5)
    3863           2 :                         return CLFS_LOCK_BUSY;
    3864             : 
    3865           6 :                     if (bVerboseWait)
    3866             :                     {
    3867           5 :                         CPLError(CE_Warning, CPLE_AppDefined,
    3868             :                                  "Waiting for %s to be freed...",
    3869             :                                  pszLockFileName);
    3870             :                     }
    3871             :                     else
    3872             :                     {
    3873           1 :                         CPLDebug("CPL", "Waiting for %s to be freed...",
    3874             :                                  pszLockFileName);
    3875             :                     }
    3876             : 
    3877           6 :                     const double dfPauseDelay = std::min(0.5, dfWaitTime);
    3878           6 :                     CPLSleep(dfPauseDelay);
    3879           6 :                     dfCurWaitTime -= dfPauseDelay;
    3880             :                 }
    3881             : 
    3882           2 :                 if (VSIUnlink(pszLockFileName) != 0)
    3883             :                 {
    3884           1 :                     return CLFS_CANNOT_CREATE_LOCK;
    3885             :                 }
    3886             :             }
    3887             :         }
    3888             :         else
    3889             :         {
    3890          11 :             close(fd);
    3891          11 :             break;
    3892             :         }
    3893             :     }
    3894             : 
    3895             :     // Touch regularly the lock file to show it is still alive
    3896             :     struct KeepAliveLockFile
    3897             :     {
    3898          11 :         static void func(void *user_data)
    3899             :         {
    3900          11 :             CPLLockFileHandle hLockFileHandle =
    3901             :                 static_cast<CPLLockFileHandle>(user_data);
    3902          23 :             while (!hLockFileHandle->bStop)
    3903             :             {
    3904             :                 auto f = VSIVirtualHandleUniquePtr(
    3905          24 :                     VSIFOpenL(hLockFileHandle->osLockFilename.c_str(), "wb"));
    3906          12 :                 if (f)
    3907             :                 {
    3908          12 :                     f.reset();
    3909             :                 }
    3910          12 :                 constexpr double REFRESH_DELAY = 0.5;
    3911          12 :                 CPLSleep(REFRESH_DELAY);
    3912             :             }
    3913          11 :         }
    3914             :     };
    3915             : 
    3916          11 :     *phLockFileHandle = new CPLLockFileStruct();
    3917          11 :     (*phLockFileHandle)->osLockFilename = pszLockFileName;
    3918             : 
    3919          22 :     (*phLockFileHandle)->hThread =
    3920          11 :         CPLCreateJoinableThread(KeepAliveLockFile::func, *phLockFileHandle);
    3921          11 :     if ((*phLockFileHandle)->hThread == nullptr)
    3922             :     {
    3923           0 :         VSIUnlink(pszLockFileName);
    3924           0 :         delete *phLockFileHandle;
    3925           0 :         *phLockFileHandle = nullptr;
    3926           0 :         return CLFS_THREAD_CREATION_FAILED;
    3927             :     }
    3928             : 
    3929          11 :     return CLFS_OK;
    3930             : }
    3931             : 
    3932             : /************************************************************************/
    3933             : /*                          CPLUnlockFileEx()                           */
    3934             : /************************************************************************/
    3935             : 
    3936             : /** Release and delete a lock file.
    3937             :  *
    3938             :  * This function must be paired with CPLLockFileEx().
    3939             :  *
    3940             :  * @param hLockFileHandle Lock handle (value of *phLockFileHandle argument
    3941             :  *                        set by CPLLockFileEx()), or NULL.
    3942             :  *
    3943             :  * @since 3.11
    3944             :  */
    3945          12 : void CPLUnlockFileEx(CPLLockFileHandle hLockFileHandle)
    3946             : {
    3947          12 :     if (hLockFileHandle)
    3948             :     {
    3949             :         // Remove .lock file
    3950          11 :         hLockFileHandle->bStop = true;
    3951          11 :         CPLJoinThread(hLockFileHandle->hThread);
    3952          11 :         VSIUnlink(hLockFileHandle->osLockFilename.c_str());
    3953             : 
    3954          11 :         delete hLockFileHandle;
    3955             :     }
    3956          12 : }
    3957             : 
    3958             : /************************************************************************/
    3959             : /*                     CPLFormatReadableFileSize()                      */
    3960             : /************************************************************************/
    3961             : 
    3962             : template <class T>
    3963          10 : static std::string CPLFormatReadableFileSizeInternal(T nSizeInBytes)
    3964             : {
    3965          10 :     constexpr T ONE_MEGA_BYTE = 1000 * 1000;
    3966          10 :     constexpr T ONE_GIGA_BYTE = 1000 * ONE_MEGA_BYTE;
    3967          10 :     constexpr T ONE_TERA_BYTE = 1000 * ONE_GIGA_BYTE;
    3968          10 :     constexpr T ONE_PETA_BYTE = 1000 * ONE_TERA_BYTE;
    3969          10 :     constexpr T ONE_HEXA_BYTE = 1000 * ONE_PETA_BYTE;
    3970             : 
    3971          10 :     if (nSizeInBytes > ONE_HEXA_BYTE)
    3972             :         return CPLSPrintf("%.02f HB", static_cast<double>(nSizeInBytes) /
    3973           2 :                                           static_cast<double>(ONE_HEXA_BYTE));
    3974             : 
    3975           8 :     if (nSizeInBytes > ONE_PETA_BYTE)
    3976             :         return CPLSPrintf("%.02f PB", static_cast<double>(nSizeInBytes) /
    3977           2 :                                           static_cast<double>(ONE_PETA_BYTE));
    3978             : 
    3979           6 :     if (nSizeInBytes > ONE_TERA_BYTE)
    3980             :         return CPLSPrintf("%.02f TB", static_cast<double>(nSizeInBytes) /
    3981           1 :                                           static_cast<double>(ONE_TERA_BYTE));
    3982             : 
    3983           5 :     if (nSizeInBytes > ONE_GIGA_BYTE)
    3984             :         return CPLSPrintf("%.02f GB", static_cast<double>(nSizeInBytes) /
    3985           3 :                                           static_cast<double>(ONE_GIGA_BYTE));
    3986             : 
    3987           2 :     if (nSizeInBytes > ONE_MEGA_BYTE)
    3988             :         return CPLSPrintf("%.02f MB", static_cast<double>(nSizeInBytes) /
    3989           1 :                                           static_cast<double>(ONE_MEGA_BYTE));
    3990             : 
    3991             :     return CPLSPrintf("%03d,%03d bytes", static_cast<int>(nSizeInBytes) / 1000,
    3992           1 :                       static_cast<int>(nSizeInBytes) % 1000);
    3993             : }
    3994             : 
    3995             : /** Return a file size in a human readable way.
    3996             :  *
    3997             :  * e.g 1200000 -> "1.20 MB"
    3998             :  *
    3999             :  * @since 3.12
    4000             :  */
    4001           3 : std::string CPLFormatReadableFileSize(uint64_t nSizeInBytes)
    4002             : {
    4003           3 :     return CPLFormatReadableFileSizeInternal(nSizeInBytes);
    4004             : }
    4005             : 
    4006             : /** Return a file size in a human readable way.
    4007             :  *
    4008             :  * e.g 1200000 -> "1.20 MB"
    4009             :  *
    4010             :  * @since 3.12
    4011             :  */
    4012           7 : std::string CPLFormatReadableFileSize(double dfSizeInBytes)
    4013             : {
    4014           7 :     return CPLFormatReadableFileSizeInternal(dfSizeInBytes);
    4015             : }
    4016             : 
    4017             : /************************************************************************/
    4018             : /*                 CPLGetRemainingFileDescriptorCount()                 */
    4019             : /************************************************************************/
    4020             : 
    4021             : /** \fn CPLGetRemainingFileDescriptorCount()
    4022             :  *
    4023             :  * Return the number of file descriptors that can still be opened by the
    4024             :  * current process.
    4025             :  *
    4026             :  * Only implemented on non-Windows operating systems
    4027             :  *
    4028             :  * Return a negative value in case of error or not implemented.
    4029             :  *
    4030             :  * @since 3.12
    4031             :  */
    4032             : 
    4033             : #if defined(__FreeBSD__)
    4034             : 
    4035             : int CPLGetRemainingFileDescriptorCount()
    4036             : {
    4037             :     struct rlimit limitNumberOfFilesPerProcess;
    4038             :     if (getrlimit(RLIMIT_NOFILE, &limitNumberOfFilesPerProcess) != 0)
    4039             :     {
    4040             :         return -1;
    4041             :     }
    4042             :     const int maxNumberOfFilesPerProcess =
    4043             :         static_cast<int>(limitNumberOfFilesPerProcess.rlim_cur);
    4044             : 
    4045             :     const pid_t pid = getpid();
    4046             :     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC,
    4047             :                   static_cast<int>(pid)};
    4048             : 
    4049             :     size_t len = 0;
    4050             : 
    4051             :     if (sysctl(mib, 4, nullptr, &len, nullptr, 0) == -1)
    4052             :     {
    4053             :         return -1;
    4054             :     }
    4055             : 
    4056             :     return maxNumberOfFilesPerProcess -
    4057             :            static_cast<int>(len / sizeof(struct kinfo_file));
    4058             : }
    4059             : 
    4060             : #else
    4061             : 
    4062         122 : int CPLGetRemainingFileDescriptorCount()
    4063             : {
    4064             : #if !defined(_WIN32) && HAVE_GETRLIMIT
    4065             :     struct rlimit limitNumberOfFilesPerProcess;
    4066         122 :     if (getrlimit(RLIMIT_NOFILE, &limitNumberOfFilesPerProcess) != 0)
    4067             :     {
    4068           0 :         return -1;
    4069             :     }
    4070         122 :     const int maxNumberOfFilesPerProcess =
    4071         122 :         static_cast<int>(limitNumberOfFilesPerProcess.rlim_cur);
    4072             : 
    4073         122 :     int countFilesInUse = 0;
    4074             :     {
    4075         122 :         const char *const apszOptions[] = {"NAME_AND_TYPE_ONLY=YES", nullptr};
    4076             : #ifdef __linux
    4077         122 :         VSIDIR *dir = VSIOpenDir("/proc/self/fd", 0, apszOptions);
    4078             : #else
    4079             :         // MacOSX
    4080             :         VSIDIR *dir = VSIOpenDir("/dev/fd", 0, apszOptions);
    4081             : #endif
    4082         122 :         if (dir)
    4083             :         {
    4084        1686 :             while (VSIGetNextDirEntry(dir))
    4085        1564 :                 ++countFilesInUse;
    4086         122 :             countFilesInUse -= 2;  // do not count . and ..
    4087         122 :             VSICloseDir(dir);
    4088             :         }
    4089             :     }
    4090             : 
    4091         122 :     if (countFilesInUse <= 0)
    4092             :     {
    4093             :         // Fallback if above method does not work
    4094           0 :         for (int fd = 0; fd < maxNumberOfFilesPerProcess; fd++)
    4095             :         {
    4096           0 :             errno = 0;
    4097           0 :             if (fcntl(fd, F_GETFD) != -1 || errno != EBADF)
    4098             :             {
    4099           0 :                 countFilesInUse++;
    4100             :             }
    4101             :         }
    4102             :     }
    4103             : 
    4104         122 :     return maxNumberOfFilesPerProcess - countFilesInUse;
    4105             : #else
    4106             :     return -1;
    4107             : #endif
    4108             : }
    4109             : 
    4110             : namespace cpl
    4111             : {
    4112             : 
    4113             : /** Attempt to parse a number of the designated type from a string. The string
    4114             :  *  must contain no characters other than a single number and surrounding
    4115             :  *  whitespace, and the parsed number must fit into the designated type.
    4116             :  *  If these conditions are not met, std::nullopt will be returned.
    4117             :  *
    4118             :  * @param str the string to parse
    4119             :  * @return a std::optional<T> containing the parsed value, or std::nullopt
    4120             :  *         in case of failure.
    4121             :  */
    4122             : template <typename T>
    4123        5323 : std::optional<T> CPL_DLL strict_parse(std::string_view str)
    4124             : {
    4125        5323 :     str = trim(str);
    4126             : 
    4127             :     T result;
    4128        5323 :     const auto begin = str.data();
    4129        5323 :     const auto end = str.data() + str.size();
    4130             : 
    4131        5323 :     auto [ptr, ec] = std::from_chars(begin, end, result);
    4132             : 
    4133        5323 :     if (ec != std::errc())
    4134           9 :         return std::nullopt;
    4135             : 
    4136        5314 :     if (ptr != end)
    4137             :     {
    4138             :         // For integer types, allow decimal and trailing zeros
    4139             :         if constexpr (std::is_integral_v<T>)
    4140             :         {
    4141          11 :             constexpr char DIGIT_ZERO = '0';
    4142             : 
    4143          11 :             if (*ptr++ == '.')
    4144             :             {
    4145          12 :                 while (ptr != end)
    4146             :                 {
    4147          10 :                     if (*ptr++ != DIGIT_ZERO)
    4148             :                     {
    4149           8 :                         return std::nullopt;
    4150             :                     }
    4151             :                 }
    4152             :             }
    4153             :         }
    4154             :         else
    4155             :         {
    4156             :             return std::nullopt;
    4157             :         }
    4158             :     }
    4159             : 
    4160        5306 :     return result;
    4161             : }
    4162             : 
    4163             : template std::optional<std::int8_t>
    4164             :     CPL_DLL strict_parse<std::int8_t>(std::string_view str);
    4165             : template std::optional<std::uint8_t>
    4166             :     CPL_DLL strict_parse<std::uint8_t>(std::string_view str);
    4167             : template std::optional<std::int16_t>
    4168             :     CPL_DLL strict_parse<std::int16_t>(std::string_view str);
    4169             : template std::optional<std::uint16_t>
    4170             :     CPL_DLL strict_parse<std::uint16_t>(std::string_view str);
    4171             : template std::optional<std::int32_t>
    4172             :     CPL_DLL strict_parse<std::int32_t>(std::string_view str);
    4173             : template std::optional<std::uint32_t>
    4174             :     CPL_DLL strict_parse<std::uint32_t>(std::string_view str);
    4175             : template std::optional<std::int64_t>
    4176             :     CPL_DLL strict_parse<std::int64_t>(std::string_view str);
    4177             : template std::optional<std::uint64_t>
    4178             :     CPL_DLL strict_parse<std::uint64_t>(std::string_view str);
    4179             : 
    4180             : template <>
    4181         136 : std::optional<double> CPL_DLL strict_parse<double>(std::string_view str)
    4182             : {
    4183         136 :     str = trim(str);
    4184             : 
    4185         136 :     if (str.empty())
    4186             :     {
    4187           2 :         return std::nullopt;
    4188             :     }
    4189             : 
    4190         134 :     char *end = nullptr;
    4191         134 :     double d = CPLStrtod(str.data(), &end);
    4192             : 
    4193         134 :     auto i = static_cast<decltype(str.size())>(end - str.data());
    4194         134 :     while (i < str.size() && std::isspace(str[i]))
    4195             :     {
    4196           0 :         i++;
    4197             :     }
    4198         134 :     if (i < str.size())
    4199             :     {
    4200          14 :         return std::nullopt;
    4201             :     }
    4202             : 
    4203         120 :     return d;
    4204             : }
    4205             : 
    4206             : template <>
    4207           4 : std::optional<float> CPL_DLL strict_parse<float>(std::string_view str)
    4208             : {
    4209           4 :     auto d = strict_parse<double>(str);
    4210           4 :     if (!d)
    4211             :     {
    4212           0 :         return std::nullopt;
    4213             :     }
    4214           7 :     if (d.value() > static_cast<double>(std::numeric_limits<float>::max()) ||
    4215           3 :         d.value() < static_cast<double>(std::numeric_limits<float>::lowest()))
    4216             :     {
    4217           2 :         return std::nullopt;
    4218             :     }
    4219           2 :     if (std::abs(d.value()) <
    4220           2 :         static_cast<double>(std::numeric_limits<float>::min()))
    4221             :     {
    4222           2 :         return std::nullopt;
    4223             :     }
    4224           0 :     return static_cast<float>(d.value());
    4225             : }
    4226             : 
    4227        1825 : template <> std::optional<bool> CPL_DLL strict_parse<bool>(std::string_view str)
    4228             : {
    4229        1825 :     str = trim(str);
    4230             : 
    4231        1825 :     if (str == "YES" || str == "ON" || str == "TRUE" || str == "1")
    4232           5 :         return true;
    4233             : 
    4234        1820 :     if (str == "NO" || str == "OFF" || str == "FALSE" || str == "0")
    4235        1814 :         return false;
    4236             : 
    4237           6 :     if (str == "yes" || str == "on" || str == "true")
    4238           1 :         return true;
    4239             : 
    4240           5 :     if (str == "no" || str == "off" || str == "false")
    4241           1 :         return false;
    4242             : 
    4243           4 :     return std::nullopt;
    4244             : }
    4245             : }  // namespace cpl
    4246             : 
    4247             : #endif

Generated by: LCOV version 1.14