diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index c228a0382f..8baa254441 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -313,6 +313,9 @@ typedef enum SDL_EnumerationResult * terminate the enumeration early, and dictate the return value of the * enumeration function itself. * + * `dirname` is guaranteed to end with a path separator ('\\' on + * Windows, '/' on most other platforms). + * * \param userdata an app-controlled pointer that is passed to the callback. * \param dirname the directory that is being enumerated. * \param fname the next entry in the enumeration. diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c index 6bb905a0b8..28388517c4 100644 --- a/src/filesystem/SDL_filesystem.c +++ b/src/filesystem/SDL_filesystem.c @@ -307,7 +307,7 @@ static SDL_EnumerationResult SDLCALL GlobDirectoryCallback(void *userdata, const // !!! FIXME: and only casefold the new pieces instead of allocating and folding full paths for all of this. char *fullpath = NULL; - if (SDL_asprintf(&fullpath, "%s/%s", dirname, fname) < 0) { + if (SDL_asprintf(&fullpath, "%s%s", dirname, fname) < 0) { return SDL_ENUM_FAILURE; } @@ -417,7 +417,8 @@ char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_Glob data.enumerator = enumerator; data.getpathinfo = getpathinfo; data.fsuserdata = userdata; - data.basedirlen = SDL_strlen(path) + 1; // +1 for the '/' we'll be adding. + data.basedirlen = *path ? (SDL_strlen(path) + 1) : 0; // +1 for the '/' we'll be adding. + char **result = NULL; if (data.enumerator(path, GlobDirectoryCallback, &data, data.fsuserdata)) { diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c index 0658b8f155..c562c2aed2 100644 --- a/src/filesystem/posix/SDL_sysfsops.c +++ b/src/filesystem/posix/SDL_sysfsops.c @@ -37,25 +37,42 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata) { - SDL_EnumerationResult result = SDL_ENUM_CONTINUE; + char *pathwithsep = NULL; + int pathwithseplen = SDL_asprintf(&pathwithsep, "%s/", path); + if ((pathwithseplen == -1) || (!pathwithsep)) { + return false; + } - DIR *dir = opendir(path); + // trim down to a single path separator at the end, in case the caller added one or more. + pathwithseplen--; + while ((pathwithseplen >= 0) && (pathwithsep[pathwithseplen] == '/')) { + pathwithsep[pathwithseplen--] = '\0'; + } + + DIR *dir = opendir(pathwithsep); if (!dir) { + SDL_free(pathwithsep); return SDL_SetError("Can't open directory: %s", strerror(errno)); } + // make sure there's a path separator at the end now for the actual callback. + pathwithsep[++pathwithseplen] = '/'; + pathwithsep[++pathwithseplen] = '\0'; + + SDL_EnumerationResult result = SDL_ENUM_CONTINUE; struct dirent *ent; - while ((result == SDL_ENUM_CONTINUE) && ((ent = readdir(dir)) != NULL)) - { + while ((result == SDL_ENUM_CONTINUE) && ((ent = readdir(dir)) != NULL)) { const char *name = ent->d_name; if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) { continue; } - result = cb(userdata, path, name); + result = cb(userdata, pathwithsep, name); } closedir(dir); + SDL_free(pathwithsep); + return (result != SDL_ENUM_FAILURE); } diff --git a/src/filesystem/windows/SDL_sysfsops.c b/src/filesystem/windows/SDL_sysfsops.c index 89961c6a8f..9c48ba957b 100644 --- a/src/filesystem/windows/SDL_sysfsops.c +++ b/src/filesystem/windows/SDL_sysfsops.c @@ -34,35 +34,45 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback SDL_EnumerationResult result = SDL_ENUM_CONTINUE; if (*path == '\0') { // if empty (completely at the root), we need to enumerate drive letters. const DWORD drives = GetLogicalDrives(); - char name[3] = { 0, ':', '\0' }; + char name[] = { 0, ':', '\\', '\0' }; for (int i = 'A'; (result == SDL_ENUM_CONTINUE) && (i <= 'Z'); i++) { if (drives & (1 << (i - 'A'))) { name[0] = (char) i; - result = cb(userdata, path, name); + result = cb(userdata, "", name); } } } else { - const size_t patternlen = SDL_strlen(path) + 3; - char *pattern = (char *) SDL_malloc(patternlen); - if (!pattern) { - return false; - } - // you need a wildcard to enumerate through FindFirstFileEx(), but the wildcard is only checked in the // filename element at the end of the path string, so always tack on a "\\*" to get everything, and // also prevent any wildcards inserted by the app from being respected. - SDL_snprintf(pattern, patternlen, "%s\\*", path); - - WCHAR *wpattern = WIN_UTF8ToStringW(pattern); - SDL_free(pattern); - if (!wpattern) { + char *pattern = NULL; + int patternlen = SDL_asprintf(&pattern, "%s\\\\", path); // we'll replace that second '\\' in the trimdown. + if ((patternlen == -1) || (!pattern)) { return false; } + // trim down to a single path separator at the end, in case the caller added one or more. + patternlen--; + while ((patternlen >= 0) && ((pattern[patternlen] == '\\') || (pattern[patternlen] == '/'))) { + pattern[patternlen--] ='\0'; + } + pattern[++patternlen] = '\\'; + pattern[++patternlen] = '*'; + pattern[++patternlen] = '\0'; + + WCHAR *wpattern = WIN_UTF8ToStringW(pattern); + if (!wpattern) { + SDL_free(pattern); + return false; + } + + pattern[--patternlen] = '\0'; // chop off the '*' so we just have the dirname with a path separator. + WIN32_FIND_DATAW entw; HANDLE dir = FindFirstFileExW(wpattern, FindExInfoStandard, &entw, FindExSearchNameMatch, NULL, 0); SDL_free(wpattern); if (dir == INVALID_HANDLE_VALUE) { + SDL_free(pattern); return WIN_SetError("Failed to enumerate directory"); } @@ -79,12 +89,13 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback if (!utf8fn) { result = SDL_ENUM_FAILURE; } else { - result = cb(userdata, path, utf8fn); + result = cb(userdata, pattern, utf8fn); SDL_free(utf8fn); } } while ((result == SDL_ENUM_CONTINUE) && (FindNextFileW(dir, &entw) != 0)); FindClose(dir); + SDL_free(pattern); } return (result != SDL_ENUM_FAILURE); diff --git a/test/testfilesystem.c b/test/testfilesystem.c index 5618413730..9fe2cdb823 100644 --- a/test/testfilesystem.c +++ b/test/testfilesystem.c @@ -20,14 +20,7 @@ static SDL_EnumerationResult SDLCALL enum_callback(void *userdata, const char *o SDL_PathInfo info; char *fullpath = NULL; - /* you can use '/' for a path separator on Windows, but to make the log output look correct, we'll #ifdef this... */ - #ifdef SDL_PLATFORM_WINDOWS - const char *pathsep = "\\"; - #else - const char *pathsep = "/"; - #endif - - if (SDL_asprintf(&fullpath, "%s%s%s", origdir, *origdir ? pathsep : "", fname) < 0) { + if (SDL_asprintf(&fullpath, "%s%s", origdir, fname) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!"); return SDL_ENUM_FAILURE; }