rcore: fix GetFileNameWithoutExt returning wrong value on repeated calls

GetFileNameWithoutExt returns a pointer into a single static buffer,
so two consecutive calls return two pointers that alias the same
address. Any use of both results (snprintf, separate variables, etc.)
silently reads whichever path was written last.

Mirror the rotating-buffer idiom TextFormat uses in rtext.c (and the
rcore.c !SUPPORT_MODULE_RTEXT fallback): MAX_FILENAME_BUFFERS (default
4, overridable) slots with a rotating index. Same lifetime contract
as TextFormat. No API/ABI change.

Drive-by: fixes the pre-existing fileNameLenght -> fileNameLength
typo in the same function.
This commit is contained in:
shyraptor 2026-04-13 23:44:43 +02:00
parent 019cc889fc
commit c4ba2a2b6f
2 changed files with 21 additions and 10 deletions

View File

@ -1152,7 +1152,7 @@ RLAPI int GetFileLength(const char *fileName); // Get file
RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png')
RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string
RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string)
RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses rotating static strings)
RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string)
RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string)
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)

View File

@ -2503,31 +2503,42 @@ const char *GetFileName(const char *filePath)
return fileName + 1;
}
// Get filename string without extension (uses static string)
// Get filename string without extension (uses rotating static strings)
// WARNING: String returned will expire after this function is called MAX_FILENAME_BUFFERS times
const char *GetFileNameWithoutExt(const char *filePath)
{
#define MAX_FILENAME_LENGTH 256
#ifndef MAX_FILENAME_BUFFERS
#define MAX_FILENAME_BUFFERS 4 // Maximum number of static buffers for filename without extension
#endif
static char fileName[MAX_FILENAME_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAME_LENGTH);
// Create an array of buffers so strings don't expire until MAX_FILENAME_BUFFERS invocations
static char fileName[MAX_FILENAME_BUFFERS][MAX_FILENAME_LENGTH] = { 0 };
static int index = 0;
char *currentBuffer = fileName[index];
memset(currentBuffer, 0, MAX_FILENAME_LENGTH); // Clear buffer before using
if (filePath != NULL)
{
strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
int fileNameLenght = (int)strlen(fileName); // Get size in bytes
strncpy(currentBuffer, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
int fileNameLength = (int)strlen(currentBuffer); // Get size in bytes
for (int i = fileNameLenght; i > 0; i--) // Reverse search '.'
for (int i = fileNameLength; i > 0; i--) // Reverse search '.'
{
if (fileName[i] == '.')
if (currentBuffer[i] == '.')
{
// NOTE: Break on first '.' found
fileName[i] = '\0';
currentBuffer[i] = '\0';
break;
}
}
index += 1; // Move to next buffer for next function call
if (index >= MAX_FILENAME_BUFFERS) index = 0;
}
return fileName;
return currentBuffer;
}
// Get directory for a given filePath