mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-01 06:12:23 -06:00
1003 lines
35 KiB
C++
1003 lines
35 KiB
C++
/**********************************************************************
|
|
* $Id: cpl_path.cpp 27044 2014-03-16 23:41:27Z rouault $
|
|
*
|
|
* Project: CPL - Common Portability Library
|
|
* Purpose: Portable filename/path parsing, and forming ala "Glob API".
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
**********************************************************************
|
|
* Copyright (c) 1999, Frank Warmerdam
|
|
* Copyright (c) 2008-2012, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_string.h"
|
|
#include "cpl_multiproc.h"
|
|
|
|
CPL_CVSID("$Id: cpl_path.cpp 27044 2014-03-16 23:41:27Z rouault $");
|
|
|
|
|
|
/* should be size of larged possible filename */
|
|
#define CPL_PATH_BUF_SIZE 2048
|
|
#define CPL_PATH_BUF_COUNT 10
|
|
|
|
#if defined(WIN32) || defined(WIN32CE)
|
|
#define SEP_CHAR '\\'
|
|
#define SEP_STRING "\\"
|
|
#else
|
|
#define SEP_CHAR '/'
|
|
#define SEP_STRING "/"
|
|
#endif
|
|
|
|
static const char* CPLStaticBufferTooSmall(char *pszStaticResult)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Destination buffer too small");
|
|
strcpy( pszStaticResult, "" );
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGetStaticResult() */
|
|
/************************************************************************/
|
|
|
|
static char *CPLGetStaticResult()
|
|
|
|
{
|
|
char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_PATHBUF );
|
|
if( pachBufRingInfo == NULL )
|
|
{
|
|
pachBufRingInfo = (char *) CPLCalloc(1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT);
|
|
CPLSetTLS( CTLS_PATHBUF, pachBufRingInfo, TRUE );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Work out which string in the "ring" we want to use this */
|
|
/* time. */
|
|
/* -------------------------------------------------------------------- */
|
|
int *pnBufIndex = (int *) pachBufRingInfo;
|
|
int nOffset = sizeof(int) + *pnBufIndex * CPL_PATH_BUF_SIZE;
|
|
char *pachBuffer = pachBufRingInfo + nOffset;
|
|
|
|
*pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT;
|
|
|
|
return pachBuffer;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLFindFilenameStart() */
|
|
/************************************************************************/
|
|
|
|
static int CPLFindFilenameStart( const char * pszFilename )
|
|
|
|
{
|
|
size_t iFileStart;
|
|
|
|
for( iFileStart = strlen(pszFilename);
|
|
iFileStart > 0
|
|
&& pszFilename[iFileStart-1] != '/'
|
|
&& pszFilename[iFileStart-1] != '\\';
|
|
iFileStart-- ) {}
|
|
|
|
return (int)iFileStart;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGetPath() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Extract directory path portion of filename.
|
|
*
|
|
* Returns a string containing the directory path portion of the passed
|
|
* filename. If there is no path in the passed filename an empty string
|
|
* will be returned (not NULL).
|
|
*
|
|
* <pre>
|
|
* CPLGetPath( "abc/def.xyz" ) == "abc"
|
|
* CPLGetPath( "/abc/def/" ) == "/abc/def"
|
|
* CPLGetPath( "/" ) == "/"
|
|
* CPLGetPath( "/abc/def" ) == "/abc"
|
|
* CPLGetPath( "abc" ) == ""
|
|
* </pre>
|
|
*
|
|
* @param pszFilename the filename potentially including a path.
|
|
*
|
|
* @return Path in an internal string which must not be freed. The string
|
|
* may be destroyed by the next CPL filename handling call. The returned
|
|
* will generally not contain a trailing path separator.
|
|
*/
|
|
|
|
const char *CPLGetPath( const char *pszFilename )
|
|
|
|
{
|
|
int iFileStart = CPLFindFilenameStart(pszFilename);
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
|
|
if( iFileStart >= CPL_PATH_BUF_SIZE )
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
if( iFileStart == 0 )
|
|
{
|
|
strcpy( pszStaticResult, "" );
|
|
return pszStaticResult;
|
|
}
|
|
|
|
CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 );
|
|
|
|
if( iFileStart > 1
|
|
&& (pszStaticResult[iFileStart-1] == '/'
|
|
|| pszStaticResult[iFileStart-1] == '\\') )
|
|
pszStaticResult[iFileStart-1] = '\0';
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGetDirname() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Extract directory path portion of filename.
|
|
*
|
|
* Returns a string containing the directory path portion of the passed
|
|
* filename. If there is no path in the passed filename the dot will be
|
|
* returned. It is the only difference from CPLGetPath().
|
|
*
|
|
* <pre>
|
|
* CPLGetDirname( "abc/def.xyz" ) == "abc"
|
|
* CPLGetDirname( "/abc/def/" ) == "/abc/def"
|
|
* CPLGetDirname( "/" ) == "/"
|
|
* CPLGetDirname( "/abc/def" ) == "/abc"
|
|
* CPLGetDirname( "abc" ) == "."
|
|
* </pre>
|
|
*
|
|
* @param pszFilename the filename potentially including a path.
|
|
*
|
|
* @return Path in an internal string which must not be freed. The string
|
|
* may be destroyed by the next CPL filename handling call. The returned
|
|
* will generally not contain a trailing path separator.
|
|
*/
|
|
|
|
const char *CPLGetDirname( const char *pszFilename )
|
|
|
|
{
|
|
int iFileStart = CPLFindFilenameStart(pszFilename);
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
|
|
if( iFileStart >= CPL_PATH_BUF_SIZE )
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
if( iFileStart == 0 )
|
|
{
|
|
strcpy( pszStaticResult, "." );
|
|
return pszStaticResult;
|
|
}
|
|
|
|
CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 );
|
|
|
|
if( iFileStart > 1
|
|
&& (pszStaticResult[iFileStart-1] == '/'
|
|
|| pszStaticResult[iFileStart-1] == '\\') )
|
|
pszStaticResult[iFileStart-1] = '\0';
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGetFilename() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Extract non-directory portion of filename.
|
|
*
|
|
* Returns a string containing the bare filename portion of the passed
|
|
* filename. If there is no filename (passed value ends in trailing directory
|
|
* separator) an empty string is returned.
|
|
*
|
|
* <pre>
|
|
* CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
|
|
* CPLGetFilename( "/abc/def/" ) == ""
|
|
* CPLGetFilename( "abc/def" ) == "def"
|
|
* </pre>
|
|
*
|
|
* @param pszFullFilename the full filename potentially including a path.
|
|
*
|
|
* @return just the non-directory portion of the path (points back into original string).
|
|
*/
|
|
|
|
const char *CPLGetFilename( const char *pszFullFilename )
|
|
|
|
{
|
|
int iFileStart = CPLFindFilenameStart( pszFullFilename );
|
|
|
|
return pszFullFilename + iFileStart;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGetBasename() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Extract basename (non-directory, non-extension) portion of filename.
|
|
*
|
|
* Returns a string containing the file basename portion of the passed
|
|
* name. If there is no basename (passed value ends in trailing directory
|
|
* separator, or filename starts with a dot) an empty string is returned.
|
|
*
|
|
* <pre>
|
|
* CPLGetBasename( "abc/def.xyz" ) == "def"
|
|
* CPLGetBasename( "abc/def" ) == "def"
|
|
* CPLGetBasename( "abc/def/" ) == ""
|
|
* </pre>
|
|
*
|
|
* @param pszFullFilename the full filename potentially including a path.
|
|
*
|
|
* @return just the non-directory, non-extension portion of the path in
|
|
* an internal string which must not be freed. The string
|
|
* may be destroyed by the next CPL filename handling call.
|
|
*/
|
|
|
|
const char *CPLGetBasename( const char *pszFullFilename )
|
|
|
|
{
|
|
size_t iFileStart = CPLFindFilenameStart( pszFullFilename );
|
|
size_t iExtStart, nLength;
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
|
|
CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
for( iExtStart = strlen(pszFullFilename);
|
|
iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
|
|
iExtStart-- ) {}
|
|
|
|
if( iExtStart == iFileStart )
|
|
iExtStart = strlen(pszFullFilename);
|
|
|
|
nLength = iExtStart - iFileStart;
|
|
|
|
if (nLength >= CPL_PATH_BUF_SIZE)
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
CPLStrlcpy( pszStaticResult, pszFullFilename + iFileStart, nLength + 1 );
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLGetExtension() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Extract filename extension from full filename.
|
|
*
|
|
* Returns a string containing the extention portion of the passed
|
|
* name. If there is no extension (the filename has no dot) an empty string
|
|
* is returned. The returned extension will not include the period.
|
|
*
|
|
* <pre>
|
|
* CPLGetExtension( "abc/def.xyz" ) == "xyz"
|
|
* CPLGetExtension( "abc/def" ) == ""
|
|
* </pre>
|
|
*
|
|
* @param pszFullFilename the full filename potentially including a path.
|
|
*
|
|
* @return just the extension portion of the path in
|
|
* an internal string which must not be freed. The string
|
|
* may be destroyed by the next CPL filename handling call.
|
|
*/
|
|
|
|
const char *CPLGetExtension( const char *pszFullFilename )
|
|
|
|
{
|
|
size_t iFileStart = CPLFindFilenameStart( pszFullFilename );
|
|
size_t iExtStart;
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
|
|
CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
for( iExtStart = strlen(pszFullFilename);
|
|
iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
|
|
iExtStart-- ) {}
|
|
|
|
if( iExtStart == iFileStart )
|
|
iExtStart = strlen(pszFullFilename)-1;
|
|
|
|
if (CPLStrlcpy( pszStaticResult, pszFullFilename+iExtStart+1, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGetCurrentDir() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Get the current working directory name.
|
|
*
|
|
* @return a pointer to buffer, containing current working directory path
|
|
* or NULL in case of error. User is responsible to free that buffer
|
|
* after usage with CPLFree() function.
|
|
* If HAVE_GETCWD macro is not defined, the function returns NULL.
|
|
**/
|
|
|
|
char *CPLGetCurrentDir()
|
|
|
|
{
|
|
size_t nPathMax;
|
|
char *pszDirPath;
|
|
|
|
# ifdef _MAX_PATH
|
|
nPathMax = _MAX_PATH;
|
|
# elif PATH_MAX
|
|
nPathMax = PATH_MAX;
|
|
# else
|
|
nPathMax = 8192;
|
|
# endif
|
|
|
|
pszDirPath = (char*)CPLMalloc( nPathMax );
|
|
if ( !pszDirPath )
|
|
return NULL;
|
|
|
|
#ifdef HAVE_GETCWD
|
|
return getcwd( pszDirPath, nPathMax );
|
|
#else
|
|
return NULL;
|
|
#endif /* HAVE_GETCWD */
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLResetExtension() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Replace the extension with the provided one.
|
|
*
|
|
* @param pszPath the input path, this string is not altered.
|
|
* @param pszExt the new extension to apply to the given path.
|
|
*
|
|
* @return an altered filename with the new extension. Do not
|
|
* modify or free the returned string. The string may be destroyed by the
|
|
* next CPL call.
|
|
*/
|
|
|
|
const char *CPLResetExtension( const char *pszPath, const char *pszExt )
|
|
|
|
{
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
size_t i;
|
|
|
|
CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* First, try and strip off any existing extension. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
if (*pszStaticResult)
|
|
{
|
|
for( i = strlen(pszStaticResult) - 1; i > 0; i-- )
|
|
{
|
|
if( pszStaticResult[i] == '.' )
|
|
{
|
|
pszStaticResult[i] = '\0';
|
|
break;
|
|
}
|
|
|
|
if( pszStaticResult[i] == '/' || pszStaticResult[i] == '\\'
|
|
|| pszStaticResult[i] == ':' )
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Append the new extension. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (CPLStrlcat( pszStaticResult, ".", CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
|
|
CPLStrlcat( pszStaticResult, pszExt, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLFormFilename() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Build a full file path from a passed path, file basename and extension.
|
|
*
|
|
* The path, and extension are optional. The basename may in fact contain
|
|
* an extension if desired.
|
|
*
|
|
* <pre>
|
|
* CPLFormFilename("abc/xyz","def", ".dat" ) == "abc/xyz/def.dat"
|
|
* CPLFormFilename(NULL,"def", NULL ) == "def"
|
|
* CPLFormFilename(NULL,"abc/def.dat", NULL ) == "abc/def.dat"
|
|
* CPLFormFilename("/abc/xyz/","def.dat", NULL ) == "/abc/xyz/def.dat"
|
|
* </pre>
|
|
*
|
|
* @param pszPath directory path to the directory containing the file. This
|
|
* may be relative or absolute, and may have a trailing path separator or
|
|
* not. May be NULL.
|
|
*
|
|
* @param pszBasename file basename. May optionally have path and/or
|
|
* extension. Must *NOT* be NULL.
|
|
*
|
|
* @param pszExtension file extension, optionally including the period. May
|
|
* be NULL.
|
|
*
|
|
* @return a fully formed filename in an internal static string. Do not
|
|
* modify or free the returned string. The string may be destroyed by the
|
|
* next CPL call.
|
|
*/
|
|
|
|
const char *CPLFormFilename( const char * pszPath,
|
|
const char * pszBasename,
|
|
const char * pszExtension )
|
|
|
|
{
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
const char *pszAddedPathSep = "";
|
|
const char *pszAddedExtSep = "";
|
|
|
|
CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
CPLAssert( ! (pszBasename >= pszStaticResult && pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
if( pszBasename[0] == '.' && pszBasename[1] == '/' )
|
|
pszBasename += 2;
|
|
|
|
if( pszPath == NULL )
|
|
pszPath = "";
|
|
else if( strlen(pszPath) > 0
|
|
&& pszPath[strlen(pszPath)-1] != '/'
|
|
&& pszPath[strlen(pszPath)-1] != '\\' )
|
|
{
|
|
/* FIXME? would be better to ask the filesystems what they */
|
|
/* prefer as directory separator */
|
|
if (strncmp(pszPath, "/vsicurl/", 9) == 0)
|
|
pszAddedPathSep = "/";
|
|
else if (strncmp(pszPath, "/vsizip/", 8) == 0)
|
|
pszAddedPathSep = "/";
|
|
else
|
|
pszAddedPathSep = SEP_STRING;
|
|
}
|
|
|
|
if( pszExtension == NULL )
|
|
pszExtension = "";
|
|
else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
|
|
pszAddedExtSep = ".";
|
|
|
|
if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
|
|
CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
|
|
CPLStrlcat( pszStaticResult, pszBasename, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
|
|
CPLStrlcat( pszStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE ||
|
|
CPLStrlcat( pszStaticResult, pszExtension, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE)
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLFormCIFilename() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Case insensitive file searching, returing full path.
|
|
*
|
|
* This function tries to return the path to a file regardless of
|
|
* whether the file exactly matches the basename, and extension case, or
|
|
* is all upper case, or all lower case. The path is treated as case
|
|
* sensitive. This function is equivelent to CPLFormFilename() on
|
|
* case insensitive file systems (like Windows).
|
|
*
|
|
* @param pszPath directory path to the directory containing the file. This
|
|
* may be relative or absolute, and may have a trailing path separator or
|
|
* not. May be NULL.
|
|
*
|
|
* @param pszBasename file basename. May optionally have path and/or
|
|
* extension. May not be NULL.
|
|
*
|
|
* @param pszExtension file extension, optionally including the period. May
|
|
* be NULL.
|
|
*
|
|
* @return a fully formed filename in an internal static string. Do not
|
|
* modify or free the returned string. The string may be destroyed by the
|
|
* next CPL call.
|
|
*/
|
|
|
|
const char *CPLFormCIFilename( const char * pszPath,
|
|
const char * pszBasename,
|
|
const char * pszExtension )
|
|
|
|
{
|
|
// On case insensitive filesystems, just default to
|
|
// CPLFormFilename()
|
|
if( !VSIIsCaseSensitiveFS(pszPath) )
|
|
return CPLFormFilename( pszPath, pszBasename, pszExtension );
|
|
|
|
const char *pszAddedExtSep = "";
|
|
char *pszFilename;
|
|
const char *pszFullPath;
|
|
int nLen = strlen(pszBasename)+2, i;
|
|
VSIStatBufL sStatBuf;
|
|
int nStatRet;
|
|
|
|
if( pszExtension != NULL )
|
|
nLen += strlen(pszExtension);
|
|
|
|
pszFilename = (char *) CPLMalloc(nLen);
|
|
|
|
if( pszExtension == NULL )
|
|
pszExtension = "";
|
|
else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
|
|
pszAddedExtSep = ".";
|
|
|
|
sprintf( pszFilename, "%s%s%s",
|
|
pszBasename, pszAddedExtSep, pszExtension );
|
|
|
|
pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
|
|
nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
|
|
if( nStatRet != 0 )
|
|
{
|
|
for( i = 0; pszFilename[i] != '\0'; i++ )
|
|
{
|
|
if( islower(pszFilename[i]) )
|
|
pszFilename[i] = (char) toupper(pszFilename[i]);
|
|
}
|
|
|
|
pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
|
|
nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
|
|
}
|
|
|
|
if( nStatRet != 0 )
|
|
{
|
|
for( i = 0; pszFilename[i] != '\0'; i++ )
|
|
{
|
|
if( isupper(pszFilename[i]) )
|
|
pszFilename[i] = (char) tolower(pszFilename[i]);
|
|
}
|
|
|
|
pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
|
|
nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG );
|
|
}
|
|
|
|
if( nStatRet != 0 )
|
|
pszFullPath = CPLFormFilename( pszPath, pszBasename, pszExtension );
|
|
|
|
CPLFree( pszFilename );
|
|
|
|
return pszFullPath;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLProjectRelativeFilename() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Find a file relative to a project file.
|
|
*
|
|
* Given the path to a "project" directory, and a path to a secondary file
|
|
* referenced from that project, build a path to the secondary file
|
|
* that the current application can use. If the secondary path is already
|
|
* absolute, rather than relative, then it will be returned unaltered.
|
|
*
|
|
* Examples:
|
|
* <pre>
|
|
* CPLProjectRelativeFilename("abc/def","tmp/abc.gif") == "abc/def/tmp/abc.gif"
|
|
* CPLProjectRelativeFilename("abc/def","/tmp/abc.gif") == "/tmp/abc.gif"
|
|
* CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
|
|
* CPLProjectRelativeFilename("/abc/def","../abc.gif") == "/abc/def/../abc.gif"
|
|
* CPLProjectRelativeFilename("C:\WIN","abc.gif") == "C:\WIN\abc.gif"
|
|
* </pre>
|
|
*
|
|
* @param pszProjectDir the directory relative to which the secondary files
|
|
* path should be interpreted.
|
|
* @param pszSecondaryFilename the filename (potentially with path) that
|
|
* is to be interpreted relative to the project directory.
|
|
*
|
|
* @return a composed path to the secondary file. The returned string is
|
|
* internal and should not be altered, freed, or depending on past the next
|
|
* CPL call.
|
|
*/
|
|
|
|
const char *CPLProjectRelativeFilename( const char *pszProjectDir,
|
|
const char *pszSecondaryFilename )
|
|
|
|
{
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
|
|
CPLAssert( ! (pszProjectDir >= pszStaticResult && pszProjectDir < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
CPLAssert( ! (pszSecondaryFilename >= pszStaticResult && pszSecondaryFilename < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
if( !CPLIsFilenameRelative( pszSecondaryFilename ) )
|
|
return pszSecondaryFilename;
|
|
|
|
if( pszProjectDir == NULL || strlen(pszProjectDir) == 0 )
|
|
return pszSecondaryFilename;
|
|
|
|
if (CPLStrlcpy( pszStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
|
|
goto error;
|
|
|
|
if( pszProjectDir[strlen(pszProjectDir)-1] != '/'
|
|
&& pszProjectDir[strlen(pszProjectDir)-1] != '\\' )
|
|
{
|
|
/* FIXME? would be better to ask the filesystems what they */
|
|
/* prefer as directory separator */
|
|
const char* pszAddedPathSep;
|
|
if (strncmp(pszStaticResult, "/vsicurl/", 9) == 0)
|
|
pszAddedPathSep = "/";
|
|
else
|
|
pszAddedPathSep = SEP_STRING;
|
|
if (CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
|
|
goto error;
|
|
}
|
|
|
|
if (CPLStrlcat( pszStaticResult, pszSecondaryFilename, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE)
|
|
goto error;
|
|
|
|
return pszStaticResult;
|
|
error:
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLIsFilenameRelative() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Is filename relative or absolute?
|
|
*
|
|
* The test is filesystem convention agnostic. That is it will test for
|
|
* Unix style and windows style path conventions regardless of the actual
|
|
* system in use.
|
|
*
|
|
* @param pszFilename the filename with path to test.
|
|
*
|
|
* @return TRUE if the filename is relative or FALSE if it is absolute.
|
|
*/
|
|
|
|
int CPLIsFilenameRelative( const char *pszFilename )
|
|
|
|
{
|
|
if( (strlen(pszFilename) > 2
|
|
&& (strncmp(pszFilename+1,":\\",2) == 0
|
|
|| strncmp(pszFilename+1,":/",2) == 0))
|
|
|| pszFilename[0] == '\\'
|
|
|| pszFilename[0] == '/' )
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLExtractRelativePath() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Get relative path from directory to target file.
|
|
*
|
|
* Computes a relative path for pszTarget relative to pszBaseDir.
|
|
* Currently this only works if they share a common base path. The returned
|
|
* path is normally into the pszTarget string. It should only be considered
|
|
* valid as long as pszTarget is valid or till the next call to
|
|
* this function, whichever comes first.
|
|
*
|
|
* @param pszBaseDir the name of the directory relative to which the path
|
|
* should be computed. pszBaseDir may be NULL in which case the original
|
|
* target is returned without relitivizing.
|
|
*
|
|
* @param pszTarget the filename to be changed to be relative to pszBaseDir.
|
|
*
|
|
* @param pbGotRelative Pointer to location in which a flag is placed
|
|
* indicating that the returned path is relative to the basename (TRUE) or
|
|
* not (FALSE). This pointer may be NULL if flag is not desired.
|
|
*
|
|
* @return an adjusted path or the original if it could not be made relative
|
|
* to the pszBaseFile's path.
|
|
**/
|
|
|
|
const char *CPLExtractRelativePath( const char *pszBaseDir,
|
|
const char *pszTarget,
|
|
int *pbGotRelative )
|
|
|
|
{
|
|
size_t nBasePathLen;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we don't have a basedir, then we can't relativize the path. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszBaseDir == NULL )
|
|
{
|
|
if( pbGotRelative != NULL )
|
|
*pbGotRelative = FALSE;
|
|
|
|
return pszTarget;
|
|
}
|
|
|
|
nBasePathLen = strlen(pszBaseDir);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* One simple case is when the base dir is '.' and the target */
|
|
/* filename is relative. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( (nBasePathLen == 0 || EQUAL(pszBaseDir,"."))
|
|
&& CPLIsFilenameRelative(pszTarget) )
|
|
{
|
|
if( pbGotRelative != NULL )
|
|
*pbGotRelative = TRUE;
|
|
|
|
return pszTarget;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* By this point, if we don't have a base path, we can't have a */
|
|
/* meaningful common prefix. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBasePathLen == 0 )
|
|
{
|
|
if( pbGotRelative != NULL )
|
|
*pbGotRelative = FALSE;
|
|
|
|
return pszTarget;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we don't have a common path prefix, then we can't get a */
|
|
/* relative path. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !EQUALN(pszBaseDir,pszTarget,nBasePathLen)
|
|
|| (pszTarget[nBasePathLen] != '\\'
|
|
&& pszTarget[nBasePathLen] != '/') )
|
|
{
|
|
if( pbGotRelative != NULL )
|
|
*pbGotRelative = FALSE;
|
|
|
|
return pszTarget;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We have a relative path. Strip it off to get a string to */
|
|
/* return. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pbGotRelative != NULL )
|
|
*pbGotRelative = TRUE;
|
|
|
|
return pszTarget + nBasePathLen + 1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLCleanTrailingSlash() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Remove trailing forward/backward slash from the path for unix/windows resp.
|
|
*
|
|
* Returns a string containing the portion of the passed path string with
|
|
* trailing slash removed. If there is no path in the passed filename
|
|
* an empty string will be returned (not NULL).
|
|
*
|
|
* <pre>
|
|
* CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
|
|
* CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
|
|
* CPLCleanTrailingSlash( "c:\abc\def\" ) == "c:\abc\def"
|
|
* CPLCleanTrailingSlash( "c:\abc\def" ) == "c:\abc\def"
|
|
* CPLCleanTrailingSlash( "abc" ) == "abc"
|
|
* </pre>
|
|
*
|
|
* @param pszPath the path to be cleaned up
|
|
*
|
|
* @return Path in an internal string which must not be freed. The string
|
|
* may be destroyed by the next CPL filename handling call.
|
|
*/
|
|
|
|
const char *CPLCleanTrailingSlash( const char *pszPath )
|
|
|
|
{
|
|
char *pszStaticResult = CPLGetStaticResult();
|
|
int iPathLength = strlen(pszPath);
|
|
|
|
CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) );
|
|
|
|
if (iPathLength >= CPL_PATH_BUF_SIZE)
|
|
return CPLStaticBufferTooSmall(pszStaticResult);
|
|
|
|
CPLStrlcpy( pszStaticResult, pszPath, iPathLength+1 );
|
|
|
|
if( iPathLength > 0
|
|
&& (pszStaticResult[iPathLength-1] == '\\'
|
|
|| pszStaticResult[iPathLength-1] == '/'))
|
|
pszStaticResult[iPathLength-1] = '\0';
|
|
|
|
return pszStaticResult;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLCorrespondingPaths() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Identify corresponding paths.
|
|
*
|
|
* Given a prototype old and new filename this function will attempt
|
|
* to determine corresponding names for a set of other old filenames that
|
|
* will rename them in a similar manner. This correspondance assumes there
|
|
* are two possibly kinds of renaming going on. A change of path, and a
|
|
* change of filename stem.
|
|
*
|
|
* If a consistent renaming cannot be established for all the files this
|
|
* function will return indicating an error.
|
|
*
|
|
* The returned file list becomes owned by the caller and should be destroyed
|
|
* with CSLDestroy().
|
|
*
|
|
* @param pszOldFilename path to old prototype file.
|
|
* @param pszNewFilename path to new prototype file.
|
|
* @param papszFileList list of other files associated with pszOldFilename to
|
|
* rename similarly.
|
|
*
|
|
* @return a list of files corresponding to papszFileList but renamed to
|
|
* correspond to pszNewFilename.
|
|
*/
|
|
|
|
char **CPLCorrespondingPaths( const char *pszOldFilename,
|
|
const char *pszNewFilename,
|
|
char **papszFileList )
|
|
|
|
{
|
|
CPLString osOldPath = CPLGetPath( pszOldFilename );
|
|
CPLString osNewPath = CPLGetPath( pszNewFilename );
|
|
CPLString osOldBasename = CPLGetBasename( pszOldFilename );
|
|
CPLString osNewBasename = CPLGetBasename( pszNewFilename );
|
|
int i;
|
|
|
|
if( CSLCount(papszFileList) == 0 )
|
|
return NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* There is a special case for a one item list which exactly */
|
|
/* matches the old name, to rename to the new name. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( CSLCount(papszFileList) == 1
|
|
&& strcmp(pszOldFilename,papszFileList[0]) == 0 )
|
|
{
|
|
return CSLAddString( NULL, pszNewFilename );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the basename is changing, verify that all source files */
|
|
/* have the same starting basename. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( osOldBasename != osNewBasename )
|
|
{
|
|
for( i=0; papszFileList[i] != NULL; i++ )
|
|
{
|
|
if( osOldBasename == CPLGetBasename( papszFileList[i] ) )
|
|
continue;
|
|
|
|
CPLString osFilePath, osFileName;
|
|
|
|
osFilePath = CPLGetPath( papszFileList[i] );
|
|
osFileName = CPLGetFilename( papszFileList[i] );
|
|
|
|
if( !EQUALN(osFileName,osOldBasename,osOldBasename.size())
|
|
|| !EQUAL(osFilePath,osOldPath)
|
|
|| osFileName[osOldBasename.size()] != '.' )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unable to rename fileset due irregular basenames.");
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the filename portions differs, ensure they only differ in */
|
|
/* basename. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( osOldBasename != osNewBasename )
|
|
{
|
|
CPLString osOldExtra = CPLGetFilename(pszOldFilename)
|
|
+ strlen(osOldBasename);
|
|
CPLString osNewExtra = CPLGetFilename(pszNewFilename)
|
|
+ strlen(osNewBasename);
|
|
|
|
if( osOldExtra != osNewExtra )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unable to rename fileset due to irregular filename correspondence." );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Generate the new filenames. */
|
|
/* -------------------------------------------------------------------- */
|
|
char **papszNewList = NULL;
|
|
|
|
for( i=0; papszFileList[i] != NULL; i++ )
|
|
{
|
|
CPLString osNewFilename;
|
|
CPLString osOldFilename = CPLGetFilename( papszFileList[i] );
|
|
|
|
if( osOldBasename == osNewBasename )
|
|
osNewFilename =
|
|
CPLFormFilename( osNewPath, osOldFilename, NULL );
|
|
else
|
|
osNewFilename =
|
|
CPLFormFilename( osNewPath, osNewBasename,
|
|
osOldFilename.c_str()+strlen(osOldBasename));
|
|
|
|
papszNewList = CSLAddString( papszNewList, osNewFilename );
|
|
}
|
|
|
|
return papszNewList;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLGenerateTempFilename() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Generate temporary file name.
|
|
*
|
|
* Returns a filename that may be used for a temporary file. The location
|
|
* of the file tries to follow operating system semantics but may be
|
|
* forced via the CPL_TMPDIR configuration option.
|
|
*
|
|
* @param pszStem if non-NULL this will be part of the filename.
|
|
*
|
|
* @return a filename which is valid till the next CPL call in this thread.
|
|
*/
|
|
|
|
const char *CPLGenerateTempFilename( const char *pszStem )
|
|
|
|
{
|
|
const char *pszDir = CPLGetConfigOption( "CPL_TMPDIR", NULL );
|
|
static volatile int nTempFileCounter = 0;
|
|
|
|
if( pszDir == NULL )
|
|
pszDir = CPLGetConfigOption( "TMPDIR", NULL );
|
|
|
|
if( pszDir == NULL )
|
|
pszDir = CPLGetConfigOption( "TEMP", NULL );
|
|
|
|
if( pszDir == NULL )
|
|
pszDir = ".";
|
|
|
|
CPLString osFilename;
|
|
|
|
if( pszStem == NULL )
|
|
pszStem = "";
|
|
|
|
osFilename.Printf( "%s%u_%d", pszStem,
|
|
(int) CPLGetPID(), nTempFileCounter++ );
|
|
|
|
return CPLFormFilename( pszDir, osFilename, NULL );
|
|
}
|