diff --git a/compiler/src/dmd/common/file.d b/compiler/src/dmd/common/file.d index 98ee13e031b0..52da6ee07925 100644 --- a/compiler/src/dmd/common/file.d +++ b/compiler/src/dmd/common/file.d @@ -14,15 +14,23 @@ module dmd.common.file; +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; +import core.stdc.limits; + import core.stdc.errno : errno; import core.stdc.stdio : fprintf, remove, rename, stderr; import core.stdc.stdlib; import core.stdc.string : strerror, strlen, memcpy; import dmd.common.smallbuffer; +import dmd.root.filename; +import dmd.root.rmem; version (Windows) { + import core.stdc.wchar_; import core.sys.windows.winbase; import core.sys.windows.winnls : CP_ACP; import core.sys.windows.winnt; @@ -32,6 +40,7 @@ version (Windows) } else version (Posix) { + import core.sys.posix.dirent; import core.sys.posix.fcntl; import core.sys.posix.sys.mman; import core.sys.posix.sys.stat; @@ -587,3 +596,163 @@ private auto ref fakePure(F)(scope F fun) pure mixin("alias PureFun = " ~ F.stringof ~ " pure;"); return (cast(PureFun) fun)(); } + +/*********************************** + * Recursively search all the directories and files under dir_path + * for files that match one of the extensions in exts[]. + * Pass the matches to sink. + * Params: + * dir_path = root of directories to search + * exts = array of filename extensions to match + * recurse = go into subdirectories + * filenameSink = accepts the resulting matches + * Returns: + * true for failed to open the directory + */ +bool findFiles(const char* dir_path, const char[][] exts, bool recurse, void delegate(const(char)[]) nothrow filenameSink) +{ + enum log = false; + if (log) printf("findFiles() dir_path: %s\n", dir_path); + version (Windows) + { + debug + enum BufLength = 10; // trigger any reallocation bugs + else + enum BufLength = 100; + char[BufLength + 1] buf = void; + char* fullPath = buf.ptr; + size_t fullPathLength = BufLength; + + // fullPath = dir_path \ *.* + const dir_pathLength = strlen(dir_path); + auto count = dir_pathLength + 1 + 3; + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + memcpy(fullPath, dir_path, dir_pathLength); + strcpy(fullPath + dir_pathLength, "\\*.*".ptr); + + if (log) printf("fullPath: %s\n", fullPath); + + WIN32_FIND_DATAW ffd = void; + HANDLE hFind = fullPath[0 .. strlen(fullPath)].extendedPathThen!(p => FindFirstFileW(p.ptr, &ffd)); + if (hFind == INVALID_HANDLE_VALUE) + return true; + + do + { + if (log) wprintf("ffd.cFileName: %s\n", ffd.cFileName.ptr); + if (ffd.cFileName[0] == 0) + continue; // ignore + + if (ffd.cFileName[0] == '.') + continue; // ignore files that start with a ., also ignore . and .. directories + + const(char)[] name = toNarrowStringz(ffd.cFileName[0 .. wcslen(ffd.cFileName.ptr)], null); + if (log) printf("name: %s\n", name.ptr); + + // fullPath = dir_path \ name.ptr + count = dir_pathLength + 1 + name.length; + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + strcpy(fullPath + dir_pathLength + 1, name.ptr); + + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (recurse) + findFiles(fullPath, exts, recurse, filenameSink); + } + else + { + const(char)[] nameExt = FileName.ext(name); + foreach (ext; exts[]) + { + if (nameExt == ext) + { + if (log) printf("adding %s\n", fullPath); + filenameSink(fullPath[0 .. count]); + } + } + } + mem.xfree(cast(void*)name.ptr); + + } while (FindNextFileW(hFind, &ffd) != 0); + + if (fullPath != buf.ptr) + mem.xfree(fullPath); + FindClose(hFind); + if (log) printf("findFiles() exit\n"); + return false; + } + else version (Posix) + { + DIR* dir = opendir(dir_path); + if (!dir) + return true; + + debug + enum BufLength = 10; // trigger any reallocation bugs + else + enum BufLength = 100; + char[BufLength + 1] buf = void; + char* fullPath = buf.ptr; + size_t fullPathLength = BufLength; + + dirent* entry; + while ((entry = readdir(dir)) != null) + { + //printf("entry: %s\n", entry.d_name.ptr); + if (entry.d_name[0] == '.') + continue; // ignore files that start with a . + + // fullPath = dir_path / entry.d_name.ptr + const dir_pathLength = strlen(dir_path); + const count = dir_pathLength + 1 + strlen(entry.d_name.ptr); + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + memcpy(fullPath, dir_path, dir_pathLength); + fullPath[dir_pathLength] = '/'; + strcpy(fullPath + dir_pathLength + 1, entry.d_name.ptr); + + stat_t statbuf; + if (lstat(fullPath, &statbuf) == -1) + continue; + + const(char)[] name = entry.d_name.ptr[0 .. strlen(entry.d_name.ptr)]; // convert to D string + if (!name.length) + continue; // ignore + + if (S_ISDIR(statbuf.st_mode)) + { + if (recurse && !(name == "." || name == "..")) + findFiles(fullPath, exts, recurse, filenameSink); + } + else if (S_ISREG(statbuf.st_mode)) + { + foreach (ext; exts) + { + if (FileName.ext(name) == ext) + { + //printf("%s\n", fullPath); + filenameSink(fullPath[0 .. count]); + } + } + } + } + + if (fullPath != buf.ptr) + mem.xfree(fullPath); + closedir(dir); + return false; + } + else + static assert(0); +}