Fix handling of paths with dots in dir names, fix #299, #301

idStr::StripFileExtension() (and SetFileExtension() which uses it) and
others didn't work correctly if there was a dot in a directory name,
because they just searched from last to first char for '.', so if the
current filename didn't have an extension to cut off, they'd just cut
off at any other '.' they found.
So D:\dev\doom3.data\base\maps\bla could turn into D:\dev\doom3
(or, for SetFileExtension(), D:\dev\doom3.map)

While at it, I made most of the idStr code that explicitly checked for
'\\' and '/' (and maybe ':' for AROS) use a little
"bool isDirSeparator(int c)" function so we don't have the #ifdefs
for different platforms all over the place.
This commit is contained in:
Daniel Gibson 2020-09-06 04:42:33 +02:00
parent 541ba91e61
commit 5e299aa5d4

View file

@ -753,6 +753,26 @@ idStr &idStr::SetFileExtension( const char *extension ) {
return *this;
}
// DG: helper-function that returns true if the character c is a directory separator
// on the current platform
static ID_INLINE bool isDirSeparator( int c )
{
if ( c == '/' ) {
return true;
}
#ifdef _WIN32
if ( c == '\\' ) {
return true;
}
#elif defined(__AROS__)
if ( c == ':' ) {
return true;
}
#endif
return false;
}
// DG end
/*
============
idStr::StripFileExtension
@ -762,6 +782,10 @@ idStr &idStr::StripFileExtension( void ) {
int i;
for ( i = len-1; i >= 0; i-- ) {
// DG: we're at a directory separator, nothing to strip at filename
if ( isDirSeparator( data[i] ) ) {
break;
} // DG end
if ( data[i] == '.' ) {
data[i] = '\0';
len = i;
@ -778,7 +802,9 @@ idStr::StripAbsoluteFileExtension
*/
idStr &idStr::StripAbsoluteFileExtension( void ) {
int i;
// FIXME DG: seems like this is unused, but it probably doesn't do what's expected
// (if you wanna strip .tar.gz this will fail with dots in path,
// if you indeed wanna strip the first dot in *path* (even in some directory) this is right)
for ( i = 0; i < len; i++ ) {
if ( data[i] == '.' ) {
data[i] = '\0';
@ -800,6 +826,10 @@ idStr &idStr::DefaultFileExtension( const char *extension ) {
// do nothing if the string already has an extension
for ( i = len-1; i >= 0; i-- ) {
// DG: we're at a directory separator, there was no file extension
if ( isDirSeparator( data[i] ) ) {
break;
} // DG end
if ( data[i] == '.' ) {
return *this;
}
@ -817,11 +847,7 @@ idStr::DefaultPath
==================
*/
idStr &idStr::DefaultPath( const char *basepath ) {
#if defined(__AROS__)
if ( ( ( *this )[ 0 ] == '/' ) || ( ( *this )[ 0 ] == '\\' ) || ( ( *this )[ 0 ] == ':' ) ) {
#else
if ( ( ( *this )[ 0 ] == '/' ) || ( ( *this )[ 0 ] == '\\' ) ) {
#endif
if ( isDirSeparator( ( *this )[ 0 ] ) ) {
// absolute path location
return *this;
}
@ -844,19 +870,12 @@ void idStr::AppendPath( const char *text ) {
EnsureAlloced( len + strlen( text ) + 2 );
if ( pos ) {
#if defined(__AROS__)
if (( data[ pos-1 ] != '/' ) || ( data[ pos-1 ] != ':' )) {
#else
if ( data[ pos-1 ] != '/' ) {
#endif
if ( !isDirSeparator( data[ pos-1 ] ) ) {
data[ pos++ ] = '/';
}
}
#if defined(__AROS__)
if (( text[i] == '/' ) || ( text[i] == ':' )) {
#else
if ( text[i] == '/' ) {
#endif
if ( isDirSeparator( text[ i ] ) ) {
i++;
}
@ -881,11 +900,7 @@ idStr &idStr::StripFilename( void ) {
int pos;
pos = Length() - 1;
#if defined(__AROS__)
while( ( pos > 0 ) && ( ( *this )[ pos ] != '/' ) && ( ( *this )[ pos ] != '\\' ) && ( ( *this )[ pos ] != ':' ) ) {
#else
while( ( pos > 0 ) && ( ( *this )[ pos ] != '/' ) && ( ( *this )[ pos ] != '\\' ) ) {
#endif
while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos ] ) ) {
pos--;
}
@ -906,11 +921,7 @@ idStr &idStr::StripPath( void ) {
int pos;
pos = Length();
#if defined(__AROS__)
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) && ( ( *this )[ pos - 1 ] != ':' ) ) {
#else
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
#endif
while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
pos--;
}
@ -930,11 +941,7 @@ void idStr::ExtractFilePath( idStr &dest ) const {
// back up until a \ or the start
//
pos = Length();
#if defined(__AROS__)
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) && ( ( *this )[ pos - 1 ] != ':' ) ) {
#else
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
#endif
while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
pos--;
}
@ -953,11 +960,7 @@ void idStr::ExtractFileName( idStr &dest ) const {
// back up until a \ or the start
//
pos = Length() - 1;
#if defined(__AROS__)
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) && ( ( *this )[ pos - 1 ] != ':' ) ) {
#else
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
#endif
while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
pos--;
}
@ -977,11 +980,7 @@ void idStr::ExtractFileBase( idStr &dest ) const {
// back up until a \ or the start
//
pos = Length() - 1;
#if defined(__AROS__)
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) && ( ( *this )[ pos - 1 ] != ':' ) ) {
#else
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) {
#endif
while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
pos--;
}
@ -1007,6 +1006,10 @@ void idStr::ExtractFileExtension( idStr &dest ) const {
pos = Length() - 1;
while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '.' ) ) {
pos--;
if( isDirSeparator( ( *this )[ pos ] ) ) { // DG: check for directory separator
// no extension in the whole filename
dest.Empty();
} // DG end
}
if ( !pos ) {