LCOV - code coverage report
Current view: top level - port - cpl_path.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 316 362 87.3 %
Date: 2024-11-21 22:18:42 Functions: 19 21 90.5 %

          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             : #include "cpl_port.h"
      15             : #include "cpl_conv.h"
      16             : 
      17             : #include <cctype>
      18             : #include <climits>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstring>
      22             : #if HAVE_UNISTD_H
      23             : #include <unistd.h>
      24             : #endif
      25             : 
      26             : #include <algorithm>
      27             : #include <string>
      28             : 
      29             : #include "cpl_atomic_ops.h"
      30             : #include "cpl_config.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_multiproc.h"
      33             : #include "cpl_string.h"
      34             : #include "cpl_vsi.h"
      35             : 
      36             : // Should be size of larged possible filename.
      37             : constexpr int CPL_PATH_BUF_SIZE = 2048;
      38             : constexpr int CPL_PATH_BUF_COUNT = 10;
      39             : 
      40           0 : static const char *CPLStaticBufferTooSmall(char *pszStaticResult)
      41             : {
      42           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Destination buffer too small");
      43           0 :     if (pszStaticResult == nullptr)
      44           0 :         return "";
      45           0 :     strcpy(pszStaticResult, "");
      46           0 :     return pszStaticResult;
      47             : }
      48             : 
      49             : /************************************************************************/
      50             : /*                         CPLGetStaticResult()                         */
      51             : /************************************************************************/
      52             : 
      53     2986490 : static char *CPLGetStaticResult()
      54             : 
      55             : {
      56     2986490 :     int bMemoryError = FALSE;
      57             :     char *pachBufRingInfo =
      58     2986490 :         static_cast<char *>(CPLGetTLSEx(CTLS_PATHBUF, &bMemoryError));
      59     2986440 :     if (bMemoryError)
      60           0 :         return nullptr;
      61     2986440 :     if (pachBufRingInfo == nullptr)
      62             :     {
      63        1375 :         pachBufRingInfo = static_cast<char *>(VSI_CALLOC_VERBOSE(
      64             :             1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT));
      65        1375 :         if (pachBufRingInfo == nullptr)
      66           0 :             return nullptr;
      67        1375 :         CPLSetTLS(CTLS_PATHBUF, pachBufRingInfo, TRUE);
      68             :     }
      69             : 
      70             :     /* -------------------------------------------------------------------- */
      71             :     /*      Work out which string in the "ring" we want to use this         */
      72             :     /*      time.                                                           */
      73             :     /* -------------------------------------------------------------------- */
      74     2986400 :     int *pnBufIndex = reinterpret_cast<int *>(pachBufRingInfo);
      75     2986400 :     const size_t nOffset =
      76     2986400 :         sizeof(int) + static_cast<size_t>(*pnBufIndex * CPL_PATH_BUF_SIZE);
      77     2986400 :     char *pachBuffer = pachBufRingInfo + nOffset;
      78             : 
      79     2986400 :     *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT;
      80             : 
      81     2986400 :     return pachBuffer;
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                        CPLFindFilenameStart()                        */
      86             : /************************************************************************/
      87             : 
      88     3211280 : static int CPLFindFilenameStart(const char *pszFilename, size_t nStart = 0)
      89             : 
      90             : {
      91     3211280 :     size_t iFileStart = nStart ? nStart : strlen(pszFilename);
      92             : 
      93    47899600 :     for (; iFileStart > 0 && pszFilename[iFileStart - 1] != '/' &&
      94    44690100 :            pszFilename[iFileStart - 1] != '\\';
      95             :          iFileStart--)
      96             :     {
      97             :     }
      98             : 
      99     3211280 :     return static_cast<int>(iFileStart);
     100             : }
     101             : 
     102             : /************************************************************************/
     103             : /*                             CPLGetPath()                             */
     104             : /************************************************************************/
     105             : 
     106             : /**
     107             :  * Extract directory path portion of filename.
     108             :  *
     109             :  * Returns a string containing the directory path portion of the passed
     110             :  * filename.  If there is no path in the passed filename an empty string
     111             :  * will be returned (not NULL).
     112             :  *
     113             :  * <pre>
     114             :  * CPLGetPath( "abc/def.xyz" ) == "abc"
     115             :  * CPLGetPath( "/abc/def/" ) == "/abc/def"
     116             :  * CPLGetPath( "/" ) == "/"
     117             :  * CPLGetPath( "/abc/def" ) == "/abc"
     118             :  * CPLGetPath( "abc" ) == ""
     119             :  * </pre>
     120             :  *
     121             :  * @param pszFilename the filename potentially including a path.
     122             :  *
     123             :  *  @return Path in an internal string which must not be freed.  The string
     124             :  * may be destroyed by the next CPL filename handling call.  The returned
     125             :  * will generally not contain a trailing path separator.
     126             :  */
     127             : 
     128      208372 : const char *CPLGetPath(const char *pszFilename)
     129             : 
     130             : {
     131      208372 :     size_t nSuffixPos = 0;
     132      208372 :     if (STARTS_WITH(pszFilename, "/vsicurl/http"))
     133             :     {
     134          12 :         const char *pszQuestionMark = strchr(pszFilename, '?');
     135          12 :         if (pszQuestionMark)
     136           1 :             nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
     137             :     }
     138             : 
     139      208372 :     const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
     140      208372 :     char *pszStaticResult = CPLGetStaticResult();
     141             : 
     142      208372 :     if (pszStaticResult == nullptr || iFileStart >= CPL_PATH_BUF_SIZE)
     143           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     144             : 
     145      208372 :     CPLAssert(!(pszFilename >= pszStaticResult &&
     146             :                 pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
     147             : 
     148      208372 :     if (iFileStart == 0)
     149             :     {
     150         550 :         strcpy(pszStaticResult, "");
     151         550 :         return pszStaticResult;
     152             :     }
     153             : 
     154      207822 :     CPLStrlcpy(pszStaticResult, pszFilename,
     155      207822 :                static_cast<size_t>(iFileStart) + 1);
     156             : 
     157      207822 :     if (iFileStart > 1 && (pszStaticResult[iFileStart - 1] == '/' ||
     158           0 :                            pszStaticResult[iFileStart - 1] == '\\'))
     159      207482 :         pszStaticResult[iFileStart - 1] = '\0';
     160             : 
     161      207822 :     if (nSuffixPos)
     162             :     {
     163           1 :         if (CPLStrlcat(pszStaticResult, pszFilename + nSuffixPos,
     164           1 :                        CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
     165           0 :             return CPLStaticBufferTooSmall(pszStaticResult);
     166             :     }
     167             : 
     168      207822 :     return pszStaticResult;
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                             CPLGetDirname()                          */
     173             : /************************************************************************/
     174             : 
     175             : /**
     176             :  * Extract directory path portion of filename.
     177             :  *
     178             :  * Returns a string containing the directory path portion of the passed
     179             :  * filename.  If there is no path in the passed filename the dot will be
     180             :  * returned.  It is the only difference from CPLGetPath().
     181             :  *
     182             :  * <pre>
     183             :  * CPLGetDirname( "abc/def.xyz" ) == "abc"
     184             :  * CPLGetDirname( "/abc/def/" ) == "/abc/def"
     185             :  * CPLGetDirname( "/" ) == "/"
     186             :  * CPLGetDirname( "/abc/def" ) == "/abc"
     187             :  * CPLGetDirname( "abc" ) == "."
     188             :  * </pre>
     189             :  *
     190             :  * @param pszFilename the filename potentially including a path.
     191             :  *
     192             :  * @return Path in an internal string which must not be freed.  The string
     193             :  * may be destroyed by the next CPL filename handling call.  The returned
     194             :  * will generally not contain a trailing path separator.
     195             :  */
     196             : 
     197      139893 : const char *CPLGetDirname(const char *pszFilename)
     198             : 
     199             : {
     200      139893 :     size_t nSuffixPos = 0;
     201      139893 :     if (STARTS_WITH(pszFilename, "/vsicurl/http"))
     202             :     {
     203         111 :         const char *pszQuestionMark = strchr(pszFilename, '?');
     204         111 :         if (pszQuestionMark)
     205           1 :             nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
     206             :     }
     207             : 
     208      139893 :     const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
     209      139873 :     char *pszStaticResult = CPLGetStaticResult();
     210             : 
     211      139826 :     if (pszStaticResult == nullptr || iFileStart >= CPL_PATH_BUF_SIZE)
     212          48 :         return CPLStaticBufferTooSmall(pszStaticResult);
     213             : 
     214      139778 :     CPLAssert(!(pszFilename >= pszStaticResult &&
     215             :                 pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
     216             : 
     217      139778 :     if (iFileStart == 0)
     218             :     {
     219          63 :         strcpy(pszStaticResult, ".");
     220          63 :         return pszStaticResult;
     221             :     }
     222             : 
     223      139715 :     CPLStrlcpy(pszStaticResult, pszFilename,
     224      139715 :                static_cast<size_t>(iFileStart) + 1);
     225             : 
     226      139813 :     if (iFileStart > 1 && (pszStaticResult[iFileStart - 1] == '/' ||
     227          22 :                            pszStaticResult[iFileStart - 1] == '\\'))
     228      139750 :         pszStaticResult[iFileStart - 1] = '\0';
     229             : 
     230      139813 :     if (nSuffixPos)
     231             :     {
     232           1 :         if (CPLStrlcat(pszStaticResult, pszFilename + nSuffixPos,
     233           1 :                        CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
     234          46 :             return CPLStaticBufferTooSmall(pszStaticResult);
     235             :     }
     236             : 
     237      139767 :     return pszStaticResult;
     238             : }
     239             : 
     240             : /************************************************************************/
     241             : /*                           CPLGetFilename()                           */
     242             : /************************************************************************/
     243             : 
     244             : /**
     245             :  * Extract non-directory portion of filename.
     246             :  *
     247             :  * Returns a string containing the bare filename portion of the passed
     248             :  * filename.  If there is no filename (passed value ends in trailing directory
     249             :  * separator) an empty string is returned.
     250             :  *
     251             :  * <pre>
     252             :  * CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
     253             :  * CPLGetFilename( "/abc/def/" ) == ""
     254             :  * CPLGetFilename( "abc/def" ) == "def"
     255             :  * </pre>
     256             :  *
     257             :  * @param pszFullFilename the full filename potentially including a path.
     258             :  *
     259             :  * @return just the non-directory portion of the path (points back into
     260             :  * original string).
     261             :  */
     262             : 
     263      755031 : const char *CPLGetFilename(const char *pszFullFilename)
     264             : 
     265             : {
     266      755031 :     const int iFileStart = CPLFindFilenameStart(pszFullFilename);
     267             : 
     268      755030 :     return pszFullFilename + iFileStart;
     269             : }
     270             : 
     271             : /************************************************************************/
     272             : /*                           CPLGetBasename()                           */
     273             : /************************************************************************/
     274             : 
     275             : /**
     276             :  * Extract basename (non-directory, non-extension) portion of filename.
     277             :  *
     278             :  * Returns a string containing the file basename portion of the passed
     279             :  * name.  If there is no basename (passed value ends in trailing directory
     280             :  * separator, or filename starts with a dot) an empty string is returned.
     281             :  *
     282             :  * <pre>
     283             :  * CPLGetBasename( "abc/def.xyz" ) == "def"
     284             :  * CPLGetBasename( "abc/def" ) == "def"
     285             :  * CPLGetBasename( "abc/def/" ) == ""
     286             :  * </pre>
     287             :  *
     288             :  * @param pszFullFilename the full filename potentially including a path.
     289             :  *
     290             :  * @return just the non-directory, non-extension portion of the path in
     291             :  * an internal string which must not be freed.  The string
     292             :  * may be destroyed by the next CPL filename handling call.
     293             :  */
     294             : 
     295      442140 : const char *CPLGetBasename(const char *pszFullFilename)
     296             : 
     297             : {
     298             :     const size_t iFileStart =
     299      442140 :         static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
     300      442140 :     char *pszStaticResult = CPLGetStaticResult();
     301      442140 :     if (pszStaticResult == nullptr)
     302           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     303             : 
     304      442140 :     CPLAssert(!(pszFullFilename >= pszStaticResult &&
     305             :                 pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
     306             : 
     307      442140 :     size_t iExtStart = strlen(pszFullFilename);
     308     2502590 :     for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
     309             :          iExtStart--)
     310             :     {
     311             :     }
     312             : 
     313      442140 :     if (iExtStart == iFileStart)
     314       27176 :         iExtStart = strlen(pszFullFilename);
     315             : 
     316      442140 :     const size_t nLength = iExtStart - iFileStart;
     317             : 
     318      442140 :     if (nLength >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
     319           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     320             : 
     321      442140 :     CPLStrlcpy(pszStaticResult, pszFullFilename + iFileStart, nLength + 1);
     322             : 
     323      442140 :     return pszStaticResult;
     324             : }
     325             : 
     326             : /************************************************************************/
     327             : /*                           CPLGetExtension()                          */
     328             : /************************************************************************/
     329             : 
     330             : /**
     331             :  * Extract filename extension from full filename.
     332             :  *
     333             :  * Returns a string containing the extension portion of the passed
     334             :  * name.  If there is no extension (the filename has no dot) an empty string
     335             :  * is returned.  The returned extension will not include the period.
     336             :  *
     337             :  * <pre>
     338             :  * CPLGetExtension( "abc/def.xyz" ) == "xyz"
     339             :  * CPLGetExtension( "abc/def" ) == ""
     340             :  * </pre>
     341             :  *
     342             :  * @param pszFullFilename the full filename potentially including a path.
     343             :  *
     344             :  * @return just the extension portion of the path in
     345             :  * an internal string which must not be freed.  The string
     346             :  * may be destroyed by the next CPL filename handling call.
     347             :  */
     348             : 
     349     1685270 : const char *CPLGetExtension(const char *pszFullFilename)
     350             : 
     351             : {
     352     1685270 :     if (pszFullFilename[0] == '\0')
     353       19378 :         return "";
     354             : 
     355             :     size_t iFileStart =
     356     1665890 :         static_cast<size_t>(CPLFindFilenameStart(pszFullFilename));
     357     1665920 :     char *pszStaticResult = CPLGetStaticResult();
     358     1665870 :     if (pszStaticResult == nullptr)
     359           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     360             : 
     361     1665870 :     CPLAssert(!(pszFullFilename >= pszStaticResult &&
     362             :                 pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
     363             : 
     364     1665870 :     size_t iExtStart = strlen(pszFullFilename);
     365    13191400 :     for (; iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
     366             :          iExtStart--)
     367             :     {
     368             :     }
     369             : 
     370     1665870 :     if (iExtStart == iFileStart)
     371      547565 :         iExtStart = strlen(pszFullFilename) - 1;
     372             : 
     373             :     // If the extension is too long, it is very much likely not an extension,
     374             :     // but another component of the path
     375     1665870 :     const size_t knMaxExtensionSize = 10;
     376     1665870 :     if (strlen(pszFullFilename + iExtStart + 1) > knMaxExtensionSize)
     377        8062 :         return "";
     378             : 
     379     1657810 :     if (CPLStrlcpy(pszStaticResult, pszFullFilename + iExtStart + 1,
     380     1657820 :                    CPL_PATH_BUF_SIZE) >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
     381           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     382             : 
     383     1657820 :     return pszStaticResult;
     384             : }
     385             : 
     386             : /************************************************************************/
     387             : /*                         CPLGetCurrentDir()                           */
     388             : /************************************************************************/
     389             : 
     390             : /**
     391             :  * Get the current working directory name.
     392             :  *
     393             :  * @return a pointer to buffer, containing current working directory path
     394             :  * or NULL in case of error.  User is responsible to free that buffer
     395             :  * after usage with CPLFree() function.
     396             :  * If HAVE_GETCWD macro is not defined, the function returns NULL.
     397             :  **/
     398             : 
     399             : #ifdef _WIN32
     400             : char *CPLGetCurrentDir()
     401             : {
     402             :     const size_t nPathMax = _MAX_PATH;
     403             :     wchar_t *pwszDirPath =
     404             :         static_cast<wchar_t *>(VSI_MALLOC_VERBOSE(nPathMax * sizeof(wchar_t)));
     405             :     char *pszRet = nullptr;
     406             :     if (pwszDirPath != nullptr && _wgetcwd(pwszDirPath, nPathMax) != nullptr)
     407             :     {
     408             :         pszRet = CPLRecodeFromWChar(pwszDirPath, CPL_ENC_UCS2, CPL_ENC_UTF8);
     409             :     }
     410             :     CPLFree(pwszDirPath);
     411             :     return pszRet;
     412             : }
     413             : #elif defined(HAVE_GETCWD)
     414        5063 : char *CPLGetCurrentDir()
     415             : {
     416             : #if PATH_MAX
     417        5063 :     const size_t nPathMax = PATH_MAX;
     418             : #else
     419             :     const size_t nPathMax = 8192;
     420             : #endif
     421             : 
     422        5063 :     char *pszDirPath = static_cast<char *>(VSI_MALLOC_VERBOSE(nPathMax));
     423        5063 :     if (!pszDirPath)
     424           0 :         return nullptr;
     425             : 
     426        5063 :     return getcwd(pszDirPath, nPathMax);
     427             : }
     428             : #else   // !HAVE_GETCWD
     429             : char *CPLGetCurrentDir()
     430             : {
     431             :     return nullptr;
     432             : }
     433             : #endif  // HAVE_GETCWD
     434             : 
     435             : /************************************************************************/
     436             : /*                         CPLResetExtension()                          */
     437             : /************************************************************************/
     438             : 
     439             : /**
     440             :  * Replace the extension with the provided one.
     441             :  *
     442             :  * @param pszPath the input path, this string is not altered.
     443             :  * @param pszExt the new extension to apply to the given path.
     444             :  *
     445             :  * @return an altered filename with the new extension.    Do not
     446             :  * modify or free the returned string.  The string may be destroyed by the
     447             :  * next CPL call.
     448             :  */
     449             : 
     450      158196 : const char *CPLResetExtension(const char *pszPath, const char *pszExt)
     451             : 
     452             : {
     453      158196 :     char *pszStaticResult = CPLGetStaticResult();
     454      158195 :     if (pszStaticResult == nullptr)
     455           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     456             : 
     457      158195 :     CPLAssert(!(pszPath >= pszStaticResult &&
     458             :                 pszPath < pszStaticResult + CPL_PATH_BUF_SIZE));
     459             : 
     460             :     /* -------------------------------------------------------------------- */
     461             :     /*      First, try and strip off any existing extension.                */
     462             :     /* -------------------------------------------------------------------- */
     463      158195 :     if (CPLStrlcpy(pszStaticResult, pszPath, CPL_PATH_BUF_SIZE) >=
     464             :         static_cast<size_t>(CPL_PATH_BUF_SIZE))
     465           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     466             : 
     467      158196 :     if (*pszStaticResult)
     468             :     {
     469      853576 :         for (size_t i = strlen(pszStaticResult) - 1; i > 0; i--)
     470             :         {
     471      853301 :             if (pszStaticResult[i] == '.')
     472             :             {
     473      141662 :                 pszStaticResult[i] = '\0';
     474      141662 :                 break;
     475             :             }
     476             : 
     477      711639 :             if (pszStaticResult[i] == '/' || pszStaticResult[i] == '\\' ||
     478      695432 :                 pszStaticResult[i] == ':')
     479             :                 break;
     480             :         }
     481             :     }
     482             : 
     483             :     /* -------------------------------------------------------------------- */
     484             :     /*      Append the new extension.                                       */
     485             :     /* -------------------------------------------------------------------- */
     486      158196 :     if (CPLStrlcat(pszStaticResult, ".", CPL_PATH_BUF_SIZE) >=
     487      316392 :             static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
     488      158196 :         CPLStrlcat(pszStaticResult, pszExt, CPL_PATH_BUF_SIZE) >=
     489             :             static_cast<size_t>(CPL_PATH_BUF_SIZE))
     490             :     {
     491           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     492             :     }
     493             : 
     494      158196 :     return pszStaticResult;
     495             : }
     496             : 
     497             : /************************************************************************/
     498             : /*                          CPLFormFilename()                           */
     499             : /************************************************************************/
     500             : 
     501             : /**
     502             :  * Build a full file path from a passed path, file basename and extension.
     503             :  *
     504             :  * The path, and extension are optional.  The basename may in fact contain
     505             :  * an extension if desired.
     506             :  *
     507             :  * <pre>
     508             :  * CPLFormFilename("abc/xyz", "def", ".dat" ) == "abc/xyz/def.dat"
     509             :  * CPLFormFilename(NULL,"def", NULL ) == "def"
     510             :  * CPLFormFilename(NULL, "abc/def.dat", NULL ) == "abc/def.dat"
     511             :  * CPLFormFilename("/abc/xyz/", "def.dat", NULL ) == "/abc/xyz/def.dat"
     512             :  * </pre>
     513             :  *
     514             :  * @param pszPath directory path to the directory containing the file.  This
     515             :  * may be relative or absolute, and may have a trailing path separator or
     516             :  * not.  May be NULL.
     517             :  *
     518             :  * @param pszBasename file basename.  May optionally have path and/or
     519             :  * extension.  Must *NOT* be NULL.
     520             :  *
     521             :  * @param pszExtension file extension, optionally including the period.  May
     522             :  * be NULL.
     523             :  *
     524             :  * @return a fully formed filename in an internal static string.  Do not
     525             :  * modify or free the returned string.  The string may be destroyed by the
     526             :  * next CPL call.
     527             :  */
     528             : 
     529      367760 : const char *CPLFormFilename(const char *pszPath, const char *pszBasename,
     530             :                             const char *pszExtension)
     531             : 
     532             : {
     533      367760 :     char *pszStaticResult = CPLGetStaticResult();
     534      367757 :     if (pszStaticResult == nullptr)
     535           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     536             : 
     537      367757 :     CPLAssert(!(pszPath >= pszStaticResult &&
     538             :                 pszPath < pszStaticResult + CPL_PATH_BUF_SIZE));
     539      367757 :     CPLAssert(!(pszBasename >= pszStaticResult &&
     540             :                 pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE));
     541             : 
     542      367757 :     if (pszBasename[0] == '.' &&
     543        9149 :         (pszBasename[1] == '/' || pszBasename[1] == '\\'))
     544          62 :         pszBasename += 2;
     545             : 
     546      367757 :     const char *pszAddedPathSep = "";
     547      367757 :     const char *pszAddedExtSep = "";
     548             : 
     549      367757 :     if (pszPath == nullptr)
     550       10862 :         pszPath = "";
     551      367757 :     size_t nLenPath = strlen(pszPath);
     552             : 
     553      367757 :     size_t nSuffixPos = 0;
     554      367757 :     if (STARTS_WITH_CI(pszPath, "/vsicurl/http"))
     555             :     {
     556         125 :         const char *pszQuestionMark = strchr(pszPath, '?');
     557         125 :         if (pszQuestionMark)
     558             :         {
     559           1 :             nSuffixPos = static_cast<size_t>(pszQuestionMark - pszPath);
     560           1 :             nLenPath = nSuffixPos;
     561             :         }
     562         125 :         pszAddedPathSep = "/";
     563             :     }
     564             : 
     565      367757 :     if (!CPLIsFilenameRelative(pszPath) && strcmp(pszBasename, "..") == 0)
     566             :     {
     567             :         // /a/b + .. --> /a
     568         251 :         if (pszPath[nLenPath - 1] == '\\' || pszPath[nLenPath - 1] == '/')
     569           9 :             nLenPath--;
     570         251 :         size_t nLenPathOri = nLenPath;
     571        4254 :         while (nLenPath > 0 && pszPath[nLenPath - 1] != '\\' &&
     572        4243 :                pszPath[nLenPath - 1] != '/')
     573             :         {
     574        4003 :             nLenPath--;
     575             :         }
     576         251 :         if (nLenPath == 1 && pszPath[0] == '/')
     577             :         {
     578           7 :             pszBasename = "";
     579             :         }
     580         244 :         else if ((nLenPath > 1 && pszPath[0] == '/') ||
     581          11 :                  (nLenPath > 2 && pszPath[1] == ':') ||
     582           1 :                  (nLenPath > 6 && strncmp(pszPath, "\\\\$\\", 4) == 0))
     583             :         {
     584         237 :             nLenPath--;
     585         237 :             pszBasename = "";
     586             :         }
     587             :         else
     588             :         {
     589           7 :             nLenPath = nLenPathOri;
     590           7 :             if (pszAddedPathSep[0] == 0)
     591           7 :                 pszAddedPathSep = VSIGetDirectorySeparator(pszPath);
     592             :         }
     593             :     }
     594      367494 :     else if (nLenPath > 0 && pszPath[nLenPath - 1] != '/' &&
     595      354692 :              pszPath[nLenPath - 1] != '\\')
     596             :     {
     597      354689 :         if (pszAddedPathSep[0] == 0)
     598      354593 :             pszAddedPathSep = VSIGetDirectorySeparator(pszPath);
     599             :     }
     600             : 
     601      367768 :     if (pszExtension == nullptr)
     602      217949 :         pszExtension = "";
     603      149819 :     else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
     604      137104 :         pszAddedExtSep = ".";
     605             : 
     606      367768 :     if (nLenPath >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
     607           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     608             : 
     609      367768 :     memcpy(pszStaticResult, pszPath, nLenPath);
     610      367768 :     pszStaticResult[nLenPath] = 0;
     611             : 
     612      367768 :     if (CPLStrlcat(pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >=
     613      367780 :             static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
     614      367776 :         CPLStrlcat(pszStaticResult, pszBasename, CPL_PATH_BUF_SIZE) >=
     615      367784 :             static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
     616      367774 :         CPLStrlcat(pszStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE) >=
     617      735558 :             static_cast<size_t>(CPL_PATH_BUF_SIZE) ||
     618      367787 :         CPLStrlcat(pszStaticResult, pszExtension, CPL_PATH_BUF_SIZE) >=
     619             :             static_cast<size_t>(CPL_PATH_BUF_SIZE))
     620             :     {
     621           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     622             :     }
     623             : 
     624      367783 :     if (nSuffixPos &&
     625           1 :         CPLStrlcat(pszStaticResult, pszPath + nSuffixPos, CPL_PATH_BUF_SIZE) >=
     626             :             static_cast<size_t>(CPL_PATH_BUF_SIZE))
     627             :     {
     628           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     629             :     }
     630             : 
     631      367782 :     return pszStaticResult;
     632             : }
     633             : 
     634             : /************************************************************************/
     635             : /*                          CPLFormCIFilename()                         */
     636             : /************************************************************************/
     637             : 
     638             : /**
     639             :  * Case insensitive file searching, returning full path.
     640             :  *
     641             :  * This function tries to return the path to a file regardless of
     642             :  * whether the file exactly matches the basename, and extension case, or
     643             :  * is all upper case, or all lower case.  The path is treated as case
     644             :  * sensitive.  This function is equivalent to CPLFormFilename() on
     645             :  * case insensitive file systems (like Windows).
     646             :  *
     647             :  * @param pszPath directory path to the directory containing the file.  This
     648             :  * may be relative or absolute, and may have a trailing path separator or
     649             :  * not.  May be NULL.
     650             :  *
     651             :  * @param pszBasename file basename.  May optionally have path and/or
     652             :  * extension.  May not be NULL.
     653             :  *
     654             :  * @param pszExtension file extension, optionally including the period.  May
     655             :  * be NULL.
     656             :  *
     657             :  * @return a fully formed filename in an internal static string.  Do not
     658             :  * modify or free the returned string.  The string may be destroyed by the
     659             :  * next CPL call.
     660             :  */
     661             : 
     662        4628 : const char *CPLFormCIFilename(const char *pszPath, const char *pszBasename,
     663             :                               const char *pszExtension)
     664             : 
     665             : {
     666             :     // On case insensitive filesystems, just default to CPLFormFilename().
     667        4628 :     if (!VSIIsCaseSensitiveFS(pszPath))
     668           0 :         return CPLFormFilename(pszPath, pszBasename, pszExtension);
     669             : 
     670        4628 :     const char *pszAddedExtSep = "";
     671        4628 :     size_t nLen = strlen(pszBasename) + 2;
     672             : 
     673        4628 :     if (pszExtension != nullptr)
     674        2195 :         nLen += strlen(pszExtension);
     675             : 
     676        4628 :     char *pszFilename = static_cast<char *>(VSI_MALLOC_VERBOSE(nLen));
     677        4628 :     if (pszFilename == nullptr)
     678           0 :         return "";
     679             : 
     680        4628 :     if (pszExtension == nullptr)
     681        2433 :         pszExtension = "";
     682        2195 :     else if (pszExtension[0] != '.' && strlen(pszExtension) > 0)
     683        2143 :         pszAddedExtSep = ".";
     684             : 
     685        4628 :     snprintf(pszFilename, nLen, "%s%s%s", pszBasename, pszAddedExtSep,
     686             :              pszExtension);
     687             : 
     688        4628 :     const char *pszFullPath = CPLFormFilename(pszPath, pszFilename, nullptr);
     689             :     VSIStatBufL sStatBuf;
     690        4628 :     int nStatRet = VSIStatExL(pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG);
     691        4628 :     if (nStatRet != 0)
     692             :     {
     693       55278 :         for (size_t i = 0; pszFilename[i] != '\0'; i++)
     694             :         {
     695       51136 :             pszFilename[i] = static_cast<char>(CPLToupper(pszFilename[i]));
     696             :         }
     697             : 
     698        4142 :         pszFullPath = CPLFormFilename(pszPath, pszFilename, nullptr);
     699        4142 :         nStatRet = VSIStatExL(pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG);
     700             :     }
     701             : 
     702        4628 :     if (nStatRet != 0)
     703             :     {
     704       55254 :         for (size_t i = 0; pszFilename[i] != '\0'; i++)
     705             :         {
     706       51115 :             pszFilename[i] = static_cast<char>(
     707       51115 :                 CPLTolower(static_cast<unsigned char>(pszFilename[i])));
     708             :         }
     709             : 
     710        4139 :         pszFullPath = CPLFormFilename(pszPath, pszFilename, nullptr);
     711        4139 :         nStatRet = VSIStatExL(pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG);
     712             :     }
     713             : 
     714        4628 :     if (nStatRet != 0)
     715        4131 :         pszFullPath = CPLFormFilename(pszPath, pszBasename, pszExtension);
     716             : 
     717        4628 :     CPLFree(pszFilename);
     718             : 
     719        4628 :     return pszFullPath;
     720             : }
     721             : 
     722             : /************************************************************************/
     723             : /*                     CPLProjectRelativeFilename()                     */
     724             : /************************************************************************/
     725             : 
     726             : /**
     727             :  * Find a file relative to a project file.
     728             :  *
     729             :  * Given the path to a "project" directory, and a path to a secondary file
     730             :  * referenced from that project, build a path to the secondary file
     731             :  * that the current application can use.  If the secondary path is already
     732             :  * absolute, rather than relative, then it will be returned unaltered.
     733             :  *
     734             :  * Examples:
     735             :  * <pre>
     736             :  * CPLProjectRelativeFilename("abc/def", "tmp/abc.gif") == "abc/def/tmp/abc.gif"
     737             :  * CPLProjectRelativeFilename("abc/def", "/tmp/abc.gif") == "/tmp/abc.gif"
     738             :  * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
     739             :  * CPLProjectRelativeFilename("/abc/def", "../abc.gif") == "/abc/def/../abc.gif"
     740             :  * CPLProjectRelativeFilename("C:\WIN", "abc.gif") == "C:\WIN\abc.gif"
     741             :  * </pre>
     742             :  *
     743             :  * @param pszProjectDir the directory relative to which the secondary files
     744             :  * path should be interpreted.
     745             :  * @param pszSecondaryFilename the filename (potentially with path) that
     746             :  * is to be interpreted relative to the project directory.
     747             :  *
     748             :  * @return a composed path to the secondary file.  The returned string is
     749             :  * internal and should not be altered, freed, or depending on past the next
     750             :  * CPL call.
     751             :  */
     752             : 
     753        4255 : const char *CPLProjectRelativeFilename(const char *pszProjectDir,
     754             :                                        const char *pszSecondaryFilename)
     755             : 
     756             : {
     757        4255 :     char *pszStaticResult = CPLGetStaticResult();
     758        4255 :     if (pszStaticResult == nullptr)
     759           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     760             : 
     761        4255 :     CPLAssert(!(pszProjectDir >= pszStaticResult &&
     762             :                 pszProjectDir < pszStaticResult + CPL_PATH_BUF_SIZE));
     763        4255 :     CPLAssert(!(pszSecondaryFilename >= pszStaticResult &&
     764             :                 pszSecondaryFilename < pszStaticResult + CPL_PATH_BUF_SIZE));
     765             : 
     766        4255 :     if (!CPLIsFilenameRelative(pszSecondaryFilename))
     767         451 :         return pszSecondaryFilename;
     768             : 
     769        3804 :     if (pszProjectDir == nullptr || strlen(pszProjectDir) == 0)
     770         216 :         return pszSecondaryFilename;
     771             : 
     772        3588 :     if (CPLStrlcpy(pszStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE) >=
     773             :         static_cast<size_t>(CPL_PATH_BUF_SIZE))
     774           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     775             : 
     776        3588 :     if (pszProjectDir[strlen(pszProjectDir) - 1] != '/' &&
     777        3588 :         pszProjectDir[strlen(pszProjectDir) - 1] != '\\')
     778             :     {
     779        3588 :         const char *pszAddedPathSep = VSIGetDirectorySeparator(pszProjectDir);
     780        3588 :         if (CPLStrlcat(pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >=
     781             :             static_cast<size_t>(CPL_PATH_BUF_SIZE))
     782           0 :             return CPLStaticBufferTooSmall(pszStaticResult);
     783             :     }
     784             : 
     785        3588 :     if (CPLStrlcat(pszStaticResult, pszSecondaryFilename, CPL_PATH_BUF_SIZE) >=
     786             :         static_cast<size_t>(CPL_PATH_BUF_SIZE))
     787           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     788             : 
     789        3588 :     return pszStaticResult;
     790             : }
     791             : 
     792             : /************************************************************************/
     793             : /*                       CPLIsFilenameRelative()                        */
     794             : /************************************************************************/
     795             : 
     796             : /**
     797             :  * Is filename relative or absolute?
     798             :  *
     799             :  * The test is filesystem convention agnostic.  That is it will test for
     800             :  * Unix style and windows style path conventions regardless of the actual
     801             :  * system in use.
     802             :  *
     803             :  * @param pszFilename the filename with path to test.
     804             :  *
     805             :  * @return TRUE if the filename is relative or FALSE if it is absolute.
     806             :  */
     807             : 
     808      389639 : int CPLIsFilenameRelative(const char *pszFilename)
     809             : 
     810             : {
     811      389639 :     if ((pszFilename[0] != '\0' &&
     812      378619 :          (STARTS_WITH(pszFilename + 1, ":\\") ||
     813      378586 :           STARTS_WITH(pszFilename + 1, ":/") ||
     814      378586 :           strstr(pszFilename + 1, "://")  // http://, ftp:// etc....
     815      389086 :           )) ||
     816      389086 :         STARTS_WITH(pszFilename, "\\\\?\\")  // Windows extended Length Path.
     817      389067 :         || pszFilename[0] == '\\' || pszFilename[0] == '/')
     818      266801 :         return FALSE;
     819             : 
     820      122838 :     return TRUE;
     821             : }
     822             : 
     823             : /************************************************************************/
     824             : /*                       CPLExtractRelativePath()                       */
     825             : /************************************************************************/
     826             : 
     827             : /**
     828             :  * Get relative path from directory to target file.
     829             :  *
     830             :  * Computes a relative path for pszTarget relative to pszBaseDir.
     831             :  * Currently this only works if they share a common base path.  The returned
     832             :  * path is normally into the pszTarget string.  It should only be considered
     833             :  * valid as long as pszTarget is valid or till the next call to
     834             :  * this function, whichever comes first.
     835             :  *
     836             :  * @param pszBaseDir the name of the directory relative to which the path
     837             :  * should be computed.  pszBaseDir may be NULL in which case the original
     838             :  * target is returned without relativizing.
     839             :  *
     840             :  * @param pszTarget the filename to be changed to be relative to pszBaseDir.
     841             :  *
     842             :  * @param pbGotRelative Pointer to location in which a flag is placed
     843             :  * indicating that the returned path is relative to the basename (TRUE) or
     844             :  * not (FALSE).  This pointer may be NULL if flag is not desired.
     845             :  *
     846             :  * @return an adjusted path or the original if it could not be made relative
     847             :  * to the pszBaseFile's path.
     848             :  **/
     849             : 
     850        2673 : const char *CPLExtractRelativePath(const char *pszBaseDir,
     851             :                                    const char *pszTarget, int *pbGotRelative)
     852             : 
     853             : {
     854             :     /* -------------------------------------------------------------------- */
     855             :     /*      If we don't have a basedir, then we can't relativize the path.  */
     856             :     /* -------------------------------------------------------------------- */
     857        2673 :     if (pszBaseDir == nullptr)
     858             :     {
     859           0 :         if (pbGotRelative != nullptr)
     860           0 :             *pbGotRelative = FALSE;
     861             : 
     862           0 :         return pszTarget;
     863             :     }
     864             : 
     865        2673 :     const size_t nBasePathLen = strlen(pszBaseDir);
     866             : 
     867             :     /* -------------------------------------------------------------------- */
     868             :     /*      One simple case is when the base dir is '.' and the target      */
     869             :     /*      filename is relative.                                           */
     870             :     /* -------------------------------------------------------------------- */
     871        2695 :     if ((nBasePathLen == 0 || EQUAL(pszBaseDir, ".")) &&
     872          22 :         CPLIsFilenameRelative(pszTarget))
     873             :     {
     874          22 :         if (pbGotRelative != nullptr)
     875          22 :             *pbGotRelative = TRUE;
     876             : 
     877          22 :         return pszTarget;
     878             :     }
     879             : 
     880             :     /* -------------------------------------------------------------------- */
     881             :     /*      By this point, if we don't have a base path, we can't have a    */
     882             :     /*      meaningful common prefix.                                       */
     883             :     /* -------------------------------------------------------------------- */
     884        2651 :     if (nBasePathLen == 0)
     885             :     {
     886           0 :         if (pbGotRelative != nullptr)
     887           0 :             *pbGotRelative = FALSE;
     888             : 
     889           0 :         return pszTarget;
     890             :     }
     891             : 
     892             :     /* -------------------------------------------------------------------- */
     893             :     /*      If we don't have a common path prefix, then we can't get a      */
     894             :     /*      relative path.                                                  */
     895             :     /* -------------------------------------------------------------------- */
     896        2651 :     if (!EQUALN(pszBaseDir, pszTarget, nBasePathLen) ||
     897        2218 :         (pszTarget[nBasePathLen] != '\\' && pszTarget[nBasePathLen] != '/'))
     898             :     {
     899         434 :         if (pbGotRelative != nullptr)
     900         434 :             *pbGotRelative = FALSE;
     901             : 
     902         434 :         return pszTarget;
     903             :     }
     904             : 
     905             :     /* -------------------------------------------------------------------- */
     906             :     /*      We have a relative path.  Strip it off to get a string to       */
     907             :     /*      return.                                                         */
     908             :     /* -------------------------------------------------------------------- */
     909        2217 :     if (pbGotRelative != nullptr)
     910        2148 :         *pbGotRelative = TRUE;
     911             : 
     912        2217 :     return pszTarget + nBasePathLen + 1;
     913             : }
     914             : 
     915             : /************************************************************************/
     916             : /*                            CPLCleanTrailingSlash()                   */
     917             : /************************************************************************/
     918             : 
     919             : /**
     920             :  * Remove trailing forward/backward slash from the path for UNIX/Windows resp.
     921             :  *
     922             :  * Returns a string containing the portion of the passed path string with
     923             :  * trailing slash removed. If there is no path in the passed filename
     924             :  * an empty string will be returned (not NULL).
     925             :  *
     926             :  * <pre>
     927             :  * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
     928             :  * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
     929             :  * CPLCleanTrailingSlash( "c:\\abc\\def\\" ) == "c:\\abc\\def"
     930             :  * CPLCleanTrailingSlash( "c:\\abc\\def" ) == "c:\\abc\\def"
     931             :  * CPLCleanTrailingSlash( "abc" ) == "abc"
     932             :  * </pre>
     933             :  *
     934             :  * @param pszPath the path to be cleaned up
     935             :  *
     936             :  * @return Path in an internal string which must not be freed.  The string
     937             :  * may be destroyed by the next CPL filename handling call.
     938             :  */
     939             : 
     940           9 : const char *CPLCleanTrailingSlash(const char *pszPath)
     941             : 
     942             : {
     943           9 :     char *pszStaticResult = CPLGetStaticResult();
     944           9 :     if (pszStaticResult == nullptr)
     945           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     946           9 :     CPLAssert(!(pszPath >= pszStaticResult &&
     947             :                 pszPath < pszStaticResult + CPL_PATH_BUF_SIZE));
     948             : 
     949           9 :     const size_t iPathLength = strlen(pszPath);
     950           9 :     if (iPathLength >= static_cast<size_t>(CPL_PATH_BUF_SIZE))
     951           0 :         return CPLStaticBufferTooSmall(pszStaticResult);
     952             : 
     953           9 :     CPLStrlcpy(pszStaticResult, pszPath, iPathLength + 1);
     954             : 
     955           9 :     if (iPathLength > 0 && (pszStaticResult[iPathLength - 1] == '\\' ||
     956           9 :                             pszStaticResult[iPathLength - 1] == '/'))
     957           0 :         pszStaticResult[iPathLength - 1] = '\0';
     958             : 
     959           9 :     return pszStaticResult;
     960             : }
     961             : 
     962             : /************************************************************************/
     963             : /*                       CPLCorrespondingPaths()                        */
     964             : /************************************************************************/
     965             : 
     966             : /**
     967             :  * Identify corresponding paths.
     968             :  *
     969             :  * Given a prototype old and new filename this function will attempt
     970             :  * to determine corresponding names for a set of other old filenames that
     971             :  * will rename them in a similar manner.  This correspondence assumes there
     972             :  * are two possibly kinds of renaming going on.  A change of path, and a
     973             :  * change of filename stem.
     974             :  *
     975             :  * If a consistent renaming cannot be established for all the files this
     976             :  * function will return indicating an error.
     977             :  *
     978             :  * The returned file list becomes owned by the caller and should be destroyed
     979             :  * with CSLDestroy().
     980             :  *
     981             :  * @param pszOldFilename path to old prototype file.
     982             :  * @param pszNewFilename path to new prototype file.
     983             :  * @param papszFileList list of other files associated with pszOldFilename to
     984             :  * rename similarly.
     985             :  *
     986             :  * @return a list of files corresponding to papszFileList but renamed to
     987             :  * correspond to pszNewFilename.
     988             :  */
     989             : 
     990         179 : char **CPLCorrespondingPaths(const char *pszOldFilename,
     991             :                              const char *pszNewFilename, char **papszFileList)
     992             : 
     993             : {
     994         179 :     if (CSLCount(papszFileList) == 0)
     995           0 :         return nullptr;
     996             : 
     997             :     /* -------------------------------------------------------------------- */
     998             :     /*      There is a special case for a one item list which exactly       */
     999             :     /*      matches the old name, to rename to the new name.                */
    1000             :     /* -------------------------------------------------------------------- */
    1001         347 :     if (CSLCount(papszFileList) == 1 &&
    1002         168 :         strcmp(pszOldFilename, papszFileList[0]) == 0)
    1003             :     {
    1004         168 :         return CSLAddString(nullptr, pszNewFilename);
    1005             :     }
    1006             : 
    1007          22 :     const CPLString osOldPath = CPLGetPath(pszOldFilename);
    1008          22 :     const CPLString osOldBasename = CPLGetBasename(pszOldFilename);
    1009          22 :     const CPLString osNewBasename = CPLGetBasename(pszNewFilename);
    1010             : 
    1011             :     /* -------------------------------------------------------------------- */
    1012             :     /*      If the basename is changing, verify that all source files       */
    1013             :     /*      have the same starting basename.                                */
    1014             :     /* -------------------------------------------------------------------- */
    1015          11 :     if (osOldBasename != osNewBasename)
    1016             :     {
    1017          34 :         for (int i = 0; papszFileList[i] != nullptr; i++)
    1018             :         {
    1019          24 :             if (osOldBasename == CPLGetBasename(papszFileList[i]))
    1020          16 :                 continue;
    1021             : 
    1022           8 :             const CPLString osFilePath = CPLGetPath(papszFileList[i]);
    1023           8 :             const CPLString osFileName = CPLGetFilename(papszFileList[i]);
    1024             : 
    1025           8 :             if (!EQUALN(osFileName, osOldBasename, osOldBasename.size()) ||
    1026          16 :                 !EQUAL(osFilePath, osOldPath) ||
    1027           8 :                 osFileName[osOldBasename.size()] != '.')
    1028             :             {
    1029           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1030             :                          "Unable to rename fileset due irregular basenames.");
    1031           0 :                 return nullptr;
    1032             :             }
    1033             :         }
    1034             :     }
    1035             : 
    1036             :     /* -------------------------------------------------------------------- */
    1037             :     /*      If the filename portions differs, ensure they only differ in    */
    1038             :     /*      basename.                                                       */
    1039             :     /* -------------------------------------------------------------------- */
    1040          11 :     if (osOldBasename != osNewBasename)
    1041             :     {
    1042             :         const CPLString osOldExtra =
    1043          10 :             CPLGetFilename(pszOldFilename) + osOldBasename.size();
    1044             :         const CPLString osNewExtra =
    1045          10 :             CPLGetFilename(pszNewFilename) + osNewBasename.size();
    1046             : 
    1047          10 :         if (osOldExtra != osNewExtra)
    1048             :         {
    1049           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1050             :                      "Unable to rename fileset due to irregular filename "
    1051             :                      "correspondence.");
    1052           0 :             return nullptr;
    1053             :         }
    1054             :     }
    1055             : 
    1056             :     /* -------------------------------------------------------------------- */
    1057             :     /*      Generate the new filenames.                                     */
    1058             :     /* -------------------------------------------------------------------- */
    1059          11 :     char **papszNewList = nullptr;
    1060          11 :     const CPLString osNewPath = CPLGetPath(pszNewFilename);
    1061             : 
    1062          37 :     for (int i = 0; papszFileList[i] != nullptr; i++)
    1063             :     {
    1064          52 :         const CPLString osOldFilename = CPLGetFilename(papszFileList[i]);
    1065             : 
    1066             :         const CPLString osNewFilename =
    1067          26 :             osOldBasename == osNewBasename
    1068           2 :                 ? CPLFormFilename(osNewPath, osOldFilename, nullptr)
    1069          24 :                 : CPLFormFilename(osNewPath, osNewBasename,
    1070          52 :                                   osOldFilename.c_str() + osOldBasename.size());
    1071             : 
    1072          26 :         papszNewList = CSLAddString(papszNewList, osNewFilename);
    1073             :     }
    1074             : 
    1075          11 :     return papszNewList;
    1076             : }
    1077             : 
    1078             : /************************************************************************/
    1079             : /*                      CPLGenerateTempFilename()                       */
    1080             : /************************************************************************/
    1081             : 
    1082             : /**
    1083             :  * Generate temporary file name.
    1084             :  *
    1085             :  * Returns a filename that may be used for a temporary file.  The location
    1086             :  * of the file tries to follow operating system semantics but may be
    1087             :  * forced via the CPL_TMPDIR configuration option.
    1088             :  *
    1089             :  * @param pszStem if non-NULL this will be part of the filename.
    1090             :  *
    1091             :  * @return a filename which is valid till the next CPL call in this thread.
    1092             :  */
    1093             : 
    1094        2326 : const char *CPLGenerateTempFilename(const char *pszStem)
    1095             : 
    1096             : {
    1097        2326 :     const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
    1098             : 
    1099        2326 :     if (pszDir == nullptr)
    1100        2324 :         pszDir = CPLGetConfigOption("TMPDIR", nullptr);
    1101             : 
    1102        2326 :     if (pszDir == nullptr)
    1103        2324 :         pszDir = CPLGetConfigOption("TEMP", nullptr);
    1104             : 
    1105        2326 :     if (pszDir == nullptr)
    1106        2324 :         pszDir = ".";
    1107             : 
    1108        2326 :     if (pszStem == nullptr)
    1109        2157 :         pszStem = "";
    1110             : 
    1111             :     static int nTempFileCounter = 0;
    1112        4652 :     CPLString osFilename;
    1113             :     osFilename.Printf("%s_%d_%d", pszStem, CPLGetCurrentProcessID(),
    1114        2326 :                       CPLAtomicInc(&nTempFileCounter));
    1115             : 
    1116        4652 :     return CPLFormFilename(pszDir, osFilename, nullptr);
    1117             : }
    1118             : 
    1119             : /************************************************************************/
    1120             : /*                         CPLExpandTilde()                             */
    1121             : /************************************************************************/
    1122             : 
    1123             : /**
    1124             :  * Expands ~/ at start of filename.
    1125             :  *
    1126             :  * Assumes that the HOME configuration option is defined.
    1127             :  *
    1128             :  * @param pszFilename filename potentially starting with ~/
    1129             :  *
    1130             :  * @return an expanded filename.
    1131             :  *
    1132             :  * @since GDAL 2.2
    1133             :  */
    1134             : 
    1135         188 : const char *CPLExpandTilde(const char *pszFilename)
    1136             : 
    1137             : {
    1138         188 :     if (!STARTS_WITH_CI(pszFilename, "~/"))
    1139         187 :         return pszFilename;
    1140             : 
    1141           1 :     const char *pszHome = CPLGetConfigOption("HOME", nullptr);
    1142           1 :     if (pszHome == nullptr)
    1143           0 :         return pszFilename;
    1144             : 
    1145           1 :     return CPLFormFilename(pszHome, pszFilename + 2, nullptr);
    1146             : }
    1147             : 
    1148             : /************************************************************************/
    1149             : /*                         CPLGetHomeDir()                              */
    1150             : /************************************************************************/
    1151             : 
    1152             : /**
    1153             :  * Return the path to the home directory
    1154             :  *
    1155             :  * That is the value of the USERPROFILE environment variable on Windows,
    1156             :  * or HOME on other platforms.
    1157             :  *
    1158             :  * @return the home directory, or NULL.
    1159             :  *
    1160             :  * @since GDAL 2.3
    1161             :  */
    1162             : 
    1163           0 : const char *CPLGetHomeDir()
    1164             : 
    1165             : {
    1166             : #ifdef _WIN32
    1167             :     return CPLGetConfigOption("USERPROFILE", nullptr);
    1168             : #else
    1169           0 :     return CPLGetConfigOption("HOME", nullptr);
    1170             : #endif
    1171             : }
    1172             : 
    1173             : /************************************************************************/
    1174             : /*                        CPLLaunderForFilename()                       */
    1175             : /************************************************************************/
    1176             : 
    1177             : /**
    1178             :  * Launder a string to be compatible of a filename.
    1179             :  *
    1180             :  * @param pszName The input string to launder.
    1181             :  * @param pszOutputPath The directory where the file would be created.
    1182             :  *                      Unused for now. May be NULL.
    1183             :  * @return the laundered name.
    1184             :  *
    1185             :  * @since GDAL 3.1
    1186             :  */
    1187             : 
    1188        1173 : const char *CPLLaunderForFilename(const char *pszName,
    1189             :                                   CPL_UNUSED const char *pszOutputPath)
    1190             : {
    1191        2346 :     std::string osRet(pszName);
    1192       10970 :     for (char &ch : osRet)
    1193             :     {
    1194             :         // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
    1195        9797 :         if (ch == '<' || ch == '>' || ch == ':' || ch == '"' || ch == '/' ||
    1196        9791 :             ch == '\\' || ch == '?' || ch == '*')
    1197             :         {
    1198           9 :             ch = '_';
    1199             :         }
    1200             :     }
    1201        2346 :     return CPLSPrintf("%s", osRet.c_str());
    1202             : }

Generated by: LCOV version 1.14