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

Generated by: LCOV version 1.14