Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -1100,38 +1100,51 @@ /// @name Iterators /// @{ -/// directory_entry - A single entry in a directory. Caches the status either -/// from the result of the iteration syscall, or the first time status is -/// called. +/// directory_entry - A single entry in a directory. class directory_entry { + // FIXME: different platforms make different information available "for free" + // when traversing a directory. The design of this class wraps most of the + // information in basic_file_status, so on platforms where we can't populate + // that whole structure, callers end up paying for a stat(). + // std::filesystem::directory_entry may be a better model. std::string Path; - bool FollowSymlinks; - basic_file_status Status; + file_type Type; // Most platforms can provide this. + bool FollowSymlinks; // Affects the behavior of status(). + basic_file_status Status; // If available. public: - explicit directory_entry(const Twine &path, bool follow_symlinks = true, - basic_file_status st = basic_file_status()) - : Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} + explicit directory_entry(const Twine &Path, bool FollowSymlinks = true, + file_type Type = file_type::type_unknown, + basic_file_status Status = basic_file_status()) + : Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks), + Status(Status) {} directory_entry() = default; - void assign(const Twine &path, basic_file_status st = basic_file_status()) { - Path = path.str(); - Status = st; - } - - void replace_filename(const Twine &filename, - basic_file_status st = basic_file_status()); + void replace_filename(const Twine &Filename, file_type Type, + basic_file_status Status = basic_file_status()); const std::string &path() const { return Path; } + // Get basic information about entry file (a subset of fs::status()). + // On most platforms this is a stat() call. + // On windows the information was already retrieved from the directory. ErrorOr status() const; + // Get the type of this file. + // On most platforms (Linux/Mac/Windows/BSD), this was already retrieved. + // On some platforms (e.g. Solaris) this is a stat() call. + file_type type() const { + if (Type != file_type::type_unknown) + return Type; + auto S = status(); + return S ? S->type() : file_type::type_unknown; + } - bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; } - bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); } - bool operator< (const directory_entry& rhs) const; - bool operator<=(const directory_entry& rhs) const; - bool operator> (const directory_entry& rhs) const; - bool operator>=(const directory_entry& rhs) const; + bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; } + bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); } + bool operator< (const directory_entry& RHS) const; + bool operator<=(const directory_entry& RHS) const; + bool operator> (const directory_entry& RHS) const; + bool operator>=(const directory_entry& RHS) const; }; namespace detail { Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -1085,12 +1085,13 @@ return std::error_code(); } -void directory_entry::replace_filename(const Twine &filename, - basic_file_status st) { - SmallString<128> path = path::parent_path(Path); - path::append(path, filename); - Path = path.str(); - Status = st; +void directory_entry::replace_filename(const Twine &Filename, file_type Type, + basic_file_status Status) { + SmallString<128> PathStr = path::parent_path(Path); + path::append(PathStr, Filename); + this->Path = PathStr.str(); + this->Type = Type; + this->Status = Status; } ErrorOr getPermissions(const Twine &Path) { Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -523,38 +523,40 @@ llvm::sys::path::append(Path, Storage); } +static file_type typeForMode(mode_t Mode) { + if (S_ISDIR(Mode)) + return file_type::directory_file; + else if (S_ISREG(Mode)) + return file_type::regular_file; + else if (S_ISBLK(Mode)) + return file_type::block_file; + else if (S_ISCHR(Mode)) + return file_type::character_file; + else if (S_ISFIFO(Mode)) + return file_type::fifo_file; + else if (S_ISSOCK(Mode)) + return file_type::socket_file; + else if (S_ISLNK(Mode)) + return file_type::symlink_file; + return file_type::type_unknown; +} + static std::error_code fillStatus(int StatRet, const struct stat &Status, file_status &Result) { if (StatRet != 0) { - std::error_code ec(errno, std::generic_category()); - if (ec == errc::no_such_file_or_directory) + std::error_code EC(errno, std::generic_category()); + if (EC == errc::no_such_file_or_directory) Result = file_status(file_type::file_not_found); else Result = file_status(file_type::status_error); - return ec; + return EC; } - file_type Type = file_type::type_unknown; - - if (S_ISDIR(Status.st_mode)) - Type = file_type::directory_file; - else if (S_ISREG(Status.st_mode)) - Type = file_type::regular_file; - else if (S_ISBLK(Status.st_mode)) - Type = file_type::block_file; - else if (S_ISCHR(Status.st_mode)) - Type = file_type::character_file; - else if (S_ISFIFO(Status.st_mode)) - Type = file_type::fifo_file; - else if (S_ISSOCK(Status.st_mode)) - Type = file_type::socket_file; - else if (S_ISLNK(Status.st_mode)) - Type = file_type::symlink_file; - perms Perms = static_cast(Status.st_mode) & all_perms; - Result = file_status(Type, Perms, Status.st_dev, Status.st_nlink, - Status.st_ino, Status.st_atime, Status.st_mtime, - Status.st_uid, Status.st_gid, Status.st_size); + Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev, + Status.st_nlink, Status.st_ino, Status.st_atime, + Status.st_mtime, Status.st_uid, Status.st_gid, + Status.st_size); return std::error_code(); } @@ -696,19 +698,30 @@ return std::error_code(); } -std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { +static file_type direntType(dirent* Entry) { + // Most platforms provide the file type in the dirent: Linux/BSD/Mac. + // The DTTOIF macro lets us reuse our status -> type conversion. +#if defined(_DIRENT_HAVE_D_TYPE) && defined(DTTOIF) + return typeForMode(DTTOIF(Entry->d_type)); +#else + // Other platforms such as Solaris require a stat() to get the type. + return file_type::type_unknown; +#endif +} + +std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { errno = 0; - dirent *cur_dir = ::readdir(reinterpret_cast(it.IterationHandle)); - if (cur_dir == nullptr && errno != 0) { + dirent *CurDir = ::readdir(reinterpret_cast(It.IterationHandle)); + if (CurDir == nullptr && errno != 0) { return std::error_code(errno, std::generic_category()); - } else if (cur_dir != nullptr) { - StringRef name(cur_dir->d_name); - if ((name.size() == 1 && name[0] == '.') || - (name.size() == 2 && name[0] == '.' && name[1] == '.')) - return directory_iterator_increment(it); - it.CurrentEntry.replace_filename(name); + } else if (CurDir != nullptr) { + StringRef Name(CurDir->d_name); + if ((Name.size() == 1 && Name[0] == '.') || + (Name.size() == 2 && Name[0] == '.' && Name[1] == '.')) + return directory_iterator_increment(It); + It.CurrentEntry.replace_filename(Name, direntType(CurDir)); } else - return directory_iterator_destruct(it); + return directory_iterator_destruct(It); return std::error_code(); } Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -902,28 +902,28 @@ FindData->nFileSizeHigh, FindData->nFileSizeLow); } -std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path, - bool follow_symlinks) { - SmallVector path_utf16; +std::error_code detail::directory_iterator_construct(detail::DirIterState &It, + StringRef Path, + bool FollowSymlinks) { + SmallVector PathUTF16; - if (std::error_code ec = widenPath(path, path_utf16)) - return ec; + if (std::error_code EC = widenPath(Path, PathUTF16)) + return EC; // Convert path to the format that Windows is happy with. - if (path_utf16.size() > 0 && - !is_separator(path_utf16[path.size() - 1]) && - path_utf16[path.size() - 1] != L':') { - path_utf16.push_back(L'\\'); - path_utf16.push_back(L'*'); + if (PathUTF16.size() > 0 && + !is_separator(PathUTF16[Path.size() - 1]) && + PathUTF16[Path.size() - 1] != L':') { + PathUTF16.push_back(L'\\'); + PathUTF16.push_back(L'*'); } else { - path_utf16.push_back(L'*'); + PathUTF16.push_back(L'*'); } // Get the first directory entry. WIN32_FIND_DATAW FirstFind; ScopedFindHandle FindHandle(::FindFirstFileExW( - c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, + c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH)); if (!FindHandle) return mapWindowsError(::GetLastError()); @@ -936,23 +936,25 @@ DWORD LastError = ::GetLastError(); // Check for end. if (LastError == ERROR_NO_MORE_FILES) - return detail::directory_iterator_destruct(it); + return detail::directory_iterator_destruct(It); return mapWindowsError(LastError); } else FilenameLen = ::wcslen(FirstFind.cFileName); // Construct the current directory entry. - SmallString<128> directory_entry_name_utf8; - if (std::error_code ec = + SmallString<128> DirectoryEntryNameUTF8; + if (std::error_code EC = UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName), - directory_entry_name_utf8)) - return ec; + DirectoryEntryNameUTF8)) + return EC; - it.IterationHandle = intptr_t(FindHandle.take()); - SmallString<128> directory_entry_path(path); - path::append(directory_entry_path, directory_entry_name_utf8); - it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks, - status_from_find_data(&FirstFind)); + It.IterationHandle = intptr_t(FindHandle.take()); + SmallString<128> DirectoryEntryPath(Path); + path::append(DirectoryEntryPath, DirectoryEntryNameUTF8); + It.CurrentEntry = + directory_entry(DirectoryEntryPath, FollowSymlinks, + file_type_from_attrs(FirstFind.dwFileAttributes), + status_from_find_data(&FirstFind)); return std::error_code(); } @@ -966,13 +968,13 @@ return std::error_code(); } -std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { +std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { WIN32_FIND_DATAW FindData; - if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) { + if (!::FindNextFileW(HANDLE(It.IterationHandle), &FindData)) { DWORD LastError = ::GetLastError(); // Check for end. if (LastError == ERROR_NO_MORE_FILES) - return detail::directory_iterator_destruct(it); + return detail::directory_iterator_destruct(It); return mapWindowsError(LastError); } @@ -980,16 +982,18 @@ if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') || (FilenameLen == 2 && FindData.cFileName[0] == L'.' && FindData.cFileName[1] == L'.')) - return directory_iterator_increment(it); + return directory_iterator_increment(It); - SmallString<128> directory_entry_path_utf8; - if (std::error_code ec = + SmallString<128> DirectoryEntryPathUTF8; + if (std::error_code EC = UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName), - directory_entry_path_utf8)) - return ec; + DirectoryEntryPathUTF8)) + return EC; - it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8), - status_from_find_data(&FindData)); + It.CurrentEntry.replace_filename( + Twine(DirectoryEntryPathUTF8), + file_type_from_attrs(FindData.dwFileAttributes), + status_from_find_data(&FindData)); return std::error_code(); }