www.pudn.com > mitab.rar > cpl_path.cpp
/**********************************************************************
* $Id: cpl_path.cpp,v 1.17 2004/08/13 15:59:39 warmerda Exp $
*
* Project: CPL - Common Portability Library
* Purpose: Portable filename/path parsing, and forming ala "Glob API".
* Author: Frank Warmerdam, warmerda@home.com
*
**********************************************************************
* Copyright (c) 1999, Frank Warmerdam
*
* 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.
**********************************************************************
*
* $Log: cpl_path.cpp,v $
* Revision 1.17 2004/08/13 15:59:39 warmerda
* Fixed bug with CPLExtractRelativePath.
*
* Revision 1.16 2004/08/11 18:41:46 warmerda
* added CPLExtractRelativePath
*
* Revision 1.15 2004/07/10 12:22:37 dron
* Use locale aware character classification functions in CPLFormCIFilename().
*
* Revision 1.14 2003/05/28 19:22:38 warmerda
* fixed docs
*
* Revision 1.13 2003/04/04 09:11:35 dron
* strcpy() and strcat() replaced by strncpy() and strncat().
* A lot of assertion on string sizes added.
*
* Revision 1.12 2002/12/13 06:14:17 warmerda
* fixed bug with IsRelative function
*
* Revision 1.11 2002/12/13 06:00:54 warmerda
* added CPLProjectRelativeFilename() and CPLIsFilenameRelative()
*
* Revision 1.10 2002/08/15 09:23:24 dron
* Added CPLGetDirname() function
*
* Revision 1.9 2001/08/30 21:20:49 warmerda
* expand tabs
*
* Revision 1.8 2001/07/18 04:00:49 warmerda
* added CPL_CVSID
*
* Revision 1.7 2001/05/12 19:20:55 warmerda
* Fixed documentation of CPLGetExtension().
*
* Revision 1.6 2001/03/16 22:15:08 warmerda
* added CPLResetExtension
*
* Revision 1.5 2001/02/24 01:53:57 warmerda
* Added CPLFormCIFilename()
*
* Revision 1.4 2001/01/19 21:18:25 warmerda
* expanded tabs
*
* Revision 1.3 2000/01/26 17:53:36 warmerda
* Fixed CPLGetExtension() for filenames with no extension.
*
* Revision 1.2 2000/01/24 19:32:59 warmerda
* Fixed CPLGetExtension() to not include the dot.
*
* Revision 1.1 1999/10/14 19:23:39 warmerda
* New
*
**********************************************************************/
#include "cpl_conv.h"
#include "cpl_string.h"
CPL_CVSID("$Id: cpl_path.cpp,v 1.17 2004/08/13 15:59:39 warmerda Exp $");
/* should be size of larged possible filename */
#define CPL_PATH_BUF_SIZE 2048
static char szStaticResult[CPL_PATH_BUF_SIZE];
#ifdef WIN32
#define SEP_CHAR '\\'
#define SEP_STRING "\\"
#else
#define SEP_CHAR '/'
#define SEP_STRING "/"
#endif
/************************************************************************/
/* CPLFindFilenameStart() */
/************************************************************************/
static int CPLFindFilenameStart( const char * pszFilename )
{
int iFileStart;
for( iFileStart = strlen(pszFilename);
iFileStart > 0
&& pszFilename[iFileStart-1] != '/'
&& pszFilename[iFileStart-1] != '\\';
iFileStart-- ) {}
return 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).
*
*
* CPLGetPath( "abc/def.xyz" ) == "abc"
* CPLGetPath( "/abc/def/" ) == "/abc/def"
* CPLGetPath( "/" ) == "/"
* CPLGetPath( "/abc/def" ) == "/abc"
* CPLGetPath( "abc" ) == ""
*
*
* @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);
CPLAssert( iFileStart < CPL_PATH_BUF_SIZE );
if( iFileStart == 0 )
{
strcpy( szStaticResult, "" );
return szStaticResult;
}
strncpy( szStaticResult, pszFilename, iFileStart );
szStaticResult[iFileStart] = '\0';
if( iFileStart > 1
&& (szStaticResult[iFileStart-1] == '/'
|| szStaticResult[iFileStart-1] == '\\') )
szStaticResult[iFileStart-1] = '\0';
return szStaticResult;
}
/************************************************************************/
/* 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().
*
*
* CPLGetDirname( "abc/def.xyz" ) == "abc"
* CPLGetDirname( "/abc/def/" ) == "/abc/def"
* CPLGetDirname( "/" ) == "/"
* CPLGetDirname( "/abc/def" ) == "/abc"
* CPLGetDirname( "abc" ) == "."
*
*
* @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);
CPLAssert( iFileStart < CPL_PATH_BUF_SIZE );
if( iFileStart == 0 )
{
strcpy( szStaticResult, "." );
return szStaticResult;
}
strncpy( szStaticResult, pszFilename, iFileStart );
szStaticResult[iFileStart] = '\0';
if( iFileStart > 1
&& (szStaticResult[iFileStart-1] == '/'
|| szStaticResult[iFileStart-1] == '\\') )
szStaticResult[iFileStart-1] = '\0';
return szStaticResult;
}
/************************************************************************/
/* 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.
*
*
* CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
* CPLGetFilename( "/abc/def/" ) == ""
* CPLGetFilename( "abc/def" ) == "def"
*
*
* @param pszFullFilename the full filename potentially including a path.
*
* @return just the non-directory 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 *CPLGetFilename( const char *pszFullFilename )
{
int iFileStart = CPLFindFilenameStart( pszFullFilename );
strncpy( szStaticResult, pszFullFilename + iFileStart, CPL_PATH_BUF_SIZE );
szStaticResult[CPL_PATH_BUF_SIZE - 1] = '\0';
return szStaticResult;
}
/************************************************************************/
/* 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.
*
*
* CPLGetBasename( "abc/def.xyz" ) == "def"
* CPLGetBasename( "abc/def" ) == "def"
* CPLGetBasename( "abc/def/" ) == ""
*
*
* @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 )
{
int iFileStart = CPLFindFilenameStart( pszFullFilename );
int iExtStart, nLength;
for( iExtStart = strlen(pszFullFilename);
iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
iExtStart-- ) {}
if( iExtStart == iFileStart )
iExtStart = strlen(pszFullFilename);
nLength = iExtStart - iFileStart;
CPLAssert( nLength < CPL_PATH_BUF_SIZE );
strncpy( szStaticResult, pszFullFilename + iFileStart, nLength );
szStaticResult[nLength] = '\0';
return szStaticResult;
}
/************************************************************************/
/* 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.
*
*
* CPLGetExtension( "abc/def.xyz" ) == "xyz"
* CPLGetExtension( "abc/def" ) == ""
*
*
* @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 )
{
int iFileStart = CPLFindFilenameStart( pszFullFilename );
int iExtStart;
for( iExtStart = strlen(pszFullFilename);
iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
iExtStart-- ) {}
if( iExtStart == iFileStart )
iExtStart = strlen(pszFullFilename)-1;
strncpy( szStaticResult, pszFullFilename+iExtStart+1, CPL_PATH_BUF_SIZE );
szStaticResult[CPL_PATH_BUF_SIZE - 1] = '\0';
return szStaticResult;
}
/************************************************************************/
/* 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 )
{
int i;
/* -------------------------------------------------------------------- */
/* First, try and strip off any existing extension. */
/* -------------------------------------------------------------------- */
strncpy( szStaticResult, pszPath, CPL_PATH_BUF_SIZE );
szStaticResult[CPL_PATH_BUF_SIZE - 1] = '\0';
for( i = strlen(szStaticResult) - 1; i > 0; i-- )
{
if( szStaticResult[i] == '.' )
{
szStaticResult[i] = '\0';
break;
}
if( szStaticResult[i] == '/' || szStaticResult[i] == '\\'
|| szStaticResult[i] == ':' )
break;
}
/* -------------------------------------------------------------------- */
/* Append the new extension. */
/* -------------------------------------------------------------------- */
CPLAssert( strlen(pszExt) + 2 < CPL_PATH_BUF_SIZE );
strcat( szStaticResult, "." );
strcat( szStaticResult, pszExt );
return szStaticResult;
}
/************************************************************************/
/* 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.
*
*
* 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"
*
*
* @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 *CPLFormFilename( const char * pszPath,
const char * pszBasename,
const char * pszExtension )
{
const char *pszAddedPathSep = "";
const char *pszAddedExtSep = "";
if( pszPath == NULL )
pszPath = "";
else if( strlen(pszPath) > 0
&& pszPath[strlen(pszPath)-1] != '/'
&& pszPath[strlen(pszPath)-1] != '\\' )
pszAddedPathSep = SEP_STRING;
if( pszExtension == NULL )
pszExtension = "";
else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 )
pszAddedExtSep = ".";
CPLAssert( strlen(pszPath) + strlen(pszAddedPathSep) +
strlen(pszBasename) + strlen(pszAddedExtSep) +
strlen(pszExtension) + 1 < CPL_PATH_BUF_SIZE );
strncpy( szStaticResult, pszPath, CPL_PATH_BUF_SIZE );
strncat( szStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE);
strncat( szStaticResult, pszBasename, CPL_PATH_BUF_SIZE);
strncat( szStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE);
strncat( szStaticResult, pszExtension, CPL_PATH_BUF_SIZE);
szStaticResult[CPL_PATH_BUF_SIZE - 1] = '\0';
return szStaticResult;
}
/************************************************************************/
/* 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 )
{
#ifdef WIN32
return CPLFormFilename( pszPath, pszBasename, pszExtension );
#else
const char *pszAddedExtSep = "";
char *pszFilename;
const char *pszFullPath;
int nLen = strlen(pszBasename)+2, i;
FILE *fp;
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 );
fp = VSIFOpen( pszFullPath, "r" );
if( fp == NULL )
{
for( i = 0; pszFilename[i] != '\0'; i++ )
{
if( islower(pszFilename[i]) )
pszFilename[i] = toupper(pszFilename[i]);
}
pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
fp = VSIFOpen( pszFullPath, "r" );
}
if( fp == NULL )
{
for( i = 0; pszFilename[i] != '\0'; i++ )
{
if( isupper(pszFilename[i]) )
pszFilename[i] = tolower(pszFilename[i]);
}
pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL );
fp = VSIFOpen( pszFullPath, "r" );
}
if( fp != NULL )
VSIFClose( fp );
else
pszFullPath = CPLFormFilename( pszPath, pszBasename, pszExtension );
CPLFree( pszFilename );
return pszFullPath;
#endif
}
/************************************************************************/
/* 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:
*
* 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"
*
*
* @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 )
{
if( !CPLIsFilenameRelative( pszSecondaryFilename ) )
return pszSecondaryFilename;
if( pszProjectDir == NULL || strlen(pszProjectDir) == 0 )
return pszSecondaryFilename;
strncpy( szStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE );
szStaticResult[CPL_PATH_BUF_SIZE - 1] = '\0';
if( pszProjectDir[strlen(pszProjectDir)-1] != '/'
&& pszProjectDir[strlen(pszProjectDir)-1] != '\\' )
{
CPLAssert( strlen(SEP_STRING) + 1 < CPL_PATH_BUF_SIZE );
strcat( szStaticResult, SEP_STRING );
}
CPLAssert( strlen(pszSecondaryFilename) + 1 < CPL_PATH_BUF_SIZE );
strcat( szStaticResult, pszSecondaryFilename );
return szStaticResult;
}
/************************************************************************/
/* 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)
|| 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 )
{
int 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 where neither file has a path. We return */
/* the original target filename and it is relative. */
/* -------------------------------------------------------------------- */
const char *pszTargetPath = CPLGetPath(pszTarget);
if( (nBasePathLen == 0 || EQUAL(pszBaseDir,"."))
&& (strlen(pszTargetPath) == 0 || EQUAL(pszTargetPath,".")) )
{
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;
}