From aa77e49de7c49af8ac7fda0ac1b09e2aea571262 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Tue, 21 Jul 2020 05:16:55 +0200 Subject: [PATCH] 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. --- neo/idlib/Str.cpp | 85 ++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/neo/idlib/Str.cpp b/neo/idlib/Str.cpp index b37e1a9e..4ccfb967 100644 --- a/neo/idlib/Str.cpp +++ b/neo/idlib/Str.cpp @@ -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 ) {