How do you extract the base path from pathname in C?
Are there any functions built into the C language or the C-Runtime L
On Windows there is _splitpath.
#include <Windows.h>
#include <tchar.h>
// Use your own error codes here
#define SUCCESS 0L
#define FAILURE_NULL_ARGUMENT 1L
#define FAILURE_API_CALL 2L
#define FAILURE_INSUFFICIENT_BUFFER 3L
DWORD GetBasePathFromPathName( LPCTSTR szPathName,
LPTSTR szBasePath,
DWORD dwBasePathSize )
{
TCHAR szDrive[_MAX_DRIVE] = { 0 };
TCHAR szDir[_MAX_DIR] = { 0 };
TCHAR szFname[_MAX_FNAME] = { 0 };
TCHAR szExt[_MAX_EXT] = { 0 };
size_t PathLength;
DWORD dwReturnCode;
// Parameter validation
if( szPathName == NULL || szBasePath == NULL )
{
return FAILURE_NULL_ARGUMENT;
}
// Split the path into it's components
dwReturnCode = _tsplitpath_s( szPathName, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFname, _MAX_FNAME, szExt, _MAX_EXT );
if( dwReturnCode != 0 )
{
_ftprintf( stderr, TEXT("Error splitting path. _tsplitpath_s returned %d.\n"), dwReturnCode );
return FAILURE_API_CALL;
}
// Check that the provided buffer is large enough to store the results and a terminal null character
PathLength = _tcslen( szDrive ) + _tcslen( szDir );
if( ( PathLength + sizeof( TCHAR ) ) > dwBasePathSize )
{
_ftprintf( stderr, TEXT("Insufficient buffer. Required %d. Provided: %d\n"), PathLength, dwBasePathSize );
return FAILURE_INSUFFICIENT_BUFFER;
}
// Copy the szDrive and szDir into the provide buffer to form the basepath
if( ( dwReturnCode = _tcscpy_s( szBasePath, dwBasePathSize, szDrive ) ) != 0 )
{
_ftprintf( stderr, TEXT("Error copying string. _tcscpy_s returned %d\n"), dwReturnCode );
return FAILURE_API_CALL;
}
if( ( dwReturnCode = _tcscat_s( szBasePath, dwBasePathSize, szDir ) ) != 0 )
{
_ftprintf( stderr, TEXT("Error copying string. _tcscat_s returned %d\n"), dwReturnCode );
return FAILURE_API_CALL;
}
return SUCCESS;
}
I think the best solution on Windows is to use _splitpath as was suggested, to use something like basename on Linux (more on that here).
That said, since someone has already suggested implementing my own (and since I had already done it while I was waiting for an answer), here is what I came up with. It is not cross-platform and it does not check for /valid/ paths or expand short or relative path names.
// Retrieves the pathpath from a pathname.
//
// Returns: SUCCESS if the basepath is present and successfully copied to the p_base_path buffer
// FAILURE_NULL_ARGUMENT if any arguments are NULL
// FAILURE_INVALID_ARGUMENTS if either buffer size is less than 1
// FAILURE_BUFFER_TOO_SMALL if the p_basepath buffer is too small
// FAILURE_INVALID_PATH if the p_pathname doesn't have a path (e.g. C:, calc.exe, ?qwa)
// FAILURE_API_CALL if there is an error from the underlying API calls
int get_base_path_from_pathname( const char* const p_pathname,
size_t pathname_size,
char* const p_basepath,
size_t basepath_size );
int get_base_path_from_pathname( const char* const p_pathname,
size_t pathname_size,
char* const p_basepath,
size_t basepath_size )
{
char* p_end_of_path;
size_t path_length;
int return_code;
// Parameter Validation
if( p_pathname == NULL || p_basepath == NULL ) { return FAILURE_NULL_ARGUMENT; }
if( pathname_size < 1 || basepath_size < 1 ) { return FAILURE_INVALID_ARGUMENTS; }
// Returns a pointer to the last occurrence of \ in p_pathname or NULL if it is not found
p_end_of_path = strrchr( p_pathname, '\\' );
if( p_end_of_path == NULL )
{
// There is no path part
return FAILURE_INVALID_PATH;
}
else
{
path_length = (size_t)( p_end_of_path - p_pathname + 1 );
// Do some sanity checks on the length
if( path_length < 1 ) { return FAILURE_INVALID_PATH; }
if( ( path_length + 1 ) > basepath_size ) { return FAILURE_BUFFER_TOO_SMALL; }
// Copy the base path into the out variable
if( strncpy( p_basepath, p_pathname, path_length ) != 0 ) { return FAILURE_API_CALL; }
p_basepath[path_length] = '\0';
}
return SUCCESS;
}