LCOV - code coverage report
Current view: top level - port - cpl_conv.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 840 1071 78.4 %
Date: 2025-08-01 10:10:57 Functions: 76 87 87.4 %

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

Generated by: LCOV version 1.14