LCOV - code coverage report
Current view: top level - port - cpl_path.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 360 404 89.1 %
Date: 2025-02-20 10:14:44 Functions: 28 34 82.4 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Portable filename/path parsing, and forming ala "Glob API".
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #define ALLOW_DEPRECATED_CPL_PATH_FUNCTIONS
      15             : 
      16             : #include "cpl_port.h"
      17             : #include "cpl_conv.h"
      18             : 
      19             : #include <cctype>
      20             : #include <climits>
      21             : #include <cstddef>
      22             : #include <cstdio>
      23             : #include <cstring>
      24             : #if HAVE_UNISTD_H
      25             : #include <unistd.h>
      26             : #endif
      27             : 
      28             : #include <algorithm>
      29             : #include <string>
      30             : 
      31             : #include "cpl_atomic_ops.h"
      32             : #include "cpl_config.h"
      33             : #include "cpl_error.h"
      34             : #include "cpl_multiproc.h"
      35             : #include "cpl_string.h"
      36             : #include "cpl_vsi.h"
      37             : 
      38             : // Should be size of larged possible filename.
      39             : constexpr int CPL_PATH_BUF_SIZE = 2048;
      40             : constexpr int CPL_PATH_BUF_COUNT = 10;
      41             : 
      42           0 : static const char *CPLStaticBufferTooSmall(char *pszStaticResult)
      43             : {
      44           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Destination buffer too small");
      45           0 :     if (pszStaticResult == nullptr)
      46           0 :         return "";
      47           0 :     strcpy(pszStaticResult, "");
      48           0 :     return pszStaticResult;
      49             : }
      50             : 
      51             : /************************************************************************/
      52             : /*                         CPLGetStaticResult()                         */
      53             : /************************************************************************/
      54             : 
      55         499 : static char *CPLGetStaticResult()
      56             : 
      57             : {
      58         499 :     int bMemoryError = FALSE;
      59             :     char *pachBufRingInfo =
      60         499 :         static_cast<char *>(CPLGetTLSEx(CTLS_PATHBUF, &bMemoryError));
      61         499 :     if (bMemoryError)
      62           0 :         return nullptr;
      63         499 :     if (pachBufRingInfo == nullptr)
      64             :     {
      65          14 :         pachBufRingInfo = static_cast<char *>(VSI_CALLOC_VERBOSE(
      66             :             1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT));
      67          14 :         if (pachBufRingInfo == nullptr)
      68           0 :             return nullptr;
      69          14 :         CPLSetTLS(CTLS_PATHBUF, pachBufRingInfo, TRUE);
      70             :     }
      71             : 
      72             :     /* -------------------------------------------------------------------- */
      73             :     /*      Work out which string in the "ring" we want to use this         */
      74             :     /*      time.                                                           */
      75             :     /* -------------------------------------------------------------------- */
      76         499 :     int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
      77         499 :     const size_t nOffset =
      78         499 :         sizeof(int) + static_cast<size_t>(*pnBufIndex * CPL_PATH_BUF_SIZE);
      79         499 :     char *pachBuffer = pachBufRingInfo + nOffset;
      80             : 
      81         499 :     *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT;
      82             : 
      83         499 :     return pachBuffer;
      84             : }
      85             : 
      86             : /************************************************************************/
      87             : /*                        CPLPathReturnTLSString()                      */
      88             : /************************************************************************/
      89             : 
      90         499 : static const char *CPLPathReturnTLSString(const std::string &osRes,
      91             :                                           const char *pszFuncName)
      92             : {
      93         499 :     if (osRes.size() >= CPL_PATH_BUF_SIZE)
      94             :     {
      95           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too long result for %s()",
      96             :                  pszFuncName);
      97           0 :         return "";
      98             :     }
      99             : 
     100         499 :     char *pszStaticResult = CPLGetStaticResult();
     101         499 :     if (pszStaticResult == nullptr)
     102           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     103         499 :     memcpy(pszStaticResult, osRes.c_str(), osRes.size() + 1);
     104         499 :     return pszStaticResult;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                        CPLFindFilenameStart()                        */
     109             : /************************************************************************/
     110             : 
     111     2182290 : static int CPLFindFilenameStart(const char *pszFilename, size_t nStart = 0)
     112             : 
     113             : {
     114     2182290 :     size_t iFileStart = nStart ? nStart : strlen(pszFilename);
     115             : 
     116    32572000 :     for (; iFileStart > 0 && pszFilename[iFileStart - 1] != '/' &&
     117    30389800 :            pszFilename[iFileStart - 1] != '\\';
     118             :          iFileStart--)
     119             :     {
     120             :     }
     121             : 
     122     2182290 :     return static_cast<int>(iFileStart);
     123             : }
     124             : 
     125             : /************************************************************************/
     126             : /*                          CPLGetPathSafe()                            */
     127             : /************************************************************************/
     128             : 
     129             : /**
     130             :  * Extract directory path portion of filename.
     131             :  *
     132             :  * Returns a string containing the directory path portion of the passed
     133             :  * filename.  If there is no path in the passed filename an empty string
     134             :  * will be returned (not NULL).
     135             :  *
     136             :  * \code{.cpp}
     137             :  * CPLGetPathSafe( "abc/def.xyz" ) == "abc"
     138             :  * CPLGetPathSafe( "/abc/def/" ) == "/abc/def"
     139             :  * CPLGetPathSafe( "/" ) == "/"
     140             :  * CPLGetPathSafe( "/abc/def" ) == "/abc"
     141             :  * CPLGetPathSafe( "abc" ) == ""
     142             :  * \endcode
     143             :  *
     144             :  * @param pszFilename the filename potentially including a path.
     145             :  *
     146             :  * @return Path.
     147             :  *
     148             :  * @since 3.11
     149             :  */
     150             : 
     151      215279 : std::string CPLGetPathSafe(const char *pszFilename)
     152             : 
     153             : {
     154      215279 :     size_t nSuffixPos = 0;
     155      215279 :     if (STARTS_WITH(pszFilename, "/vsicurl/http"))
     156             :     {
     157          12 :         const char *pszQuestionMark = strchr(pszFilename, '?');
     158          12 :         if (pszQuestionMark)
     159           1 :             nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
     160             :     }
     161      215267 :     else if (STARTS_WITH(pszFilename, "/vsicurl?") &&
     162           1 :              strstr(pszFilename, "url="))
     163             :     {
     164           2 :         std::string osRet;
     165             :         const CPLStringList aosTokens(
     166           2 :             CSLTokenizeString2(pszFilename + strlen("/vsicurl?"), "&", 0));
     167           3 :         for (int i = 0; i < aosTokens.size(); i++)
     168             :         {
     169           2 :             if (osRet.empty())
     170           1 :                 osRet = "/vsicurl?";
     171             :             else
     172           1 :                 osRet += '&';
     173           3 :             if (STARTS_WITH(aosTokens[i], "url=") &&
     174           1 :                 !STARTS_WITH(aosTokens[i], "url=/vsicurl"))
     175             :             {
     176             :                 char *pszUnescaped =
     177           1 :                     CPLUnescapeString(aosTokens[i], nullptr, CPLES_URL);
     178           1 :                 char *pszPath = CPLEscapeString(
     179           2 :                     CPLGetPathSafe(pszUnescaped + strlen("url=")).c_str(), -1,
     180             :                     CPLES_URL);
     181           1 :                 osRet += "url=";
     182           1 :                 osRet += pszPath;
     183           1 :                 CPLFree(pszPath);
     184           1 :                 CPLFree(pszUnescaped);
     185             :             }
     186             :             else
     187             :             {
     188           1 :                 osRet += aosTokens[i];
     189             :             }
     190             :         }
     191           1 :         return osRet;
     192             :     }
     193             : 
     194      215278 :     const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
     195      215281 :     if (iFileStart == 0)
     196             :     {
     197         558 :         return std::string();
     198             :     }
     199             : 
     200      429421 :     std::string osRet(pszFilename, iFileStart);
     201             : 
     202      214710 :     if (iFileStart > 1 && (osRet.back() == '/' || osRet.back() == '\\'))
     203      214371 :         osRet.pop_back();
     204             : 
     205      214713 :     if (nSuffixPos)
     206             :     {
     207           1 :         osRet += (pszFilename + nSuffixPos);
     208             :     }
     209             : 
     210      214713 :     return osRet;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                             CPLGetPath()                             */
     215             : /************************************************************************/
     216             : 
     217             : /**
     218             :  * Extract directory path portion of filename.
     219             :  *
     220             :  * Returns a string containing the directory path portion of the passed
     221             :  * filename.  If there is no path in the passed filename an empty string
     222             :  * will be returned (not NULL).
     223             :  *
     224             :  * \code{.cpp}
     225             :  * CPLGetPath( "abc/def.xyz" ) == "abc"
     226             :  * CPLGetPath( "/abc/def/" ) == "/abc/def"
     227             :  * CPLGetPath( "/" ) == "/"
     228             :  * CPLGetPath( "/abc/def" ) == "/abc"
     229             :  * CPLGetPath( "abc" ) == ""
     230             :  * \endcode
     231             :  *
     232             :  * @param pszFilename the filename potentially including a path.
     233             :  *
     234             :  * @return Path in an internal string which must not be freed.  The string
     235             :  * may be destroyed by the next CPL filename handling call.  The returned
     236             :  * will generally not contain a trailing path separator.
     237             :  *
     238             :  * @deprecated If using C++, prefer using CPLGetPathSafe() instead
     239             :  */
     240             : 
     241          50 : const char *CPLGetPath(const char *pszFilename)
     242             : 
     243             : {
     244          50 :     return CPLPathReturnTLSString(CPLGetPathSafe(pszFilename), __FUNCTION__);
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                             CPLGetDirname()                          */
     249             : /************************************************************************/
     250             : 
     251             : /**
     252             :  * Extract directory path portion of filename.
     253             :  *
     254             :  * Returns a string containing the directory path portion of the passed
     255             :  * filename.  If there is no path in the passed filename the dot will be
     256             :  * returned.  It is the only difference from CPLGetPath().
     257             :  *
     258             :  * \code{.cpp}
     259             :  * CPLGetDirnameSafe( "abc/def.xyz" ) == "abc"
     260             :  * CPLGetDirnameSafe( "/abc/def/" ) == "/abc/def"
     261             :  * CPLGetDirnameSafe( "/" ) == "/"
     262             :  * CPLGetDirnameSafe( "/abc/def" ) == "/abc"
     263             :  * CPLGetDirnameSafe( "abc" ) == "."
     264             :  * \endcode
     265             :  *
     266             :  * @param pszFilename the filename potentially including a path.
     267             :  *
     268             :  * @return Path
     269             :  *
     270             :  * @since 3.11
     271             :  */
     272             : 
     273      143619 : std::string CPLGetDirnameSafe(const char *pszFilename)
     274             : 
     275             : {
     276      143619 :     size_t nSuffixPos = 0;
     277      143619 :     if (STARTS_WITH(pszFilename, "/vsicurl/http"))
     278             :     {
     279         149 :         const char *pszQuestionMark = strchr(pszFilename, '?');
     280         149 :         if (pszQuestionMark)
     281           1 :             nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
     282             :     }
     283      143470 :     else if (STARTS_WITH(pszFilename, "/vsicurl?") &&
     284          14 :              strstr(pszFilename, "url="))
     285             :     {
     286          28 :         std::string osRet;
     287             :         const CPLStringList aosTokens(
     288          28 :             CSLTokenizeString2(pszFilename + strlen("/vsicurl?"), "&", 0));
     289          42 :         for (int i = 0; i < aosTokens.size(); i++)
     290             :         {
     291          28 :             if (osRet.empty())
     292          14 :                 osRet = "/vsicurl?";
     293             :             else
     294          14 :                 osRet += '&';
     295          42 :             if (STARTS_WITH(aosTokens[i], "url=") &&
     296          14 :                 !STARTS_WITH(aosTokens[i], "url=/vsicurl"))
     297             :             {
     298             :                 char *pszUnescaped =
     299          14 :                     CPLUnescapeString(aosTokens[i], nullptr, CPLES_URL);
     300          14 :                 char *pszPath = CPLEscapeString(
     301          14 :                     CPLGetDirname(pszUnescaped + strlen("url=")), -1,
     302             :                     CPLES_URL);
     303          14 :                 osRet += "url=";
     304          14 :                 osRet += pszPath;
     305          14 :                 CPLFree(pszPath);
     306          14 :                 CPLFree(pszUnescaped);
     307             :             }
     308             :             else
     309             :             {
     310          14 :                 osRet += aosTokens[i];
     311             :             }
     312             :         }
     313          14 :         return osRet;
     314             :     }
     315             : 
     316      143605 :     const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
     317      143597 :     if (iFileStart == 0)
     318             :     {
     319          64 :         return std::string(".");
     320             :     }
     321             : 
     322      287012 :     std::string osRet(pszFilename, iFileStart);
     323             : 
     324      143463 :     if (iFileStart > 1 && (osRet.back() == '/' || osRet.back() == '\\'))
     325      143506 :         osRet.pop_back();
     326             : 
     327      143408 :     if (nSuffixPos)
     328             :     {
     329           1 :         osRet += (pszFilename + nSuffixPos);
     330             :     }
     331             : 
     332      143408 :     return osRet;
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                             CPLGetDirname()                          */
     337             : /************************************************************************/
     338             : 
     339             : /**
     340             :  * Extract directory path portion of filename.
     341             :  *
     342             :  * Returns a string containing the directory path portion of the passed
     343             :  * filename.  If there is no path in the passed filename the dot will be
     344             :  * returned.  It is the only difference from CPLGetPath().
     345             :  *
     346             :  * \code{.cpp}
     347             :  * CPLGetDirname( "abc/def.xyz" ) == "abc"
     348             :  * CPLGetDirname( "/abc/def/" ) == "/abc/def"
     349             :  * CPLGetDirname( "/" ) == "/"
     350             :  * CPLGetDirname( "/abc/def" ) == "/abc"
     351             :  * CPLGetDirname( "abc" ) == "."
     352             :  * \endcode
     353             :  *
     354             :  * @param pszFilename the filename potentially including a path.
     355             :  *
     356             :  * @return Path in an internal string which must not be freed.  The string
     357             :  * may be destroyed by the next CPL filename handling call.  The returned
     358             :  * will generally not contain a trailing path separator.
     359             :  */
     360             : 
     361          48 : const char *CPLGetDirname(const char *pszFilename)
     362             : 
     363             : {
     364          48 :     return CPLPathReturnTLSString(CPLGetDirnameSafe(pszFilename), __FUNCTION__);
     365             : }
     366             : 
     367             : /************************************************************************/
     368             : /*                           CPLGetFilename()                           */
     369             : /************************************************************************/
     370             : 
     371             : /**
     372             :  * Extract non-directory portion of filename.
     373             :  *
     374             :  * Returns a string containing the bare filename portion of the passed
     375             :  * filename.  If there is no filename (passed value ends in trailing directory
     376             :  * separator) an empty string is returned.
     377             :  *
     378             :  * \code{.cpp}
     379             :  * CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
     380             :  * CPLGetFilename( "/abc/def/" ) == ""
     381             :  * CPLGetFilename( "abc/def" ) == "def"
     382             :  * \endcode
     383             :  *
     384             :  * @param pszFullFilename the full filename potentially including a path.
     385             :  *
     386             :  * @return just the non-directory portion of the path (points back into
     387             :  * original string).
     388             :  */
     389             : 
     390      779615 : const char *CPLGetFilename(const char *pszFullFilename)
     391             : 
     392             : {
     393      779615 :     const int iFileStart = CPLFindFilenameStart(pszFullFilename);
     394             : 
     395      779613 :     return pszFullFilename + iFileStart;
     396             : }
     397             : 
     398             : /************************************************************************/
     399             : /*                       CPLGetBasenameSafe()                           */
     400             : /************************************************************************/
     401             : 
     402             : /**
     403             :  * Extract basename (non-directory, non-extension) portion of filename.
     404             :  *
     405             :  * Returns a string containing the file basename portion of the passed
     406             :  * name.  If there is no basename (passed value ends in trailing directory
     407             :  * separator, or filename starts with a dot) an empty string is returned.
     408             :  *
     409             :  * \code{.cpp}
     410             :  * CPLGetBasename( "abc/def.xyz" ) == "def"
     411             :  * CPLGetBasename( "abc/def" ) == "def"
     412             :  * CPLGetBasename( "abc/def/" ) == ""
     413             :  * \endcode
     414             :  *
     415             :  * @param pszFullFilename the full filename potentially including a path.
     416             :  *
     417             :  * @return just the non-directory, non-extension portion of the path
     418             :  *
     419             :  * @since 3.11
     420             :  */
     421             : 
     422      436972 : std::string CPLGetBasenameSafe(const char *pszFullFilename)
     423             : 
     424             : {
     425             :     const size_t iFileStart =
     426      436972 :         static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
     427             : 
     428      436974 :     size_t iExtStart = strlen(pszFullFilename);
     429     2468750 :     for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
     430             :          iExtStart--)
     431             :     {
     432             :     }
     433             : 
     434      436974 :     if (iExtStart == iFileStart)
     435       26696 :         iExtStart = strlen(pszFullFilename);
     436             : 
     437      436974 :     const size_t nLength = iExtStart - iFileStart;
     438      436974 :     return std::string(pszFullFilename + iFileStart, nLength);
     439             : }
     440             : 
     441             : /************************************************************************/
     442             : /*                           CPLGetBasename()                           */
     443             : /************************************************************************/
     444             : 
     445             : /**
     446             :  * Extract basename (non-directory, non-extension) portion of filename.
     447             :  *
     448             :  * Returns a string containing the file basename portion of the passed
     449             :  * name.  If there is no basename (passed value ends in trailing directory
     450             :  * separator, or filename starts with a dot) an empty string is returned.
     451             :  *
     452             :  * \code{.cpp}
     453             :  * CPLGetBasename( "abc/def.xyz" ) == "def"
     454             :  * CPLGetBasename( "abc/def" ) == "def"
     455             :  * CPLGetBasename( "abc/def/" ) == ""
     456             :  * \endcode
     457             :  *
     458             :  * @param pszFullFilename the full filename potentially including a path.
     459             :  *
     460             :  * @return just the non-directory, non-extension portion of the path in
     461             :  * an internal string which must not be freed.  The string
     462             :  * may be destroyed by the next CPL filename handling call.
     463             :  *
     464             :  * @deprecated If using C++, prefer using CPLGetBasenameSafe() instead
     465             :  */
     466             : 
     467          98 : const char *CPLGetBasename(const char *pszFullFilename)
     468             : 
     469             : {
     470         196 :     return CPLPathReturnTLSString(CPLGetBasenameSafe(pszFullFilename),
     471         196 :                                   __FUNCTION__);
     472             : }
     473             : 
     474             : /************************************************************************/
     475             : /*                        CPLGetExtensionSafe()                         */
     476             : /************************************************************************/
     477             : 
     478             : /**
     479             :  * Extract filename extension from full filename.
     480             :  *
     481             :  * Returns a string containing the extension portion of the passed
     482             :  * name.  If there is no extension (the filename has no dot) an empty string
     483             :  * is returned.  The returned extension will not include the period.
     484             :  *
     485             :  * \code{.cpp}
     486             :  * CPLGetExtensionSafe( "abc/def.xyz" ) == "xyz"
     487             :  * CPLGetExtensionSafe( "abc/def" ) == ""
     488             :  * \endcode
     489             :  *
     490             :  * @param pszFullFilename the full filename potentially including a path.
     491             :  *
     492             :  * @return just the extension portion of the path.
     493             :  *
     494             :  * @since 3.11
     495             :  */
     496             : 
     497      610405 : std::string CPLGetExtensionSafe(const char *pszFullFilename)
     498             : 
     499             : {
     500      610405 :     if (pszFullFilename[0] == '\0')
     501        3583 :         return std::string();
     502             : 
     503             :     size_t iFileStart =
     504      606822 :         static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
     505      606819 :     size_t iExtStart = strlen(pszFullFilename);
     506     4394260 :     for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
     507             :          iExtStart--)
     508             :     {
     509             :     }
     510             : 
     511      606819 :     if (iExtStart == iFileStart)
     512      104500 :         iExtStart = strlen(pszFullFilename) - 1;
     513             : 
     514             :     // If the extension is too long, it is very much likely not an extension,
     515             :     // but another component of the path
     516      606819 :     const size_t knMaxExtensionSize = 10;
     517      606819 :     if (strlen(pszFullFilename + iExtStart + 1) > knMaxExtensionSize)
     518        2069 :         return "";
     519             : 
     520      604750 :     return std::string(pszFullFilename + iExtStart + 1);
     521             : }
     522             : 
     523             : /************************************************************************/
     524             : /*                           CPLGetExtension()                          */
     525             : /************************************************************************/
     526             : 
     527             : /**
     528             :  * Extract filename extension from full filename.
     529             :  *
     530             :  * Returns a string containing the extension portion of the passed
     531             :  * name.  If there is no extension (the filename has no dot) an empty string
     532             :  * is returned.  The returned extension will not include the period.
     533             :  *
     534             :  * \code{.cpp}
     535             :  * CPLGetExtension( "abc/def.xyz" ) == "xyz"
     536             :  * CPLGetExtension( "abc/def" ) == ""
     537             :  * \endcode
     538             :  *
     539             :  * @param pszFullFilename the full filename potentially including a path.
     540             :  *
     541             :  * @return just the extension portion of the path in
     542             :  * an internal string which must not be freed.  The string
     543             :  * may be destroyed by the next CPL filename handling call.
     544             :  *
     545             :  * @deprecated If using C++, prefer using CPLGetExtensionSafe() instead
     546             :  */
     547             : 
     548          46 : const char *CPLGetExtension(const char *pszFullFilename)
     549             : 
     550             : {
     551          92 :     return CPLPathReturnTLSString(CPLGetExtensionSafe(pszFullFilename),
     552          92 :                                   __FUNCTION__);
     553             : }
     554             : 
     555             : /************************************************************************/
     556             : /*                         CPLGetCurrentDir()                           */
     557             : /************************************************************************/
     558             : 
     559             : /**
     560             :  * Get the current working directory name.
     561             :  *
     562             :  * @return a pointer to buffer, containing current working directory path
     563             :  * or NULL in case of error.  User is responsible to free that buffer
     564             :  * after usage with CPLFree() function.
     565             :  * If HAVE_GETCWD macro is not defined, the function returns NULL.
     566             :  **/
     567             : 
     568             : #ifdef _WIN32
     569             : char *CPLGetCurrentDir()
     570             : {
     571             :     const size_t nPathMax = _MAX_PATH;
     572             :     wchar_t *pwszDirPath =
     573             :         static_cast<wchar_t *>(VSI_MALLOC_VERBOSE(nPathMax * sizeof(wchar_t)));
     574             :     char *pszRet = nullptr;
     575             :     if (pwszDirPath != nullptr && _wgetcwd(pwszDirPath, nPathMax) != nullptr)
     576             :     {
     577             :         pszRet = CPLRecodeFromWChar(pwszDirPath, CPL_ENC_UCS2, CPL_ENC_UTF8);
     578             :     }
     579             :     CPLFree(pwszDirPath);
     580             :     return pszRet;
     581             : }
     582             : #elif defined(HAVE_GETCWD)
     583        5137 : char *CPLGetCurrentDir()
     584             : {
     585             : #if PATH_MAX
     586        5137 :     const size_t nPathMax = PATH_MAX;
     587             : #else
     588             :     const size_t nPathMax = 8192;
     589             : #endif
     590             : 
     591        5137 :     char *pszDirPath = static_cast<char *>(VSI_MALLOC_VERBOSE(nPathMax));
     592        5137 :     if (!pszDirPath)
     593           0 :         return nullptr;
     594             : 
     595        5137 :     return getcwd(pszDirPath, nPathMax);
     596             : }
     597             : #else   // !HAVE_GETCWD
     598             : char *CPLGetCurrentDir()
     599             : {
     600             :     return nullptr;
     601             : }
     602             : #endif  // HAVE_GETCWD
     603             : 
     604             : /************************************************************************/
     605             : /*                         CPLResetExtension()                          */
     606             : /************************************************************************/
     607             : 
     608             : /**
     609             :  * Replace the extension with the provided one.
     610             :  *
     611             :  * @param pszPath the input path, this string is not altered.
     612             :  * @param pszExt the new extension to apply to the given path.
     613             :  *
     614             :  * @return an altered filename with the new extension.
     615             :  *
     616             :  * @since 3.11
     617             :  */
     618             : 
     619      164213 : std::string CPLResetExtensionSafe(const char *pszPath, const char *pszExt)
     620             : 
     621             : {
     622      164213 :     std::string osRet(pszPath);
     623             : 
     624             :     /* -------------------------------------------------------------------- */
     625             :     /*      First, try and strip off any existing extension.                */
     626             :     /* -------------------------------------------------------------------- */
     627             : 
     628      887102 :     for (size_t i = osRet.size(); i > 0;)
     629             :     {
     630      886823 :         --i;
     631      886823 :         if (osRet[i] == '.')
     632             :         {
     633      147739 :             osRet.resize(i);
     634      147734 :             break;
     635             :         }
     636      739084 :         else if (osRet[i] == '/' || osRet[i] == '\\' || osRet[i] == ':')
     637             :         {
     638       16198 :             break;
     639             :         }
     640             :     }
     641             : 
     642             :     /* -------------------------------------------------------------------- */
     643             :     /*      Append the new extension.                                       */
     644             :     /* -------------------------------------------------------------------- */
     645      164205 :     osRet += '.';
     646      164204 :     osRet += pszExt;
     647             : 
     648      164208 :     return osRet;
     649             : }
     650             : 
     651             : /************************************************************************/
     652             : /*                         CPLResetExtension()                          */
     653             : /************************************************************************/
     654             : 
     655             : /**
     656             :  * Replace the extension with the provided one.
     657             :  *
     658             :  * @param pszPath the input path, this string is not altered.
     659             :  * @param pszExt the new extension to apply to the given path.
     660             :  *
     661             :  * @return an altered filename with the new extension.    Do not
     662             :  * modify or free the returned string.  The string may be destroyed by the
     663             :  * next CPL call.
     664             :  *
     665             :  * @deprecated If using C++, prefer using CPLResetExtensionSafe() instead
     666             :  */
     667             : 
     668         133 : const char *CPLResetExtension(const char *pszPath, const char *pszExt)
     669             : 
     670             : {
     671         266 :     return CPLPathReturnTLSString(CPLResetExtensionSafe(pszPath, pszExt),
     672         266 :                                   __FUNCTION__);
     673             : }
     674             : 
     675             : /************************************************************************/
     676             : /*                        CPLFormFilenameSafe()                         */
     677             : /************************************************************************/
     678             : 
     679             : /**
     680             :  * Build a full file path from a passed path, file basename and extension.
     681             :  *
     682             :  * The path, and extension are optional.  The basename may in fact contain
     683             :  * an extension if desired.
     684             :  *
     685             :  * \code{.cpp}
     686             :  * CPLFormFilenameSafe("abc/xyz", "def", ".dat" ) == "abc/xyz/def.dat"
     687             :  * CPLFormFilenameSafe(NULL,"def", NULL ) == "def"
     688             :  * CPLFormFilenameSafe(NULL, "abc/def.dat", NULL ) == "abc/def.dat"
     689             :  * CPLFormFilenameSafe("/abc/xyz/", "def.dat", NULL ) == "/abc/xyz/def.dat"
     690             :  * CPLFormFilenameSafe("/a/b/c", "../d", NULL ) == "/a/b/d" (since 3.10.1)
     691             :  * \endcode
     692             :  *
     693             :  * @param pszPath directory path to the directory containing the file.  This
     694             :  * may be relative or absolute, and may have a trailing path separator or
     695             :  * not.  May be NULL.
     696             :  *
     697             :  * @param pszBasename file basename.  May optionally have path and/or
     698             :  * extension.  Must *NOT* be NULL.
     699             :  *
     700             :  * @param pszExtension file extension, optionally including the period.  May
     701             :  * be NULL.
     702             :  *
     703             :  * @return a fully formed filename.
     704             :  *
     705             :  * @since 3.11
     706             :  */
     707             : 
     708      379793 : std::string CPLFormFilenameSafe(const char *pszPath, const char *pszBasename,
     709             :                                 const char *pszExtension)
     710             : 
     711             : {
     712      379793 :     if (pszBasename[0] == '.' &&
     713        9258 :         (pszBasename[1] == '/' || pszBasename[1] == '\\'))
     714          62 :         pszBasename += 2;
     715             : 
     716      379793 :     const char *pszAddedPathSep = "";
     717      379793 :     const char *pszAddedExtSep = "";
     718             : 
     719      379793 :     if (pszPath == nullptr)
     720       10943 :         pszPath = "";
     721      379793 :     size_t nLenPath = strlen(pszPath);
     722             : 
     723      379793 :     const char *pszQuestionMark = nullptr;
     724      379793 :     if (STARTS_WITH_CI(pszPath, "/vsicurl/http"))
     725             :     {
     726         170 :         pszQuestionMark = strchr(pszPath, '?');
     727         170 :         if (pszQuestionMark)
     728             :         {
     729           1 :             nLenPath = pszQuestionMark - pszPath;
     730             :         }
     731         170 :         pszAddedPathSep = "/";
     732             :     }
     733             : 
     734      645160 :     if (!CPLIsFilenameRelative(pszPath) && pszBasename[0] == '.' &&
     735      645462 :         pszBasename[1] == '.' &&
     736         351 :         (pszBasename[2] == 0 || pszBasename[2] == '\\' ||
     737         118 :          pszBasename[2] == '/'))
     738             :     {
     739             :         // "/a/b/" + "..[/something]" --> "/a[/something]"
     740             :         // "/a/b" + "..[/something]" --> "/a[/something]"
     741         351 :         if (pszPath[nLenPath - 1] == '\\' || pszPath[nLenPath - 1] == '/')
     742          14 :             nLenPath--;
     743             :         while (true)
     744             :         {
     745         402 :             const char *pszBasenameOri = pszBasename;
     746         402 :             const size_t nLenPathOri = nLenPath;
     747        4930 :             while (nLenPath > 0 && pszPath[nLenPath - 1] != '\\' &&
     748        4919 :                    pszPath[nLenPath - 1] != '/')
     749             :             {
     750        4528 :                 nLenPath--;
     751             :             }
     752         402 :             if (nLenPath == 1 && pszPath[0] == '/')
     753             :             {
     754          18 :                 pszBasename += 2;
     755          18 :                 if (pszBasename[0] == '/' || pszBasename[0] == '\\')
     756          10 :                     pszBasename++;
     757          18 :                 if (*pszBasename == '.')
     758             :                 {
     759           1 :                     pszBasename = pszBasenameOri;
     760           1 :                     nLenPath = nLenPathOri;
     761           1 :                     if (pszAddedPathSep[0] == 0)
     762           1 :                         pszAddedPathSep =
     763           1 :                             pszPath[0] == '/'
     764           1 :                                 ? "/"
     765           0 :                                 : VSIGetDirectorySeparator(pszPath);
     766             :                 }
     767          18 :                 break;
     768             :             }
     769         384 :             else if ((nLenPath > 1 && pszPath[0] == '/') ||
     770          11 :                      (nLenPath > 2 && pszPath[1] == ':') ||
     771           1 :                      (nLenPath > 6 && strncmp(pszPath, "\\\\$\\", 4) == 0))
     772             :             {
     773         377 :                 nLenPath--;
     774         377 :                 pszBasename += 2;
     775         377 :                 if ((pszBasename[0] == '/' || pszBasename[0] == '\\') &&
     776         158 :                     pszBasename[1] == '.' && pszBasename[2] == '.')
     777             :                 {
     778          51 :                     pszBasename++;
     779             :                 }
     780             :                 else
     781             :                 {
     782             :                     break;
     783             :                 }
     784             :             }
     785             :             else
     786             :             {
     787             :                 // cppcheck-suppress redundantAssignment
     788           7 :                 pszBasename = pszBasenameOri;
     789           7 :                 nLenPath = nLenPathOri;
     790           7 :                 if (pszAddedPathSep[0] == 0)
     791           7 :                     pszAddedPathSep = pszPath[0] == '/'
     792           7 :                                           ? "/"
     793           2 :                                           : VSIGetDirectorySeparator(pszPath);
     794           7 :                 break;
     795             :             }
     796          51 :         }
     797             :     }
     798      379393 :     else if (nLenPath > 0 && pszPath[nLenPath - 1] != '/' &&
     799      366453 :              pszPath[nLenPath - 1] != '\\')
     800             :     {
     801      366416 :         if (pszAddedPathSep[0] == 0)
     802      366234 :             pszAddedPathSep = VSIGetDirectorySeparator(pszPath);
     803             :     }
     804             : 
     805      379810 :     if (pszExtension == nullptr)
     806      223761 :         pszExtension = "";
     807      156049 :     else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
     808      142710 :         pszAddedExtSep = ".";
     809             : 
     810      379810 :     std::string osRes;
     811      379775 :     osRes.reserve(nLenPath + strlen(pszAddedPathSep) + strlen(pszBasename) +
     812      379775 :                   strlen(pszAddedExtSep) + strlen(pszExtension) +
     813      379775 :                   (pszQuestionMark ? strlen(pszQuestionMark) : 0));
     814      379749 :     osRes.assign(pszPath, nLenPath);
     815      379733 :     osRes += pszAddedPathSep;
     816      379763 :     osRes += pszBasename;
     817      379734 :     osRes += pszAddedExtSep;
     818      379735 :     osRes += pszExtension;
     819             : 
     820      379705 :     if (pszQuestionMark)
     821             :     {
     822           1 :         osRes += pszQuestionMark;
     823             :     }
     824             : 
     825      379700 :     return osRes;
     826             : }
     827             : 
     828             : /************************************************************************/
     829             : /*                          CPLFormFilename()                           */
     830             : /************************************************************************/
     831             : 
     832             : /**
     833             :  * Build a full file path from a passed path, file basename and extension.
     834             :  *
     835             :  * The path, and extension are optional.  The basename may in fact contain
     836             :  * an extension if desired.
     837             :  *
     838             :  * \code{.cpp}
     839             :  * CPLFormFilename("abc/xyz", "def", ".dat" ) == "abc/xyz/def.dat"
     840             :  * CPLFormFilename(NULL,"def", NULL ) == "def"
     841             :  * CPLFormFilename(NULL, "abc/def.dat", NULL ) == "abc/def.dat"
     842             :  * CPLFormFilename("/abc/xyz/", "def.dat", NULL ) == "/abc/xyz/def.dat"
     843             :  * CPLFormFilename("/a/b/c", "../d", NULL ) == "/a/b/d" (since 3.10.1)
     844             :  * \endcode
     845             :  *
     846             :  * @param pszPath directory path to the directory containing the file.  This
     847             :  * may be relative or absolute, and may have a trailing path separator or
     848             :  * not.  May be NULL.
     849             :  *
     850             :  * @param pszBasename file basename.  May optionally have path and/or
     851             :  * extension.  Must *NOT* be NULL.
     852             :  *
     853             :  * @param pszExtension file extension, optionally including the period.  May
     854             :  * be NULL.
     855             :  *
     856             :  * @return a fully formed filename in an internal static string.  Do not
     857             :  * modify or free the returned string.  The string may be destroyed by the
     858             :  * next CPL call.
     859             :  *
     860             :  * @deprecated If using C++, prefer using CPLFormFilenameSafe() instead
     861             :  */
     862         116 : const char *CPLFormFilename(const char *pszPath, const char *pszBasename,
     863             :                             const char *pszExtension)
     864             : 
     865             : {
     866         116 :     return CPLPathReturnTLSString(
     867         232 :         CPLFormFilenameSafe(pszPath, pszBasename, pszExtension), __FUNCTION__);
     868             : }
     869             : 
     870             : /************************************************************************/
     871             : /*                       CPLFormCIFilenameSafe()                        */
     872             : /************************************************************************/
     873             : 
     874             : /**
     875             :  * Case insensitive file searching, returning full path.
     876             :  *
     877             :  * This function tries to return the path to a file regardless of
     878             :  * whether the file exactly matches the basename, and extension case, or
     879             :  * is all upper case, or all lower case.  The path is treated as case
     880             :  * sensitive.  This function is equivalent to CPLFormFilename() on
     881             :  * case insensitive file systems (like Windows).
     882             :  *
     883             :  * @param pszPath directory path to the directory containing the file.  This
     884             :  * may be relative or absolute, and may have a trailing path separator or
     885             :  * not.  May be NULL.
     886             :  *
     887             :  * @param pszBasename file basename.  May optionally have path and/or
     888             :  * extension.  May not be NULL.
     889             :  *
     890             :  * @param pszExtension file extension, optionally including the period.  May
     891             :  * be NULL.
     892             :  *
     893             :  * @return a fully formed filename.
     894             :  *
     895             :  * @since 3.11
     896             :  */
     897             : 
     898        4687 : std::string CPLFormCIFilenameSafe(const char *pszPath, const char *pszBasename,
     899             :                                   const char *pszExtension)
     900             : 
     901             : {
     902             :     // On case insensitive filesystems, just default to CPLFormFilename().
     903        4687 :     if (!VSIIsCaseSensitiveFS(pszPath))
     904           0 :         return CPLFormFilenameSafe(pszPath, pszBasename, pszExtension);
     905             : 
     906        4687 :     const char *pszAddedExtSep = "";
     907        4687 :     size_t nLen = strlen(pszBasename) + 2;
     908             : 
     909        4687 :     if (pszExtension != nullptr)
     910        2214 :         nLen += strlen(pszExtension);
     911             : 
     912        4687 :     char *pszFilename = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
     913        4687 :     if (pszFilename == nullptr)
     914           0 :         return "";
     915             : 
     916        4687 :     if (pszExtension == nullptr)
     917        2473 :         pszExtension = "";
     918        2214 :     else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
     919        2162 :         pszAddedExtSep = ".";
     920             : 
     921        4687 :     snprintf(pszFilename, nLen, "%s%s%s", pszBasename, pszAddedExtSep,
     922             :              pszExtension);
     923             : 
     924        9374 :     std::string osRet = CPLFormFilenameSafe(pszPath, pszFilename, nullptr);
     925             :     VSIStatBufL sStatBuf;
     926        4687 :     int nStatRet = VSIStatExL(osRet.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG);
     927             : 
     928        4687 :     if (nStatRet != 0)
     929             :     {
     930       55955 :         for (size_t i = 0; pszFilename[i] != '\0'; i++)
     931             :         {
     932       51757 :             pszFilename[i] = static_cast<char>(CPLToupper(pszFilename[i]));
     933             :         }
     934             : 
     935             :         std::string osTmpPath(
     936        8396 :             CPLFormFilenameSafe(pszPath, pszFilename, nullptr));
     937             :         nStatRet =
     938        4198 :             VSIStatExL(osTmpPath.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG);
     939        4198 :         if (nStatRet == 0)
     940           3 :             osRet = std::move(osTmpPath);
     941             :     }
     942             : 
     943        4687 :     if (nStatRet != 0)
     944             :     {
     945       55931 :         for (size_t i = 0; pszFilename[i] != '\0'; i++)
     946             :         {
     947       51736 :             pszFilename[i] = static_cast<char>(
     948       51736 :                 CPLTolower(static_cast<unsigned char>(pszFilename[i])));
     949             :         }
     950             : 
     951             :         std::string osTmpPath(
     952        8390 :             CPLFormFilenameSafe(pszPath, pszFilename, nullptr));
     953             :         nStatRet =
     954        4195 :             VSIStatExL(osTmpPath.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG);
     955        4195 :         if (nStatRet == 0)
     956           8 :             osRet = std::move(osTmpPath);
     957             :     }
     958             : 
     959        4687 :     if (nStatRet != 0)
     960        4187 :         osRet = CPLFormFilenameSafe(pszPath, pszBasename, pszExtension);
     961             : 
     962        4687 :     CPLFree(pszFilename);
     963             : 
     964        4687 :     return osRet;
     965             : }
     966             : 
     967             : /************************************************************************/
     968             : /*                          CPLFormCIFilename()                         */
     969             : /************************************************************************/
     970             : 
     971             : /**
     972             :  * Case insensitive file searching, returning full path.
     973             :  *
     974             :  * This function tries to return the path to a file regardless of
     975             :  * whether the file exactly matches the basename, and extension case, or
     976             :  * is all upper case, or all lower case.  The path is treated as case
     977             :  * sensitive.  This function is equivalent to CPLFormFilename() on
     978             :  * case insensitive file systems (like Windows).
     979             :  *
     980             :  * @param pszPath directory path to the directory containing the file.  This
     981             :  * may be relative or absolute, and may have a trailing path separator or
     982             :  * not.  May be NULL.
     983             :  *
     984             :  * @param pszBasename file basename.  May optionally have path and/or
     985             :  * extension.  May not be NULL.
     986             :  *
     987             :  * @param pszExtension file extension, optionally including the period.  May
     988             :  * be NULL.
     989             :  *
     990             :  * @return a fully formed filename in an internal static string.  Do not
     991             :  * modify or free the returned string.  The string may be destroyed by the
     992             :  * next CPL call.
     993             :  *
     994             :  * @deprecated If using C++, prefer using CPLFormCIFilenameSafe() instead
     995             : */
     996             : 
     997           0 : const char *CPLFormCIFilename(const char *pszPath, const char *pszBasename,
     998             :                               const char *pszExtension)
     999             : 
    1000             : {
    1001           0 :     return CPLPathReturnTLSString(
    1002           0 :         CPLFormCIFilenameSafe(pszPath, pszBasename, pszExtension),
    1003           0 :         __FUNCTION__);
    1004             : }
    1005             : 
    1006             : /************************************************************************/
    1007             : /*                   CPLProjectRelativeFilenameSafe()                   */
    1008             : /************************************************************************/
    1009             : 
    1010             : /**
    1011             :  * Find a file relative to a project file.
    1012             :  *
    1013             :  * Given the path to a "project" directory, and a path to a secondary file
    1014             :  * referenced from that project, build a path to the secondary file
    1015             :  * that the current application can use.  If the secondary path is already
    1016             :  * absolute, rather than relative, then it will be returned unaltered.
    1017             :  *
    1018             :  * Examples:
    1019             :  * \code{.cpp}
    1020             :  * CPLProjectRelativeFilenameSafe("abc/def", "tmp/abc.gif") == "abc/def/tmp/abc.gif"
    1021             :  * CPLProjectRelativeFilenameSafe("abc/def", "/tmp/abc.gif") == "/tmp/abc.gif"
    1022             :  * CPLProjectRelativeFilenameSafe("/xy", "abc.gif") == "/xy/abc.gif"
    1023             :  * CPLProjectRelativeFilenameSafe("/abc/def", "../abc.gif") == "/abc/def/../abc.gif"
    1024             :  * CPLProjectRelativeFilenameSafe("C:\WIN", "abc.gif") == "C:\WIN\abc.gif"
    1025             :  * \endcode
    1026             :  *
    1027             :  * @param pszProjectDir the directory relative to which the secondary files
    1028             :  * path should be interpreted.
    1029             :  * @param pszSecondaryFilename the filename (potentially with path) that
    1030             :  * is to be interpreted relative to the project directory.
    1031             :  *
    1032             :  * @return a composed path to the secondary file.
    1033             :  *
    1034             :  * @since 3.11
    1035             :  */
    1036             : 
    1037        4324 : std::string CPLProjectRelativeFilenameSafe(const char *pszProjectDir,
    1038             :                                            const char *pszSecondaryFilename)
    1039             : 
    1040             : {
    1041        8403 :     if (pszProjectDir == nullptr || pszProjectDir[0] == 0 ||
    1042        4079 :         !CPLIsFilenameRelative(pszSecondaryFilename))
    1043             :     {
    1044         687 :         return pszSecondaryFilename;
    1045             :     }
    1046             : 
    1047        7274 :     std::string osRes(pszProjectDir);
    1048        3637 :     if (osRes.back() != '/' && osRes.back() != '\\')
    1049             :     {
    1050        3637 :         osRes += VSIGetDirectorySeparator(pszProjectDir);
    1051             :     }
    1052             : 
    1053        3637 :     osRes += pszSecondaryFilename;
    1054        3637 :     return osRes;
    1055             : }
    1056             : 
    1057             : /************************************************************************/
    1058             : /*                     CPLProjectRelativeFilename()                     */
    1059             : /************************************************************************/
    1060             : 
    1061             : /**
    1062             :  * Find a file relative to a project file.
    1063             :  *
    1064             :  * Given the path to a "project" directory, and a path to a secondary file
    1065             :  * referenced from that project, build a path to the secondary file
    1066             :  * that the current application can use.  If the secondary path is already
    1067             :  * absolute, rather than relative, then it will be returned unaltered.
    1068             :  *
    1069             :  * Examples:
    1070             :  * \code{.cpp}
    1071             :  * CPLProjectRelativeFilename("abc/def", "tmp/abc.gif") == "abc/def/tmp/abc.gif"
    1072             :  * CPLProjectRelativeFilename("abc/def", "/tmp/abc.gif") == "/tmp/abc.gif"
    1073             :  * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
    1074             :  * CPLProjectRelativeFilename("/abc/def", "../abc.gif") == "/abc/def/../abc.gif"
    1075             :  * CPLProjectRelativeFilename("C:\WIN", "abc.gif") == "C:\WIN\abc.gif"
    1076             :  * \endcode
    1077             :  *
    1078             :  * @param pszProjectDir the directory relative to which the secondary files
    1079             :  * path should be interpreted.
    1080             :  * @param pszSecondaryFilename the filename (potentially with path) that
    1081             :  * is to be interpreted relative to the project directory.
    1082             :  *
    1083             :  * @return a composed path to the secondary file.  The returned string is
    1084             :  * internal and should not be altered, freed, or depending on past the next
    1085             :  * CPL call.
    1086             :  *
    1087             :  * @deprecated If using C++, prefer using CPLProjectRelativeFilenameSafe() instead
    1088             :  */
    1089             : 
    1090           0 : const char *CPLProjectRelativeFilename(const char *pszProjectDir,
    1091             :                                        const char *pszSecondaryFilename)
    1092             : 
    1093             : {
    1094           0 :     return CPLPathReturnTLSString(
    1095           0 :         CPLProjectRelativeFilenameSafe(pszProjectDir, pszSecondaryFilename),
    1096           0 :         __FUNCTION__);
    1097             : }
    1098             : 
    1099             : /************************************************************************/
    1100             : /*                       CPLIsFilenameRelative()                        */
    1101             : /************************************************************************/
    1102             : 
    1103             : /**
    1104             :  * Is filename relative or absolute?
    1105             :  *
    1106             :  * The test is filesystem convention agnostic.  That is it will test for
    1107             :  * Unix style and windows style path conventions regardless of the actual
    1108             :  * system in use.
    1109             :  *
    1110             :  * @param pszFilename the filename with path to test.
    1111             :  *
    1112             :  * @return TRUE if the filename is relative or FALSE if it is absolute.
    1113             :  */
    1114             : 
    1115      402013 : int CPLIsFilenameRelative(const char *pszFilename)
    1116             : 
    1117             : {
    1118      402013 :     if ((pszFilename[0] != '\0' &&
    1119      390905 :          (STARTS_WITH(pszFilename + 1, ":\\") ||
    1120      390846 :           STARTS_WITH(pszFilename + 1, ":/") ||
    1121      390856 :           strstr(pszFilename + 1, "://")  // http://, ftp:// etc....
    1122      401337 :           )) ||
    1123      401337 :         STARTS_WITH(pszFilename, "\\\\?\\")  // Windows extended Length Path.
    1124      401300 :         || pszFilename[0] == '\\' || pszFilename[0] == '/')
    1125      276655 :         return FALSE;
    1126             : 
    1127      125358 :     return TRUE;
    1128             : }
    1129             : 
    1130             : /************************************************************************/
    1131             : /*                       CPLExtractRelativePath()                       */
    1132             : /************************************************************************/
    1133             : 
    1134             : /**
    1135             :  * Get relative path from directory to target file.
    1136             :  *
    1137             :  * Computes a relative path for pszTarget relative to pszBaseDir.
    1138             :  * Currently this only works if they share a common base path.  The returned
    1139             :  * path is normally into the pszTarget string.  It should only be considered
    1140             :  * valid as long as pszTarget is valid or till the next call to
    1141             :  * this function, whichever comes first.
    1142             :  *
    1143             :  * @param pszBaseDir the name of the directory relative to which the path
    1144             :  * should be computed.  pszBaseDir may be NULL in which case the original
    1145             :  * target is returned without relativizing.
    1146             :  *
    1147             :  * @param pszTarget the filename to be changed to be relative to pszBaseDir.
    1148             :  *
    1149             :  * @param pbGotRelative Pointer to location in which a flag is placed
    1150             :  * indicating that the returned path is relative to the basename (TRUE) or
    1151             :  * not (FALSE).  This pointer may be NULL if flag is not desired.
    1152             :  *
    1153             :  * @return an adjusted path or the original if it could not be made relative
    1154             :  * to the pszBaseFile's path.
    1155             :  **/
    1156             : 
    1157        2712 : const char *CPLExtractRelativePath(const char *pszBaseDir,
    1158             :                                    const char *pszTarget, int *pbGotRelative)
    1159             : 
    1160             : {
    1161             :     /* -------------------------------------------------------------------- */
    1162             :     /*      If we don't have a basedir, then we can't relativize the path.  */
    1163             :     /* -------------------------------------------------------------------- */
    1164        2712 :     if (pszBaseDir == nullptr)
    1165             :     {
    1166           0 :         if (pbGotRelative != nullptr)
    1167           0 :             *pbGotRelative = FALSE;
    1168             : 
    1169           0 :         return pszTarget;
    1170             :     }
    1171             : 
    1172        2712 :     const size_t nBasePathLen = strlen(pszBaseDir);
    1173             : 
    1174             :     /* -------------------------------------------------------------------- */
    1175             :     /*      One simple case is when the base dir is '.' and the target      */
    1176             :     /*      filename is relative.                                           */
    1177             :     /* -------------------------------------------------------------------- */
    1178        2734 :     if ((nBasePathLen == 0 || EQUAL(pszBaseDir, ".")) &&
    1179          22 :         CPLIsFilenameRelative(pszTarget))
    1180             :     {
    1181          22 :         if (pbGotRelative != nullptr)
    1182          22 :             *pbGotRelative = TRUE;
    1183             : 
    1184          22 :         return pszTarget;
    1185             :     }
    1186             : 
    1187             :     /* -------------------------------------------------------------------- */
    1188             :     /*      By this point, if we don't have a base path, we can't have a    */
    1189             :     /*      meaningful common prefix.                                       */
    1190             :     /* -------------------------------------------------------------------- */
    1191        2690 :     if (nBasePathLen == 0)
    1192             :     {
    1193           0 :         if (pbGotRelative != nullptr)
    1194           0 :             *pbGotRelative = FALSE;
    1195             : 
    1196           0 :         return pszTarget;
    1197             :     }
    1198             : 
    1199             :     /* -------------------------------------------------------------------- */
    1200             :     /*      If we don't have a common path prefix, then we can't get a      */
    1201             :     /*      relative path.                                                  */
    1202             :     /* -------------------------------------------------------------------- */
    1203        2690 :     if (!EQUALN(pszBaseDir, pszTarget, nBasePathLen) ||
    1204        2248 :         (pszTarget[nBasePathLen] != '\\' && pszTarget[nBasePathLen] != '/'))
    1205             :     {
    1206         443 :         if (pbGotRelative != nullptr)
    1207         443 :             *pbGotRelative = FALSE;
    1208             : 
    1209         443 :         return pszTarget;
    1210             :     }
    1211             : 
    1212             :     /* -------------------------------------------------------------------- */
    1213             :     /*      We have a relative path.  Strip it off to get a string to       */
    1214             :     /*      return.                                                         */
    1215             :     /* -------------------------------------------------------------------- */
    1216        2247 :     if (pbGotRelative != nullptr)
    1217        2178 :         *pbGotRelative = TRUE;
    1218             : 
    1219        2247 :     return pszTarget + nBasePathLen + 1;
    1220             : }
    1221             : 
    1222             : /************************************************************************/
    1223             : /*                      CPLCleanTrailingSlashSafe()                     */
    1224             : /************************************************************************/
    1225             : 
    1226             : /**
    1227             :  * Remove trailing forward/backward slash from the path for UNIX/Windows resp.
    1228             :  *
    1229             :  * Returns a string containing the portion of the passed path string with
    1230             :  * trailing slash removed. If there is no path in the passed filename
    1231             :  * an empty string will be returned (not NULL).
    1232             :  *
    1233             :  * \code{.cpp}
    1234             :  * CPLCleanTrailingSlashSafe( "abc/def/" ) == "abc/def"
    1235             :  * CPLCleanTrailingSlashSafe( "abc/def" ) == "abc/def"
    1236             :  * CPLCleanTrailingSlashSafe( "c:\\abc\\def\\" ) == "c:\\abc\\def"
    1237             :  * CPLCleanTrailingSlashSafe( "c:\\abc\\def" ) == "c:\\abc\\def"
    1238             :  * CPLCleanTrailingSlashSafe( "abc" ) == "abc"
    1239             :  * \endcode
    1240             :  *
    1241             :  * @param pszPath the path to be cleaned up
    1242             :  *
    1243             :  * @return Path
    1244             :  *
    1245             :  * @since 3.11
    1246             :  */
    1247             : 
    1248           9 : std::string CPLCleanTrailingSlashSafe(const char *pszPath)
    1249             : 
    1250             : {
    1251           9 :     std::string osRes(pszPath);
    1252           9 :     if (!osRes.empty() && (osRes.back() == '\\' || osRes.back() == '/'))
    1253           0 :         osRes.pop_back();
    1254           9 :     return osRes;
    1255             : }
    1256             : 
    1257             : /************************************************************************/
    1258             : /*                            CPLCleanTrailingSlash()                   */
    1259             : /************************************************************************/
    1260             : 
    1261             : /**
    1262             :  * Remove trailing forward/backward slash from the path for UNIX/Windows resp.
    1263             :  *
    1264             :  * Returns a string containing the portion of the passed path string with
    1265             :  * trailing slash removed. If there is no path in the passed filename
    1266             :  * an empty string will be returned (not NULL).
    1267             :  *
    1268             :  * \code{.cpp}
    1269             :  * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
    1270             :  * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
    1271             :  * CPLCleanTrailingSlash( "c:\\abc\\def\\" ) == "c:\\abc\\def"
    1272             :  * CPLCleanTrailingSlash( "c:\\abc\\def" ) == "c:\\abc\\def"
    1273             :  * CPLCleanTrailingSlash( "abc" ) == "abc"
    1274             :  * \endcode
    1275             :  *
    1276             :  * @param pszPath the path to be cleaned up
    1277             :  *
    1278             :  * @return Path in an internal string which must not be freed.  The string
    1279             :  * may be destroyed by the next CPL filename handling call.
    1280             :  *
    1281             :  * @deprecated If using C++, prefer using CPLCleanTrailingSlashSafe() instead
    1282             :  */
    1283             : 
    1284           0 : const char *CPLCleanTrailingSlash(const char *pszPath)
    1285             : 
    1286             : {
    1287           0 :     return CPLPathReturnTLSString(CPLCleanTrailingSlashSafe(pszPath),
    1288           0 :                                   __FUNCTION__);
    1289             : }
    1290             : 
    1291             : /************************************************************************/
    1292             : /*                       CPLCorrespondingPaths()                        */
    1293             : /************************************************************************/
    1294             : 
    1295             : /**
    1296             :  * Identify corresponding paths.
    1297             :  *
    1298             :  * Given a prototype old and new filename this function will attempt
    1299             :  * to determine corresponding names for a set of other old filenames that
    1300             :  * will rename them in a similar manner.  This correspondence assumes there
    1301             :  * are two possibly kinds of renaming going on.  A change of path, and a
    1302             :  * change of filename stem.
    1303             :  *
    1304             :  * If a consistent renaming cannot be established for all the files this
    1305             :  * function will return indicating an error.
    1306             :  *
    1307             :  * The returned file list becomes owned by the caller and should be destroyed
    1308             :  * with CSLDestroy().
    1309             :  *
    1310             :  * @param pszOldFilename path to old prototype file.
    1311             :  * @param pszNewFilename path to new prototype file.
    1312             :  * @param papszFileList list of other files associated with pszOldFilename to
    1313             :  * rename similarly.
    1314             :  *
    1315             :  * @return a list of files corresponding to papszFileList but renamed to
    1316             :  * correspond to pszNewFilename.
    1317             :  */
    1318             : 
    1319         179 : char **CPLCorrespondingPaths(const char *pszOldFilename,
    1320             :                              const char *pszNewFilename, char **papszFileList)
    1321             : 
    1322             : {
    1323         179 :     if (CSLCount(papszFileList) == 0)
    1324           0 :         return nullptr;
    1325             : 
    1326             :     /* -------------------------------------------------------------------- */
    1327             :     /*      There is a special case for a one item list which exactly       */
    1328             :     /*      matches the old name, to rename to the new name.                */
    1329             :     /* -------------------------------------------------------------------- */
    1330         347 :     if (CSLCount(papszFileList) == 1 &&
    1331         168 :         strcmp(pszOldFilename, papszFileList[0]) == 0)
    1332             :     {
    1333         168 :         return CSLAddString(nullptr, pszNewFilename);
    1334             :     }
    1335             : 
    1336          22 :     const std::string osOldPath = CPLGetPathSafe(pszOldFilename);
    1337          22 :     const std::string osOldBasename = CPLGetBasenameSafe(pszOldFilename);
    1338          22 :     const std::string osNewBasename = CPLGetBasenameSafe(pszNewFilename);
    1339             : 
    1340             :     /* -------------------------------------------------------------------- */
    1341             :     /*      If the basename is changing, verify that all source files       */
    1342             :     /*      have the same starting basename.                                */
    1343             :     /* -------------------------------------------------------------------- */
    1344          11 :     if (osOldBasename != osNewBasename)
    1345             :     {
    1346          34 :         for (int i = 0; papszFileList[i] != nullptr; i++)
    1347             :         {
    1348          24 :             if (osOldBasename == CPLGetBasenameSafe(papszFileList[i]))
    1349          16 :                 continue;
    1350             : 
    1351           8 :             const std::string osFilePath = CPLGetPathSafe(papszFileList[i]);
    1352           8 :             const std::string osFileName = CPLGetFilename(papszFileList[i]);
    1353             : 
    1354           8 :             if (!EQUALN(osFileName.c_str(), osOldBasename.c_str(),
    1355           8 :                         osOldBasename.size()) ||
    1356          16 :                 !EQUAL(osFilePath.c_str(), osOldPath.c_str()) ||
    1357           8 :                 osFileName[osOldBasename.size()] != '.')
    1358             :             {
    1359           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1360             :                          "Unable to rename fileset due irregular basenames.");
    1361           0 :                 return nullptr;
    1362             :             }
    1363             :         }
    1364             :     }
    1365             : 
    1366             :     /* -------------------------------------------------------------------- */
    1367             :     /*      If the filename portions differs, ensure they only differ in    */
    1368             :     /*      basename.                                                       */
    1369             :     /* -------------------------------------------------------------------- */
    1370          11 :     if (osOldBasename != osNewBasename)
    1371             :     {
    1372             :         const std::string osOldExtra =
    1373          10 :             CPLGetFilename(pszOldFilename) + osOldBasename.size();
    1374             :         const std::string osNewExtra =
    1375          10 :             CPLGetFilename(pszNewFilename) + osNewBasename.size();
    1376             : 
    1377          10 :         if (osOldExtra != osNewExtra)
    1378             :         {
    1379           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1380             :                      "Unable to rename fileset due to irregular filename "
    1381             :                      "correspondence.");
    1382           0 :             return nullptr;
    1383             :         }
    1384             :     }
    1385             : 
    1386             :     /* -------------------------------------------------------------------- */
    1387             :     /*      Generate the new filenames.                                     */
    1388             :     /* -------------------------------------------------------------------- */
    1389          11 :     char **papszNewList = nullptr;
    1390          11 :     const std::string osNewPath = CPLGetPathSafe(pszNewFilename);
    1391             : 
    1392          37 :     for (int i = 0; papszFileList[i] != nullptr; i++)
    1393             :     {
    1394          52 :         const std::string osOldFilename = CPLGetFilename(papszFileList[i]);
    1395             : 
    1396             :         const std::string osNewFilename =
    1397          26 :             osOldBasename == osNewBasename
    1398             :                 ? CPLFormFilenameSafe(osNewPath.c_str(), osOldFilename.c_str(),
    1399             :                                       nullptr)
    1400             :                 : CPLFormFilenameSafe(osNewPath.c_str(), osNewBasename.c_str(),
    1401          24 :                                       osOldFilename.c_str() +
    1402          50 :                                           osOldBasename.size());
    1403             : 
    1404          26 :         papszNewList = CSLAddString(papszNewList, osNewFilename.c_str());
    1405             :     }
    1406             : 
    1407          11 :     return papszNewList;
    1408             : }
    1409             : 
    1410             : /************************************************************************/
    1411             : /*                   CPLGenerateTempFilenameSafe()                      */
    1412             : /************************************************************************/
    1413             : 
    1414             : /**
    1415             :  * Generate temporary file name.
    1416             :  *
    1417             :  * Returns a filename that may be used for a temporary file.  The location
    1418             :  * of the file tries to follow operating system semantics but may be
    1419             :  * forced via the CPL_TMPDIR configuration option.
    1420             :  *
    1421             :  * @param pszStem if non-NULL this will be part of the filename.
    1422             :  *
    1423             :  * @return a filename
    1424             :  *
    1425             :  * @since 3.11
    1426             :  */
    1427             : 
    1428        2342 : std::string CPLGenerateTempFilenameSafe(const char *pszStem)
    1429             : 
    1430             : {
    1431        2342 :     const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
    1432             : 
    1433        2342 :     if (pszDir == nullptr)
    1434        2340 :         pszDir = CPLGetConfigOption("TMPDIR", nullptr);
    1435             : 
    1436        2342 :     if (pszDir == nullptr)
    1437        2340 :         pszDir = CPLGetConfigOption("TEMP", nullptr);
    1438             : 
    1439        2342 :     if (pszDir == nullptr)
    1440        2340 :         pszDir = ".";
    1441             : 
    1442        2342 :     if (pszStem == nullptr)
    1443        2173 :         pszStem = "";
    1444             : 
    1445             :     static int nTempFileCounter = 0;
    1446        4684 :     CPLString osFilename;
    1447             :     osFilename.Printf("%s_%d_%d", pszStem, CPLGetCurrentProcessID(),
    1448        2342 :                       CPLAtomicInc(&nTempFileCounter));
    1449             : 
    1450        4684 :     return CPLFormFilenameSafe(pszDir, osFilename.c_str(), nullptr);
    1451             : }
    1452             : 
    1453             : /************************************************************************/
    1454             : /*                      CPLGenerateTempFilename()                       */
    1455             : /************************************************************************/
    1456             : 
    1457             : /**
    1458             :  * Generate temporary file name.
    1459             :  *
    1460             :  * Returns a filename that may be used for a temporary file.  The location
    1461             :  * of the file tries to follow operating system semantics but may be
    1462             :  * forced via the CPL_TMPDIR configuration option.
    1463             :  *
    1464             :  * @param pszStem if non-NULL this will be part of the filename.
    1465             :  *
    1466             :  * @return a filename which is valid till the next CPL call in this thread.
    1467             :  *
    1468             :  * @deprecated If using C++, prefer using CPLCleanTrailingSlashSafe() instead
    1469             :  */
    1470             : 
    1471           6 : const char *CPLGenerateTempFilename(const char *pszStem)
    1472             : 
    1473             : {
    1474          12 :     return CPLPathReturnTLSString(CPLGenerateTempFilenameSafe(pszStem),
    1475          12 :                                   __FUNCTION__);
    1476             : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*                        CPLExpandTildeSafe()                          */
    1480             : /************************************************************************/
    1481             : 
    1482             : /**
    1483             :  * Expands ~/ at start of filename.
    1484             :  *
    1485             :  * Assumes that the HOME configuration option is defined.
    1486             :  *
    1487             :  * @param pszFilename filename potentially starting with ~/
    1488             :  *
    1489             :  * @return an expanded filename.
    1490             :  *
    1491             :  * @since GDAL 3.11
    1492             :  */
    1493             : 
    1494         189 : std::string CPLExpandTildeSafe(const char *pszFilename)
    1495             : 
    1496             : {
    1497         189 :     if (!STARTS_WITH_CI(pszFilename, "~/"))
    1498         188 :         return pszFilename;
    1499             : 
    1500           1 :     const char *pszHome = CPLGetConfigOption("HOME", nullptr);
    1501           1 :     if (pszHome == nullptr)
    1502           0 :         return pszFilename;
    1503             : 
    1504           1 :     return CPLFormFilenameSafe(pszHome, pszFilename + 2, nullptr);
    1505             : }
    1506             : 
    1507             : /************************************************************************/
    1508             : /*                         CPLExpandTilde()                             */
    1509             : /************************************************************************/
    1510             : 
    1511             : /**
    1512             :  * Expands ~/ at start of filename.
    1513             :  *
    1514             :  * Assumes that the HOME configuration option is defined.
    1515             :  *
    1516             :  * @param pszFilename filename potentially starting with ~/
    1517             :  *
    1518             :  * @return an expanded filename.
    1519             :  *
    1520             :  * @since GDAL 2.2
    1521             :  *
    1522             :  * @deprecated If using C++, prefer using CPLExpandTildeSafe() instead
    1523             :  */
    1524             : 
    1525           2 : const char *CPLExpandTilde(const char *pszFilename)
    1526             : 
    1527             : {
    1528           4 :     return CPLPathReturnTLSString(CPLExpandTildeSafe(pszFilename),
    1529           4 :                                   __FUNCTION__);
    1530             : }
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                         CPLGetHomeDir()                              */
    1534             : /************************************************************************/
    1535             : 
    1536             : /**
    1537             :  * Return the path to the home directory
    1538             :  *
    1539             :  * That is the value of the USERPROFILE environment variable on Windows,
    1540             :  * or HOME on other platforms.
    1541             :  *
    1542             :  * @return the home directory, or NULL.
    1543             :  *
    1544             :  * @since GDAL 2.3
    1545             :  */
    1546             : 
    1547           0 : const char *CPLGetHomeDir()
    1548             : 
    1549             : {
    1550             : #ifdef _WIN32
    1551             :     return CPLGetConfigOption("USERPROFILE", nullptr);
    1552             : #else
    1553           0 :     return CPLGetConfigOption("HOME", nullptr);
    1554             : #endif
    1555             : }
    1556             : 
    1557             : /************************************************************************/
    1558             : /*                      CPLLaunderForFilenameSafe()                     */
    1559             : /************************************************************************/
    1560             : 
    1561             : /**
    1562             :  * Launder a string to be compatible of a filename.
    1563             :  *
    1564             :  * @param pszName The input string to launder.
    1565             :  * @param pszOutputPath The directory where the file would be created.
    1566             :  *                      Unused for now. May be NULL.
    1567             :  * @return the laundered name.
    1568             :  *
    1569             :  * @since GDAL 3.11
    1570             :  */
    1571             : 
    1572        1162 : std::string CPLLaunderForFilenameSafe(const char *pszName,
    1573             :                                       CPL_UNUSED const char *pszOutputPath)
    1574             : {
    1575        1162 :     std::string osRet(pszName);
    1576       10899 :     for (char &ch : osRet)
    1577             :     {
    1578             :         // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
    1579        9737 :         if (ch == '<' || ch == '>' || ch == ':' || ch == '"' || ch == '/' ||
    1580        9731 :             ch == '\\' || ch == '?' || ch == '*')
    1581             :         {
    1582           9 :             ch = '_';
    1583             :         }
    1584             :     }
    1585        1162 :     return osRet;
    1586             : }
    1587             : 
    1588             : /************************************************************************/
    1589             : /*                        CPLLaunderForFilename()                       */
    1590             : /************************************************************************/
    1591             : 
    1592             : /**
    1593             :  * Launder a string to be compatible of a filename.
    1594             :  *
    1595             :  * @param pszName The input string to launder.
    1596             :  * @param pszOutputPath The directory where the file would be created.
    1597             :  *                      Unused for now. May be NULL.
    1598             :  * @return the laundered name.
    1599             :  *
    1600             :  * @since GDAL 3.1
    1601             :  *
    1602             :  * @deprecated If using C++, prefer using CPLLaunderForFilenameSafe() instead
    1603             :  */
    1604             : 
    1605           0 : const char *CPLLaunderForFilename(const char *pszName,
    1606             :                                   const char *pszOutputPath)
    1607             : {
    1608           0 :     return CPLPathReturnTLSString(
    1609           0 :         CPLLaunderForFilenameSafe(pszName, pszOutputPath), __FUNCTION__);
    1610             : }

Generated by: LCOV version 1.14