/* ================= Sys_ListFiles_r Internal function to filesystem. Conventions apply: - files should hold at least MAX_LISTED_FILES - *count_p must be initialized in range [0, MAX_LISTED_FILES - 1] - depth must be 0 on the first call ================= */ void Sys_ListFiles_r(const char *path, const char *filter, unsigned flags, size_t baselen, int *count_p, void **files, int depth) { struct dirent *ent; DIR *dir; struct stat st; char fullpath[MAX_OSPATH]; char *name; size_t len; void *info; if ((dir = opendir(path)) == NULL) { return; } while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') { continue; // ignore dotfiles } len = Q_concat(fullpath, sizeof(fullpath), path, "/", ent->d_name, NULL); if (len >= sizeof(fullpath)) { continue; } st.st_mode = 0; #ifdef _DIRENT_HAVE_D_TYPE // try to avoid stat() if possible if (!(flags & FS_SEARCH_EXTRAINFO) && ent->d_type != DT_UNKNOWN && ent->d_type != DT_LNK) { st.st_mode = DTTOIF(ent->d_type); } #endif if (st.st_mode == 0 && stat(fullpath, &st) == -1) { continue; } // pattern search implies recursive search if ((flags & FS_SEARCH_BYFILTER) && S_ISDIR(st.st_mode) && depth < MAX_LISTED_DEPTH) { Sys_ListFiles_r(fullpath, filter, flags, baselen, count_p, files, depth + 1); // re-check count if (*count_p >= MAX_LISTED_FILES) { break; } } // check type if (flags & FS_SEARCH_DIRSONLY) { if (!S_ISDIR(st.st_mode)) { continue; } } else { if (!S_ISREG(st.st_mode)) { continue; } } // check filter if (filter) { if (flags & FS_SEARCH_BYFILTER) { if (!FS_WildCmp(filter, fullpath + baselen)) { continue; } } else { if (!FS_ExtCmp(filter, ent->d_name)) { continue; } } } // strip path if (flags & FS_SEARCH_SAVEPATH) { name = fullpath + baselen; } else { name = ent->d_name; } // strip extension if (flags & FS_SEARCH_STRIPEXT) { *COM_FileExtension(name) = 0; if (!*name) { continue; } } // copy info off if (flags & FS_SEARCH_EXTRAINFO) { info = FS_CopyInfo(name, st.st_size, st.st_ctime, st.st_mtime); } else { info = FS_CopyString(name); } files[(*count_p)++] = info; if (*count_p >= MAX_LISTED_FILES) { break; } } closedir(dir); }
/* ================= Sys_ListFiles_r Internal function to filesystem. Conventions apply: - files should hold at least MAX_LISTED_FILES - *count_p must be initialized in range [0, MAX_LISTED_FILES - 1] - depth must be 0 on the first call ================= */ void Sys_ListFiles_r(const char *path, const char *filter, unsigned flags, size_t baselen, int *count_p, void **files, int depth) { WIN32_FIND_DATAA data; HANDLE handle; char fullpath[MAX_OSPATH], *name; size_t pathlen, len; unsigned mask; void *info; // optimize single extension search if (!(flags & FS_SEARCH_BYFILTER) && filter && !strchr(filter, ';')) { if (*filter == '.') { filter++; } len = Q_concat(fullpath, sizeof(fullpath), path, "\\*.", filter, NULL); filter = NULL; // do not check it later } else { len = Q_concat(fullpath, sizeof(fullpath), path, "\\*", NULL); } if (len >= sizeof(fullpath)) { return; } // format path to windows style // done on the first run only if (!depth) { FS_ReplaceSeparators(fullpath, '\\'); } handle = FindFirstFileA(fullpath, &data); if (handle == INVALID_HANDLE_VALUE) { return; } // make it point right after the slash pathlen = strlen(path) + 1; do { if (!strcmp(data.cFileName, ".") || !strcmp(data.cFileName, "..")) { continue; // ignore special entries } // construct full path len = strlen(data.cFileName); if (pathlen + len >= sizeof(fullpath)) { continue; } memcpy(fullpath + pathlen, data.cFileName, len + 1); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { mask = FS_SEARCH_DIRSONLY; } else { mask = 0; } // pattern search implies recursive search if ((flags & FS_SEARCH_BYFILTER) && mask && depth < MAX_LISTED_DEPTH) { Sys_ListFiles_r(fullpath, filter, flags, baselen, count_p, files, depth + 1); // re-check count if (*count_p >= MAX_LISTED_FILES) { break; } } // check type if ((flags & FS_SEARCH_DIRSONLY) != mask) { continue; } // check filter if (filter) { if (flags & FS_SEARCH_BYFILTER) { if (!FS_WildCmp(filter, fullpath + baselen)) { continue; } } else { if (!FS_ExtCmp(filter, data.cFileName)) { continue; } } } // strip path if (flags & FS_SEARCH_SAVEPATH) { name = fullpath + baselen; } else { name = data.cFileName; } // reformat it back to quake filesystem style FS_ReplaceSeparators(name, '/'); // strip extension if (flags & FS_SEARCH_STRIPEXT) { *COM_FileExtension(name) = 0; if (!*name) { continue; } } // copy info off if (flags & FS_SEARCH_EXTRAINFO) { info = copy_info(name, &data); } else { info = FS_CopyString(name); } files[(*count_p)++] = info; } while (*count_p < MAX_LISTED_FILES && FindNextFileA(handle, &data) != FALSE); FindClose(handle); }