LCOV - code coverage report
Current view: top level - port - cpl_path.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 361 405 89.1 %
Date: 2025-01-18 12:42:00 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     2183420 : static int CPLFindFilenameStart(const char *pszFilename, size_t nStart = 0)
     112             : 
     113             : {
     114     2183420 :     size_t iFileStart = nStart ? nStart : strlen(pszFilename);
     115             : 
     116    32607800 :     for (; iFileStart > 0 && pszFilename[iFileStart - 1] != '/' &&
     117    30424800 :            pszFilename[iFileStart - 1] != '\\';
     118             :          iFileStart--)
     119             :     {
     120             :     }
     121             : 
     122     2183420 :     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      211734 : std::string CPLGetPathSafe(const char *pszFilename)
     152             : 
     153             : {
     154      211734 :     size_t nSuffixPos = 0;
     155      211734 :     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      211722 :     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      211733 :     const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
     195      211733 :     if (iFileStart == 0)
     196             :     {
     197         556 :         return std::string();
     198             :     }
     199             : 
     200      422354 :     std::string osRet(pszFilename, iFileStart);
     201             : 
     202      211176 :     if (iFileStart > 1 && (osRet.back() == '/' || osRet.back() == '\\'))
     203      210837 :         osRet.pop_back();
     204             : 
     205      211177 :     if (nSuffixPos)
     206             :     {
     207           1 :         osRet += (pszFilename + nSuffixPos);
     208             :     }
     209             : 
     210      211177 :     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      143203 : std::string CPLGetDirnameSafe(const char *pszFilename)
     274             : 
     275             : {
     276      143203 :     size_t nSuffixPos = 0;
     277      143203 :     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      143054 :     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      143189 :     const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
     317      143194 :     if (iFileStart == 0)
     318             :     {
     319          64 :         return std::string(".");
     320             :     }
     321             : 
     322      286248 :     std::string osRet(pszFilename, iFileStart);
     323             : 
     324      143098 :     if (iFileStart > 1 && (osRet.back() == '/' || osRet.back() == '\\'))
     325      143065 :         osRet.pop_back();
     326             : 
     327      143131 :     if (nSuffixPos)
     328             :     {
     329           1 :         osRet += (pszFilename + nSuffixPos);
     330             :     }
     331             : 
     332      143131 :     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      774815 : const char *CPLGetFilename(const char *pszFullFilename)
     391             : 
     392             : {
     393      774815 :     const int iFileStart = CPLFindFilenameStart(pszFullFilename);
     394             : 
     395      774815 :     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      439768 : std::string CPLGetBasenameSafe(const char *pszFullFilename)
     423             : 
     424             : {
     425             :     const size_t iFileStart =
     426      439768 :         static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
     427             : 
     428      439768 :     size_t iExtStart = strlen(pszFullFilename);
     429     2489810 :     for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
     430             :          iExtStart--)
     431             :     {
     432             :     }
     433             : 
     434      439768 :     if (iExtStart == iFileStart)
     435       26911 :         iExtStart = strlen(pszFullFilename);
     436             : 
     437      439768 :     const size_t nLength = iExtStart - iFileStart;
     438      439768 :     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      617510 : std::string CPLGetExtensionSafe(const char *pszFullFilename)
     498             : 
     499             : {
     500      617510 :     if (pszFullFilename[0] == '\0')
     501        3556 :         return std::string();
     502             : 
     503             :     size_t iFileStart =
     504      613954 :         static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
     505      613955 :     size_t iExtStart = strlen(pszFullFilename);
     506     4457230 :     for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
     507             :          iExtStart--)
     508             :     {
     509             :     }
     510             : 
     511      613955 :     if (iExtStart == iFileStart)
     512      105794 :         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      613955 :     const size_t knMaxExtensionSize = 10;
     517      613955 :     if (strlen(pszFullFilename + iExtStart + 1) > knMaxExtensionSize)
     518        2069 :         return "";
     519             : 
     520      611886 :     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        5107 : char *CPLGetCurrentDir()
     584             : {
     585             : #if PATH_MAX
     586        5107 :     const size_t nPathMax = PATH_MAX;
     587             : #else
     588             :     const size_t nPathMax = 8192;
     589             : #endif
     590             : 
     591        5107 :     char *pszDirPath = static_cast<char *>(VSI_MALLOC_VERBOSE(nPathMax));
     592        5107 :     if (!pszDirPath)
     593           0 :         return nullptr;
     594             : 
     595        5107 :     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      162661 : std::string CPLResetExtensionSafe(const char *pszPath, const char *pszExt)
     620             : 
     621             : {
     622      162661 :     std::string osRet(pszPath);
     623             : 
     624             :     /* -------------------------------------------------------------------- */
     625             :     /*      First, try and strip off any existing extension.                */
     626             :     /* -------------------------------------------------------------------- */
     627             : 
     628      878581 :     for (size_t i = osRet.size(); i > 0;)
     629             :     {
     630      878306 :         --i;
     631      878306 :         if (osRet[i] == '.')
     632             :         {
     633      146116 :             osRet.resize(i);
     634      146115 :             break;
     635             :         }
     636      732190 :         else if (osRet[i] == '/' || osRet[i] == '\\' || osRet[i] == ':')
     637             :         {
     638       16270 :             break;
     639             :         }
     640             :     }
     641             : 
     642             :     /* -------------------------------------------------------------------- */
     643             :     /*      Append the new extension.                                       */
     644             :     /* -------------------------------------------------------------------- */
     645      162659 :     osRet += '.';
     646      162660 :     osRet += pszExt;
     647             : 
     648      162660 :     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      380110 : std::string CPLFormFilenameSafe(const char *pszPath, const char *pszBasename,
     709             :                                 const char *pszExtension)
     710             : 
     711             : {
     712      380110 :     if (pszBasename[0] == '.' &&
     713        9286 :         (pszBasename[1] == '/' || pszBasename[1] == '\\'))
     714          62 :         pszBasename += 2;
     715             : 
     716      380110 :     const char *pszAddedPathSep = "";
     717      380110 :     const char *pszAddedExtSep = "";
     718             : 
     719      380110 :     if (pszPath == nullptr)
     720       10901 :         pszPath = "";
     721      380110 :     size_t nLenPath = strlen(pszPath);
     722             : 
     723      380110 :     size_t nSuffixPos = 0;
     724      380110 :     if (STARTS_WITH_CI(pszPath, "/vsicurl/http"))
     725             :     {
     726         170 :         const char *pszQuestionMark = strchr(pszPath, '?');
     727         170 :         if (pszQuestionMark)
     728             :         {
     729           1 :             nSuffixPos = static_cast<size_t>(pszQuestionMark - pszPath);
     730           1 :             nLenPath = nSuffixPos;
     731             :         }
     732         170 :         pszAddedPathSep = "/";
     733             :     }
     734             : 
     735      646162 :     if (!CPLIsFilenameRelative(pszPath) && pszBasename[0] == '.' &&
     736      646486 :         pszBasename[1] == '.' &&
     737         369 :         (pszBasename[2] == 0 || pszBasename[2] == '\\' ||
     738         118 :          pszBasename[2] == '/'))
     739             :     {
     740             :         // "/a/b/" + "..[/something]" --> "/a[/something]"
     741             :         // "/a/b" + "..[/something]" --> "/a[/something]"
     742         369 :         if (pszPath[nLenPath - 1] == '\\' || pszPath[nLenPath - 1] == '/')
     743          14 :             nLenPath--;
     744             :         while (true)
     745             :         {
     746         420 :             const char *pszBasenameOri = pszBasename;
     747         420 :             const size_t nLenPathOri = nLenPath;
     748        5098 :             while (nLenPath > 0 && pszPath[nLenPath - 1] != '\\' &&
     749        5087 :                    pszPath[nLenPath - 1] != '/')
     750             :             {
     751        4678 :                 nLenPath--;
     752             :             }
     753         420 :             if (nLenPath == 1 && pszPath[0] == '/')
     754             :             {
     755          18 :                 pszBasename += 2;
     756          18 :                 if (pszBasename[0] == '/' || pszBasename[0] == '\\')
     757          10 :                     pszBasename++;
     758          18 :                 if (*pszBasename == '.')
     759             :                 {
     760           1 :                     pszBasename = pszBasenameOri;
     761           1 :                     nLenPath = nLenPathOri;
     762           1 :                     if (pszAddedPathSep[0] == 0)
     763           1 :                         pszAddedPathSep =
     764           1 :                             pszPath[0] == '/'
     765           1 :                                 ? "/"
     766           0 :                                 : VSIGetDirectorySeparator(pszPath);
     767             :                 }
     768          18 :                 break;
     769             :             }
     770         402 :             else if ((nLenPath > 1 && pszPath[0] == '/') ||
     771          11 :                      (nLenPath > 2 && pszPath[1] == ':') ||
     772           1 :                      (nLenPath > 6 && strncmp(pszPath, "\\\\$\\", 4) == 0))
     773             :             {
     774         395 :                 nLenPath--;
     775         395 :                 pszBasename += 2;
     776         395 :                 if ((pszBasename[0] == '/' || pszBasename[0] == '\\') &&
     777         158 :                     pszBasename[1] == '.' && pszBasename[2] == '.')
     778             :                 {
     779          51 :                     pszBasename++;
     780             :                 }
     781             :                 else
     782             :                 {
     783             :                     break;
     784             :                 }
     785             :             }
     786             :             else
     787             :             {
     788             :                 // cppcheck-suppress redundantAssignment
     789           7 :                 pszBasename = pszBasenameOri;
     790           7 :                 nLenPath = nLenPathOri;
     791           7 :                 if (pszAddedPathSep[0] == 0)
     792           7 :                     pszAddedPathSep = pszPath[0] == '/'
     793           7 :                                           ? "/"
     794           2 :                                           : VSIGetDirectorySeparator(pszPath);
     795           7 :                 break;
     796             :             }
     797          51 :         }
     798             :     }
     799      379696 :     else if (nLenPath > 0 && pszPath[nLenPath - 1] != '/' &&
     800      366824 :              pszPath[nLenPath - 1] != '\\')
     801             :     {
     802      366787 :         if (pszAddedPathSep[0] == 0)
     803      366634 :             pszAddedPathSep = VSIGetDirectorySeparator(pszPath);
     804             :     }
     805             : 
     806      380075 :     if (pszExtension == nullptr)
     807      225231 :         pszExtension = "";
     808      154844 :     else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
     809      141584 :         pszAddedExtSep = ".";
     810             : 
     811      380075 :     std::string osRes;
     812      380101 :     osRes.reserve(nLenPath + strlen(pszAddedPathSep) + strlen(pszBasename) +
     813      380101 :                   strlen(pszAddedExtSep) + strlen(pszExtension) +
     814      380101 :                   (nSuffixPos ? strlen(pszPath + nSuffixPos) : 0));
     815      380084 :     osRes.assign(pszPath, nLenPath);
     816      380091 :     osRes += pszAddedPathSep;
     817      380044 :     osRes += pszBasename;
     818      380028 :     osRes += pszAddedExtSep;
     819      380032 :     osRes += pszExtension;
     820             : 
     821      380038 :     if (nSuffixPos)
     822             :     {
     823           1 :         osRes += (pszPath + nSuffixPos);
     824             :     }
     825             : 
     826      380071 :     return osRes;
     827             : }
     828             : 
     829             : /************************************************************************/
     830             : /*                          CPLFormFilename()                           */
     831             : /************************************************************************/
     832             : 
     833             : /**
     834             :  * Build a full file path from a passed path, file basename and extension.
     835             :  *
     836             :  * The path, and extension are optional.  The basename may in fact contain
     837             :  * an extension if desired.
     838             :  *
     839             :  * \code{.cpp}
     840             :  * CPLFormFilename("abc/xyz", "def", ".dat" ) == "abc/xyz/def.dat"
     841             :  * CPLFormFilename(NULL,"def", NULL ) == "def"
     842             :  * CPLFormFilename(NULL, "abc/def.dat", NULL ) == "abc/def.dat"
     843             :  * CPLFormFilename("/abc/xyz/", "def.dat", NULL ) == "/abc/xyz/def.dat"
     844             :  * CPLFormFilename("/a/b/c", "../d", NULL ) == "/a/b/d" (since 3.10.1)
     845             :  * \endcode
     846             :  *
     847             :  * @param pszPath directory path to the directory containing the file.  This
     848             :  * may be relative or absolute, and may have a trailing path separator or
     849             :  * not.  May be NULL.
     850             :  *
     851             :  * @param pszBasename file basename.  May optionally have path and/or
     852             :  * extension.  Must *NOT* be NULL.
     853             :  *
     854             :  * @param pszExtension file extension, optionally including the period.  May
     855             :  * be NULL.
     856             :  *
     857             :  * @return a fully formed filename in an internal static string.  Do not
     858             :  * modify or free the returned string.  The string may be destroyed by the
     859             :  * next CPL call.
     860             :  *
     861             :  * @deprecated If using C++, prefer using CPLFormFilenameSafe() instead
     862             :  */
     863         116 : const char *CPLFormFilename(const char *pszPath, const char *pszBasename,
     864             :                             const char *pszExtension)
     865             : 
     866             : {
     867         116 :     return CPLPathReturnTLSString(
     868         232 :         CPLFormFilenameSafe(pszPath, pszBasename, pszExtension), __FUNCTION__);
     869             : }
     870             : 
     871             : /************************************************************************/
     872             : /*                       CPLFormCIFilenameSafe()                        */
     873             : /************************************************************************/
     874             : 
     875             : /**
     876             :  * Case insensitive file searching, returning full path.
     877             :  *
     878             :  * This function tries to return the path to a file regardless of
     879             :  * whether the file exactly matches the basename, and extension case, or
     880             :  * is all upper case, or all lower case.  The path is treated as case
     881             :  * sensitive.  This function is equivalent to CPLFormFilename() on
     882             :  * case insensitive file systems (like Windows).
     883             :  *
     884             :  * @param pszPath directory path to the directory containing the file.  This
     885             :  * may be relative or absolute, and may have a trailing path separator or
     886             :  * not.  May be NULL.
     887             :  *
     888             :  * @param pszBasename file basename.  May optionally have path and/or
     889             :  * extension.  May not be NULL.
     890             :  *
     891             :  * @param pszExtension file extension, optionally including the period.  May
     892             :  * be NULL.
     893             :  *
     894             :  * @return a fully formed filename.
     895             :  *
     896             :  * @since 3.11
     897             :  */
     898             : 
     899        4658 : std::string CPLFormCIFilenameSafe(const char *pszPath, const char *pszBasename,
     900             :                                   const char *pszExtension)
     901             : 
     902             : {
     903             :     // On case insensitive filesystems, just default to CPLFormFilename().
     904        4658 :     if (!VSIIsCaseSensitiveFS(pszPath))
     905           0 :         return CPLFormFilenameSafe(pszPath, pszBasename, pszExtension);
     906             : 
     907        4658 :     const char *pszAddedExtSep = "";
     908        4658 :     size_t nLen = strlen(pszBasename) + 2;
     909             : 
     910        4658 :     if (pszExtension != nullptr)
     911        2200 :         nLen += strlen(pszExtension);
     912             : 
     913        4658 :     char *pszFilename = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
     914        4658 :     if (pszFilename == nullptr)
     915           0 :         return "";
     916             : 
     917        4658 :     if (pszExtension == nullptr)
     918        2458 :         pszExtension = "";
     919        2200 :     else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
     920        2148 :         pszAddedExtSep = ".";
     921             : 
     922        4658 :     snprintf(pszFilename, nLen, "%s%s%s", pszBasename, pszAddedExtSep,
     923             :              pszExtension);
     924             : 
     925        9316 :     std::string osRet = CPLFormFilenameSafe(pszPath, pszFilename, nullptr);
     926             :     VSIStatBufL sStatBuf;
     927        4658 :     int nStatRet = VSIStatExL(osRet.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG);
     928             : 
     929        4658 :     if (nStatRet != 0)
     930             :     {
     931       55675 :         for (size_t i = 0; pszFilename[i] != '\0'; i++)
     932             :         {
     933       51503 :             pszFilename[i] = static_cast<char>(CPLToupper(pszFilename[i]));
     934             :         }
     935             : 
     936             :         std::string osTmpPath(
     937        8344 :             CPLFormFilenameSafe(pszPath, pszFilename, nullptr));
     938             :         nStatRet =
     939        4172 :             VSIStatExL(osTmpPath.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG);
     940        4172 :         if (nStatRet == 0)
     941           3 :             osRet = std::move(osTmpPath);
     942             :     }
     943             : 
     944        4658 :     if (nStatRet != 0)
     945             :     {
     946       55651 :         for (size_t i = 0; pszFilename[i] != '\0'; i++)
     947             :         {
     948       51482 :             pszFilename[i] = static_cast<char>(
     949       51482 :                 CPLTolower(static_cast<unsigned char>(pszFilename[i])));
     950             :         }
     951             : 
     952             :         std::string osTmpPath(
     953        8338 :             CPLFormFilenameSafe(pszPath, pszFilename, nullptr));
     954             :         nStatRet =
     955        4169 :             VSIStatExL(osTmpPath.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG);
     956        4169 :         if (nStatRet == 0)
     957           8 :             osRet = std::move(osTmpPath);
     958             :     }
     959             : 
     960        4658 :     if (nStatRet != 0)
     961        4161 :         osRet = CPLFormFilenameSafe(pszPath, pszBasename, pszExtension);
     962             : 
     963        4658 :     CPLFree(pszFilename);
     964             : 
     965        4658 :     return osRet;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                          CPLFormCIFilename()                         */
     970             : /************************************************************************/
     971             : 
     972             : /**
     973             :  * Case insensitive file searching, returning full path.
     974             :  *
     975             :  * This function tries to return the path to a file regardless of
     976             :  * whether the file exactly matches the basename, and extension case, or
     977             :  * is all upper case, or all lower case.  The path is treated as case
     978             :  * sensitive.  This function is equivalent to CPLFormFilename() on
     979             :  * case insensitive file systems (like Windows).
     980             :  *
     981             :  * @param pszPath directory path to the directory containing the file.  This
     982             :  * may be relative or absolute, and may have a trailing path separator or
     983             :  * not.  May be NULL.
     984             :  *
     985             :  * @param pszBasename file basename.  May optionally have path and/or
     986             :  * extension.  May not be NULL.
     987             :  *
     988             :  * @param pszExtension file extension, optionally including the period.  May
     989             :  * be NULL.
     990             :  *
     991             :  * @return a fully formed filename in an internal static string.  Do not
     992             :  * modify or free the returned string.  The string may be destroyed by the
     993             :  * next CPL call.
     994             :  *
     995             :  * @deprecated If using C++, prefer using CPLFormCIFilenameSafe() instead
     996             : */
     997             : 
     998           0 : const char *CPLFormCIFilename(const char *pszPath, const char *pszBasename,
     999             :                               const char *pszExtension)
    1000             : 
    1001             : {
    1002           0 :     return CPLPathReturnTLSString(
    1003           0 :         CPLFormCIFilenameSafe(pszPath, pszBasename, pszExtension),
    1004           0 :         __FUNCTION__);
    1005             : }
    1006             : 
    1007             : /************************************************************************/
    1008             : /*                   CPLProjectRelativeFilenameSafe()                   */
    1009             : /************************************************************************/
    1010             : 
    1011             : /**
    1012             :  * Find a file relative to a project file.
    1013             :  *
    1014             :  * Given the path to a "project" directory, and a path to a secondary file
    1015             :  * referenced from that project, build a path to the secondary file
    1016             :  * that the current application can use.  If the secondary path is already
    1017             :  * absolute, rather than relative, then it will be returned unaltered.
    1018             :  *
    1019             :  * Examples:
    1020             :  * \code{.cpp}
    1021             :  * CPLProjectRelativeFilenameSafe("abc/def", "tmp/abc.gif") == "abc/def/tmp/abc.gif"
    1022             :  * CPLProjectRelativeFilenameSafe("abc/def", "/tmp/abc.gif") == "/tmp/abc.gif"
    1023             :  * CPLProjectRelativeFilenameSafe("/xy", "abc.gif") == "/xy/abc.gif"
    1024             :  * CPLProjectRelativeFilenameSafe("/abc/def", "../abc.gif") == "/abc/def/../abc.gif"
    1025             :  * CPLProjectRelativeFilenameSafe("C:\WIN", "abc.gif") == "C:\WIN\abc.gif"
    1026             :  * \endcode
    1027             :  *
    1028             :  * @param pszProjectDir the directory relative to which the secondary files
    1029             :  * path should be interpreted.
    1030             :  * @param pszSecondaryFilename the filename (potentially with path) that
    1031             :  * is to be interpreted relative to the project directory.
    1032             :  *
    1033             :  * @return a composed path to the secondary file.
    1034             :  *
    1035             :  * @since 3.11
    1036             :  */
    1037             : 
    1038        4293 : std::string CPLProjectRelativeFilenameSafe(const char *pszProjectDir,
    1039             :                                            const char *pszSecondaryFilename)
    1040             : 
    1041             : {
    1042        8341 :     if (pszProjectDir == nullptr || pszProjectDir[0] == 0 ||
    1043        4048 :         !CPLIsFilenameRelative(pszSecondaryFilename))
    1044             :     {
    1045         673 :         return pszSecondaryFilename;
    1046             :     }
    1047             : 
    1048        7240 :     std::string osRes(pszProjectDir);
    1049        3620 :     if (osRes.back() != '/' && osRes.back() != '\\')
    1050             :     {
    1051        3620 :         osRes += VSIGetDirectorySeparator(pszProjectDir);
    1052             :     }
    1053             : 
    1054        3620 :     osRes += pszSecondaryFilename;
    1055        3620 :     return osRes;
    1056             : }
    1057             : 
    1058             : /************************************************************************/
    1059             : /*                     CPLProjectRelativeFilename()                     */
    1060             : /************************************************************************/
    1061             : 
    1062             : /**
    1063             :  * Find a file relative to a project file.
    1064             :  *
    1065             :  * Given the path to a "project" directory, and a path to a secondary file
    1066             :  * referenced from that project, build a path to the secondary file
    1067             :  * that the current application can use.  If the secondary path is already
    1068             :  * absolute, rather than relative, then it will be returned unaltered.
    1069             :  *
    1070             :  * Examples:
    1071             :  * \code{.cpp}
    1072             :  * CPLProjectRelativeFilename("abc/def", "tmp/abc.gif") == "abc/def/tmp/abc.gif"
    1073             :  * CPLProjectRelativeFilename("abc/def", "/tmp/abc.gif") == "/tmp/abc.gif"
    1074             :  * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
    1075             :  * CPLProjectRelativeFilename("/abc/def", "../abc.gif") == "/abc/def/../abc.gif"
    1076             :  * CPLProjectRelativeFilename("C:\WIN", "abc.gif") == "C:\WIN\abc.gif"
    1077             :  * \endcode
    1078             :  *
    1079             :  * @param pszProjectDir the directory relative to which the secondary files
    1080             :  * path should be interpreted.
    1081             :  * @param pszSecondaryFilename the filename (potentially with path) that
    1082             :  * is to be interpreted relative to the project directory.
    1083             :  *
    1084             :  * @return a composed path to the secondary file.  The returned string is
    1085             :  * internal and should not be altered, freed, or depending on past the next
    1086             :  * CPL call.
    1087             :  *
    1088             :  * @deprecated If using C++, prefer using CPLProjectRelativeFilenameSafe() instead
    1089             :  */
    1090             : 
    1091           0 : const char *CPLProjectRelativeFilename(const char *pszProjectDir,
    1092             :                                        const char *pszSecondaryFilename)
    1093             : 
    1094             : {
    1095           0 :     return CPLPathReturnTLSString(
    1096           0 :         CPLProjectRelativeFilenameSafe(pszProjectDir, pszSecondaryFilename),
    1097           0 :         __FUNCTION__);
    1098             : }
    1099             : 
    1100             : /************************************************************************/
    1101             : /*                       CPLIsFilenameRelative()                        */
    1102             : /************************************************************************/
    1103             : 
    1104             : /**
    1105             :  * Is filename relative or absolute?
    1106             :  *
    1107             :  * The test is filesystem convention agnostic.  That is it will test for
    1108             :  * Unix style and windows style path conventions regardless of the actual
    1109             :  * system in use.
    1110             :  *
    1111             :  * @param pszFilename the filename with path to test.
    1112             :  *
    1113             :  * @return TRUE if the filename is relative or FALSE if it is absolute.
    1114             :  */
    1115             : 
    1116      402304 : int CPLIsFilenameRelative(const char *pszFilename)
    1117             : 
    1118             : {
    1119      402304 :     if ((pszFilename[0] != '\0' &&
    1120      391244 :          (STARTS_WITH(pszFilename + 1, ":\\") ||
    1121      391193 :           STARTS_WITH(pszFilename + 1, ":/") ||
    1122      391199 :           strstr(pszFilename + 1, "://")  // http://, ftp:// etc....
    1123      401621 :           )) ||
    1124      401621 :         STARTS_WITH(pszFilename, "\\\\?\\")  // Windows extended Length Path.
    1125      401615 :         || pszFilename[0] == '\\' || pszFilename[0] == '/')
    1126      277270 :         return FALSE;
    1127             : 
    1128      125034 :     return TRUE;
    1129             : }
    1130             : 
    1131             : /************************************************************************/
    1132             : /*                       CPLExtractRelativePath()                       */
    1133             : /************************************************************************/
    1134             : 
    1135             : /**
    1136             :  * Get relative path from directory to target file.
    1137             :  *
    1138             :  * Computes a relative path for pszTarget relative to pszBaseDir.
    1139             :  * Currently this only works if they share a common base path.  The returned
    1140             :  * path is normally into the pszTarget string.  It should only be considered
    1141             :  * valid as long as pszTarget is valid or till the next call to
    1142             :  * this function, whichever comes first.
    1143             :  *
    1144             :  * @param pszBaseDir the name of the directory relative to which the path
    1145             :  * should be computed.  pszBaseDir may be NULL in which case the original
    1146             :  * target is returned without relativizing.
    1147             :  *
    1148             :  * @param pszTarget the filename to be changed to be relative to pszBaseDir.
    1149             :  *
    1150             :  * @param pbGotRelative Pointer to location in which a flag is placed
    1151             :  * indicating that the returned path is relative to the basename (TRUE) or
    1152             :  * not (FALSE).  This pointer may be NULL if flag is not desired.
    1153             :  *
    1154             :  * @return an adjusted path or the original if it could not be made relative
    1155             :  * to the pszBaseFile's path.
    1156             :  **/
    1157             : 
    1158        2698 : const char *CPLExtractRelativePath(const char *pszBaseDir,
    1159             :                                    const char *pszTarget, int *pbGotRelative)
    1160             : 
    1161             : {
    1162             :     /* -------------------------------------------------------------------- */
    1163             :     /*      If we don't have a basedir, then we can't relativize the path.  */
    1164             :     /* -------------------------------------------------------------------- */
    1165        2698 :     if (pszBaseDir == nullptr)
    1166             :     {
    1167           0 :         if (pbGotRelative != nullptr)
    1168           0 :             *pbGotRelative = FALSE;
    1169             : 
    1170           0 :         return pszTarget;
    1171             :     }
    1172             : 
    1173        2698 :     const size_t nBasePathLen = strlen(pszBaseDir);
    1174             : 
    1175             :     /* -------------------------------------------------------------------- */
    1176             :     /*      One simple case is when the base dir is '.' and the target      */
    1177             :     /*      filename is relative.                                           */
    1178             :     /* -------------------------------------------------------------------- */
    1179        2720 :     if ((nBasePathLen == 0 || EQUAL(pszBaseDir, ".")) &&
    1180          22 :         CPLIsFilenameRelative(pszTarget))
    1181             :     {
    1182          22 :         if (pbGotRelative != nullptr)
    1183          22 :             *pbGotRelative = TRUE;
    1184             : 
    1185          22 :         return pszTarget;
    1186             :     }
    1187             : 
    1188             :     /* -------------------------------------------------------------------- */
    1189             :     /*      By this point, if we don't have a base path, we can't have a    */
    1190             :     /*      meaningful common prefix.                                       */
    1191             :     /* -------------------------------------------------------------------- */
    1192        2676 :     if (nBasePathLen == 0)
    1193             :     {
    1194           0 :         if (pbGotRelative != nullptr)
    1195           0 :             *pbGotRelative = FALSE;
    1196             : 
    1197           0 :         return pszTarget;
    1198             :     }
    1199             : 
    1200             :     /* -------------------------------------------------------------------- */
    1201             :     /*      If we don't have a common path prefix, then we can't get a      */
    1202             :     /*      relative path.                                                  */
    1203             :     /* -------------------------------------------------------------------- */
    1204        2676 :     if (!EQUALN(pszBaseDir, pszTarget, nBasePathLen) ||
    1205        2234 :         (pszTarget[nBasePathLen] != '\\' && pszTarget[nBasePathLen] != '/'))
    1206             :     {
    1207         443 :         if (pbGotRelative != nullptr)
    1208         443 :             *pbGotRelative = FALSE;
    1209             : 
    1210         443 :         return pszTarget;
    1211             :     }
    1212             : 
    1213             :     /* -------------------------------------------------------------------- */
    1214             :     /*      We have a relative path.  Strip it off to get a string to       */
    1215             :     /*      return.                                                         */
    1216             :     /* -------------------------------------------------------------------- */
    1217        2233 :     if (pbGotRelative != nullptr)
    1218        2164 :         *pbGotRelative = TRUE;
    1219             : 
    1220        2233 :     return pszTarget + nBasePathLen + 1;
    1221             : }
    1222             : 
    1223             : /************************************************************************/
    1224             : /*                      CPLCleanTrailingSlashSafe()                     */
    1225             : /************************************************************************/
    1226             : 
    1227             : /**
    1228             :  * Remove trailing forward/backward slash from the path for UNIX/Windows resp.
    1229             :  *
    1230             :  * Returns a string containing the portion of the passed path string with
    1231             :  * trailing slash removed. If there is no path in the passed filename
    1232             :  * an empty string will be returned (not NULL).
    1233             :  *
    1234             :  * \code{.cpp}
    1235             :  * CPLCleanTrailingSlashSafe( "abc/def/" ) == "abc/def"
    1236             :  * CPLCleanTrailingSlashSafe( "abc/def" ) == "abc/def"
    1237             :  * CPLCleanTrailingSlashSafe( "c:\\abc\\def\\" ) == "c:\\abc\\def"
    1238             :  * CPLCleanTrailingSlashSafe( "c:\\abc\\def" ) == "c:\\abc\\def"
    1239             :  * CPLCleanTrailingSlashSafe( "abc" ) == "abc"
    1240             :  * \endcode
    1241             :  *
    1242             :  * @param pszPath the path to be cleaned up
    1243             :  *
    1244             :  * @return Path
    1245             :  *
    1246             :  * @since 3.11
    1247             :  */
    1248             : 
    1249           9 : std::string CPLCleanTrailingSlashSafe(const char *pszPath)
    1250             : 
    1251             : {
    1252           9 :     std::string osRes(pszPath);
    1253           9 :     if (!osRes.empty() && (osRes.back() == '\\' || osRes.back() == '/'))
    1254           0 :         osRes.pop_back();
    1255           9 :     return osRes;
    1256             : }
    1257             : 
    1258             : /************************************************************************/
    1259             : /*                            CPLCleanTrailingSlash()                   */
    1260             : /************************************************************************/
    1261             : 
    1262             : /**
    1263             :  * Remove trailing forward/backward slash from the path for UNIX/Windows resp.
    1264             :  *
    1265             :  * Returns a string containing the portion of the passed path string with
    1266             :  * trailing slash removed. If there is no path in the passed filename
    1267             :  * an empty string will be returned (not NULL).
    1268             :  *
    1269             :  * \code{.cpp}
    1270             :  * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
    1271             :  * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
    1272             :  * CPLCleanTrailingSlash( "c:\\abc\\def\\" ) == "c:\\abc\\def"
    1273             :  * CPLCleanTrailingSlash( "c:\\abc\\def" ) == "c:\\abc\\def"
    1274             :  * CPLCleanTrailingSlash( "abc" ) == "abc"
    1275             :  * \endcode
    1276             :  *
    1277             :  * @param pszPath the path to be cleaned up
    1278             :  *
    1279             :  * @return Path in an internal string which must not be freed.  The string
    1280             :  * may be destroyed by the next CPL filename handling call.
    1281             :  *
    1282             :  * @deprecated If using C++, prefer using CPLCleanTrailingSlashSafe() instead
    1283             :  */
    1284             : 
    1285           0 : const char *CPLCleanTrailingSlash(const char *pszPath)
    1286             : 
    1287             : {
    1288           0 :     return CPLPathReturnTLSString(CPLCleanTrailingSlashSafe(pszPath),
    1289           0 :                                   __FUNCTION__);
    1290             : }
    1291             : 
    1292             : /************************************************************************/
    1293             : /*                       CPLCorrespondingPaths()                        */
    1294             : /************************************************************************/
    1295             : 
    1296             : /**
    1297             :  * Identify corresponding paths.
    1298             :  *
    1299             :  * Given a prototype old and new filename this function will attempt
    1300             :  * to determine corresponding names for a set of other old filenames that
    1301             :  * will rename them in a similar manner.  This correspondence assumes there
    1302             :  * are two possibly kinds of renaming going on.  A change of path, and a
    1303             :  * change of filename stem.
    1304             :  *
    1305             :  * If a consistent renaming cannot be established for all the files this
    1306             :  * function will return indicating an error.
    1307             :  *
    1308             :  * The returned file list becomes owned by the caller and should be destroyed
    1309             :  * with CSLDestroy().
    1310             :  *
    1311             :  * @param pszOldFilename path to old prototype file.
    1312             :  * @param pszNewFilename path to new prototype file.
    1313             :  * @param papszFileList list of other files associated with pszOldFilename to
    1314             :  * rename similarly.
    1315             :  *
    1316             :  * @return a list of files corresponding to papszFileList but renamed to
    1317             :  * correspond to pszNewFilename.
    1318             :  */
    1319             : 
    1320         179 : char **CPLCorrespondingPaths(const char *pszOldFilename,
    1321             :                              const char *pszNewFilename, char **papszFileList)
    1322             : 
    1323             : {
    1324         179 :     if (CSLCount(papszFileList) == 0)
    1325           0 :         return nullptr;
    1326             : 
    1327             :     /* -------------------------------------------------------------------- */
    1328             :     /*      There is a special case for a one item list which exactly       */
    1329             :     /*      matches the old name, to rename to the new name.                */
    1330             :     /* -------------------------------------------------------------------- */
    1331         347 :     if (CSLCount(papszFileList) == 1 &&
    1332         168 :         strcmp(pszOldFilename, papszFileList[0]) == 0)
    1333             :     {
    1334         168 :         return CSLAddString(nullptr, pszNewFilename);
    1335             :     }
    1336             : 
    1337          22 :     const std::string osOldPath = CPLGetPathSafe(pszOldFilename);
    1338          22 :     const std::string osOldBasename = CPLGetBasenameSafe(pszOldFilename);
    1339          22 :     const std::string osNewBasename = CPLGetBasenameSafe(pszNewFilename);
    1340             : 
    1341             :     /* -------------------------------------------------------------------- */
    1342             :     /*      If the basename is changing, verify that all source files       */
    1343             :     /*      have the same starting basename.                                */
    1344             :     /* -------------------------------------------------------------------- */
    1345          11 :     if (osOldBasename != osNewBasename)
    1346             :     {
    1347          34 :         for (int i = 0; papszFileList[i] != nullptr; i++)
    1348             :         {
    1349          24 :             if (osOldBasename == CPLGetBasenameSafe(papszFileList[i]))
    1350          16 :                 continue;
    1351             : 
    1352           8 :             const std::string osFilePath = CPLGetPathSafe(papszFileList[i]);
    1353           8 :             const std::string osFileName = CPLGetFilename(papszFileList[i]);
    1354             : 
    1355           8 :             if (!EQUALN(osFileName.c_str(), osOldBasename.c_str(),
    1356           8 :                         osOldBasename.size()) ||
    1357          16 :                 !EQUAL(osFilePath.c_str(), osOldPath.c_str()) ||
    1358           8 :                 osFileName[osOldBasename.size()] != '.')
    1359             :             {
    1360           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1361             :                          "Unable to rename fileset due irregular basenames.");
    1362           0 :                 return nullptr;
    1363             :             }
    1364             :         }
    1365             :     }
    1366             : 
    1367             :     /* -------------------------------------------------------------------- */
    1368             :     /*      If the filename portions differs, ensure they only differ in    */
    1369             :     /*      basename.                                                       */
    1370             :     /* -------------------------------------------------------------------- */
    1371          11 :     if (osOldBasename != osNewBasename)
    1372             :     {
    1373             :         const std::string osOldExtra =
    1374          10 :             CPLGetFilename(pszOldFilename) + osOldBasename.size();
    1375             :         const std::string osNewExtra =
    1376          10 :             CPLGetFilename(pszNewFilename) + osNewBasename.size();
    1377             : 
    1378          10 :         if (osOldExtra != osNewExtra)
    1379             :         {
    1380           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1381             :                      "Unable to rename fileset due to irregular filename "
    1382             :                      "correspondence.");
    1383           0 :             return nullptr;
    1384             :         }
    1385             :     }
    1386             : 
    1387             :     /* -------------------------------------------------------------------- */
    1388             :     /*      Generate the new filenames.                                     */
    1389             :     /* -------------------------------------------------------------------- */
    1390          11 :     char **papszNewList = nullptr;
    1391          11 :     const std::string osNewPath = CPLGetPathSafe(pszNewFilename);
    1392             : 
    1393          37 :     for (int i = 0; papszFileList[i] != nullptr; i++)
    1394             :     {
    1395          52 :         const std::string osOldFilename = CPLGetFilename(papszFileList[i]);
    1396             : 
    1397             :         const std::string osNewFilename =
    1398          26 :             osOldBasename == osNewBasename
    1399             :                 ? CPLFormFilenameSafe(osNewPath.c_str(), osOldFilename.c_str(),
    1400             :                                       nullptr)
    1401             :                 : CPLFormFilenameSafe(osNewPath.c_str(), osNewBasename.c_str(),
    1402          24 :                                       osOldFilename.c_str() +
    1403          50 :                                           osOldBasename.size());
    1404             : 
    1405          26 :         papszNewList = CSLAddString(papszNewList, osNewFilename.c_str());
    1406             :     }
    1407             : 
    1408          11 :     return papszNewList;
    1409             : }
    1410             : 
    1411             : /************************************************************************/
    1412             : /*                   CPLGenerateTempFilenameSafe()                      */
    1413             : /************************************************************************/
    1414             : 
    1415             : /**
    1416             :  * Generate temporary file name.
    1417             :  *
    1418             :  * Returns a filename that may be used for a temporary file.  The location
    1419             :  * of the file tries to follow operating system semantics but may be
    1420             :  * forced via the CPL_TMPDIR configuration option.
    1421             :  *
    1422             :  * @param pszStem if non-NULL this will be part of the filename.
    1423             :  *
    1424             :  * @return a filename
    1425             :  *
    1426             :  * @since 3.11
    1427             :  */
    1428             : 
    1429        2336 : std::string CPLGenerateTempFilenameSafe(const char *pszStem)
    1430             : 
    1431             : {
    1432        2336 :     const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
    1433             : 
    1434        2336 :     if (pszDir == nullptr)
    1435        2334 :         pszDir = CPLGetConfigOption("TMPDIR", nullptr);
    1436             : 
    1437        2336 :     if (pszDir == nullptr)
    1438        2334 :         pszDir = CPLGetConfigOption("TEMP", nullptr);
    1439             : 
    1440        2336 :     if (pszDir == nullptr)
    1441        2334 :         pszDir = ".";
    1442             : 
    1443        2336 :     if (pszStem == nullptr)
    1444        2167 :         pszStem = "";
    1445             : 
    1446             :     static int nTempFileCounter = 0;
    1447        4672 :     CPLString osFilename;
    1448             :     osFilename.Printf("%s_%d_%d", pszStem, CPLGetCurrentProcessID(),
    1449        2336 :                       CPLAtomicInc(&nTempFileCounter));
    1450             : 
    1451        4672 :     return CPLFormFilenameSafe(pszDir, osFilename.c_str(), nullptr);
    1452             : }
    1453             : 
    1454             : /************************************************************************/
    1455             : /*                      CPLGenerateTempFilename()                       */
    1456             : /************************************************************************/
    1457             : 
    1458             : /**
    1459             :  * Generate temporary file name.
    1460             :  *
    1461             :  * Returns a filename that may be used for a temporary file.  The location
    1462             :  * of the file tries to follow operating system semantics but may be
    1463             :  * forced via the CPL_TMPDIR configuration option.
    1464             :  *
    1465             :  * @param pszStem if non-NULL this will be part of the filename.
    1466             :  *
    1467             :  * @return a filename which is valid till the next CPL call in this thread.
    1468             :  *
    1469             :  * @deprecated If using C++, prefer using CPLCleanTrailingSlashSafe() instead
    1470             :  */
    1471             : 
    1472           6 : const char *CPLGenerateTempFilename(const char *pszStem)
    1473             : 
    1474             : {
    1475          12 :     return CPLPathReturnTLSString(CPLGenerateTempFilenameSafe(pszStem),
    1476          12 :                                   __FUNCTION__);
    1477             : }
    1478             : 
    1479             : /************************************************************************/
    1480             : /*                        CPLExpandTildeSafe()                          */
    1481             : /************************************************************************/
    1482             : 
    1483             : /**
    1484             :  * Expands ~/ at start of filename.
    1485             :  *
    1486             :  * Assumes that the HOME configuration option is defined.
    1487             :  *
    1488             :  * @param pszFilename filename potentially starting with ~/
    1489             :  *
    1490             :  * @return an expanded filename.
    1491             :  *
    1492             :  * @since GDAL 3.11
    1493             :  */
    1494             : 
    1495         189 : std::string CPLExpandTildeSafe(const char *pszFilename)
    1496             : 
    1497             : {
    1498         189 :     if (!STARTS_WITH_CI(pszFilename, "~/"))
    1499         188 :         return pszFilename;
    1500             : 
    1501           1 :     const char *pszHome = CPLGetConfigOption("HOME", nullptr);
    1502           1 :     if (pszHome == nullptr)
    1503           0 :         return pszFilename;
    1504             : 
    1505           1 :     return CPLFormFilenameSafe(pszHome, pszFilename + 2, nullptr);
    1506             : }
    1507             : 
    1508             : /************************************************************************/
    1509             : /*                         CPLExpandTilde()                             */
    1510             : /************************************************************************/
    1511             : 
    1512             : /**
    1513             :  * Expands ~/ at start of filename.
    1514             :  *
    1515             :  * Assumes that the HOME configuration option is defined.
    1516             :  *
    1517             :  * @param pszFilename filename potentially starting with ~/
    1518             :  *
    1519             :  * @return an expanded filename.
    1520             :  *
    1521             :  * @since GDAL 2.2
    1522             :  *
    1523             :  * @deprecated If using C++, prefer using CPLExpandTildeSafe() instead
    1524             :  */
    1525             : 
    1526           2 : const char *CPLExpandTilde(const char *pszFilename)
    1527             : 
    1528             : {
    1529           4 :     return CPLPathReturnTLSString(CPLExpandTildeSafe(pszFilename),
    1530           4 :                                   __FUNCTION__);
    1531             : }
    1532             : 
    1533             : /************************************************************************/
    1534             : /*                         CPLGetHomeDir()                              */
    1535             : /************************************************************************/
    1536             : 
    1537             : /**
    1538             :  * Return the path to the home directory
    1539             :  *
    1540             :  * That is the value of the USERPROFILE environment variable on Windows,
    1541             :  * or HOME on other platforms.
    1542             :  *
    1543             :  * @return the home directory, or NULL.
    1544             :  *
    1545             :  * @since GDAL 2.3
    1546             :  */
    1547             : 
    1548           0 : const char *CPLGetHomeDir()
    1549             : 
    1550             : {
    1551             : #ifdef _WIN32
    1552             :     return CPLGetConfigOption("USERPROFILE", nullptr);
    1553             : #else
    1554           0 :     return CPLGetConfigOption("HOME", nullptr);
    1555             : #endif
    1556             : }
    1557             : 
    1558             : /************************************************************************/
    1559             : /*                      CPLLaunderForFilenameSafe()                     */
    1560             : /************************************************************************/
    1561             : 
    1562             : /**
    1563             :  * Launder a string to be compatible of a filename.
    1564             :  *
    1565             :  * @param pszName The input string to launder.
    1566             :  * @param pszOutputPath The directory where the file would be created.
    1567             :  *                      Unused for now. May be NULL.
    1568             :  * @return the laundered name.
    1569             :  *
    1570             :  * @since GDAL 3.11
    1571             :  */
    1572             : 
    1573        1157 : std::string CPLLaunderForFilenameSafe(const char *pszName,
    1574             :                                       CPL_UNUSED const char *pszOutputPath)
    1575             : {
    1576        1157 :     std::string osRet(pszName);
    1577       10874 :     for (char &ch : osRet)
    1578             :     {
    1579             :         // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
    1580        9717 :         if (ch == '<' || ch == '>' || ch == ':' || ch == '"' || ch == '/' ||
    1581        9711 :             ch == '\\' || ch == '?' || ch == '*')
    1582             :         {
    1583           9 :             ch = '_';
    1584             :         }
    1585             :     }
    1586        1157 :     return osRet;
    1587             : }
    1588             : 
    1589             : /************************************************************************/
    1590             : /*                        CPLLaunderForFilename()                       */
    1591             : /************************************************************************/
    1592             : 
    1593             : /**
    1594             :  * Launder a string to be compatible of a filename.
    1595             :  *
    1596             :  * @param pszName The input string to launder.
    1597             :  * @param pszOutputPath The directory where the file would be created.
    1598             :  *                      Unused for now. May be NULL.
    1599             :  * @return the laundered name.
    1600             :  *
    1601             :  * @since GDAL 3.1
    1602             :  *
    1603             :  * @deprecated If using C++, prefer using CPLLaunderForFilenameSafe() instead
    1604             :  */
    1605             : 
    1606           0 : const char *CPLLaunderForFilename(const char *pszName,
    1607             :                                   const char *pszOutputPath)
    1608             : {
    1609           0 :     return CPLPathReturnTLSString(
    1610           0 :         CPLLaunderForFilenameSafe(pszName, pszOutputPath), __FUNCTION__);
    1611             : }

Generated by: LCOV version 1.14