LCOV - code coverage report
Current view: top level - port - cpl_conv.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 805 1031 78.1 %
Date: 2025-05-24 03:54:53 Functions: 71 82 86.6 %

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

Generated by: LCOV version 1.14