Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -1100,31 +1100,44 @@ /// @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, + file_type type = file_type::type_unknown, basic_file_status st = basic_file_status()) - : Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} + : Path(path.str()), Type(type), FollowSymlinks(follow_symlinks), + Status(st) {} 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, + void replace_filename(const Twine &filename, file_type type, basic_file_status st = 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); } Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -1085,11 +1085,12 @@ return std::error_code(); } -void directory_entry::replace_filename(const Twine &filename, +void directory_entry::replace_filename(const Twine &filename, file_type type, basic_file_status st) { SmallString<128> path = path::parent_path(Path); path::append(path, filename); Path = path.str(); + Type = type; Status = st; } Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -523,6 +523,24 @@ 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) { @@ -534,27 +552,11 @@ 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,6 +698,17 @@ return std::error_code(); } +static file_type dirent_type(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)); @@ -706,7 +719,7 @@ if ((name.size() == 1 && name[0] == '.') || (name.size() == 2 && name[0] == '.' && name[1] == '.')) return directory_iterator_increment(it); - it.CurrentEntry.replace_filename(name); + it.CurrentEntry.replace_filename(name, dirent_type(cur_dir)); } else return directory_iterator_destruct(it); Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -951,8 +951,10 @@ 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.CurrentEntry = + directory_entry(directory_entry_path, follow_symlinks, + file_type_from_attrs(FindData->dwFileAttributes), + status_from_find_data(&FirstFind)); return std::error_code(); } @@ -988,8 +990,10 @@ directory_entry_path_utf8)) return ec; - it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8), - status_from_find_data(&FindData)); + it.CurrentEntry.replace_filename( + Twine(directory_entry_path_utf8), + file_type_from_attrs(FindData->dwFileAttributes), + status_from_find_data(&FindData)); return std::error_code(); }