diff options
author | dbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-08 05:46:20 +0000 |
---|---|---|
committer | dbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-08 05:46:20 +0000 |
commit | 9e66a9b94a40eeeeb52c47c83990b0766e1b7ceb (patch) | |
tree | 0818f910c522fb5bda00cab55412201251db32f7 /base | |
parent | ad5b14e00b36aab6e2b2d6aba8477a5dd9a11998 (diff) | |
download | chromium_src-9e66a9b94a40eeeeb52c47c83990b0766e1b7ceb.zip chromium_src-9e66a9b94a40eeeeb52c47c83990b0766e1b7ceb.tar.gz chromium_src-9e66a9b94a40eeeeb52c47c83990b0766e1b7ceb.tar.bz2 |
Revert 198820 "Move FileEnumerator to its own file, do some refa..."
Broke both windows clobber and official builders' compile with this error:
771>Link:
771> Creating library ..\..\..\build\Release\lib\gcp_portmon64.lib and
object ..\..\..\build\Release\lib\gcp_portmon64.exp
771>base.lib(path_service.obj) : fatalerror LNK1112: module machine type 'X86'
conflicts with target machine type 'x64'
771>
771>Build FAILED.
> Move FileEnumerator to its own file, do some refactoring.
>
> It creates a class FileInfo to contain the details rather than using a platform-specific typedef. This allows the accessors GetName, GetSize, etc. to be moved directly to this class (previously they were static helpers on the FileEnumerator class) which makes a bunch of code much cleaner. It also gives reasonable getting and initialization which the previous version lacked.
>
> BUG=175002
> R=rvargas@chromium.org
>
> Review URL: https://codereview.chromium.org/13165005
TBR=brettw@chromium.org
Review URL: https://codereview.chromium.org/14824006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198850 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gypi | 5 | ||||
-rw-r--r-- | base/file_util.cc | 29 | ||||
-rw-r--r-- | base/file_util.h | 114 | ||||
-rw-r--r-- | base/file_util_posix.cc | 178 | ||||
-rw-r--r-- | base/file_util_unittest.cc | 42 | ||||
-rw-r--r-- | base/file_util_win.cc | 133 | ||||
-rw-r--r-- | base/files/file_enumerator.cc | 21 | ||||
-rw-r--r-- | base/files/file_enumerator.h | 156 | ||||
-rw-r--r-- | base/files/file_enumerator_posix.cc | 160 | ||||
-rw-r--r-- | base/files/file_enumerator_win.cc | 149 | ||||
-rw-r--r-- | base/test/test_file_util_posix.cc | 3 |
11 files changed, 457 insertions, 533 deletions
diff --git a/base/base.gypi b/base/base.gypi index be869ce..7194f24 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -159,10 +159,6 @@ 'files/dir_reader_fallback.h', 'files/dir_reader_linux.h', 'files/dir_reader_posix.h', - 'files/file_enumerator.cc', - 'files/file_enumerator.h', - 'files/file_enumerator_posix.cc', - 'files/file_enumerator_win.cc', 'files/file_path.cc', 'files/file_path.h', 'files/file_path_watcher.cc', @@ -633,7 +629,6 @@ 'file_util.cc', 'file_util_posix.cc', 'file_util_proxy.cc', - 'files/file_enumerator_posix.cc', 'files/file_path_watcher_kqueue.cc', 'memory/shared_memory_posix.cc', 'native_library_posix.cc', diff --git a/base/file_util.cc b/base/file_util.cc index 8cdf75b..e76c5c2 100644 --- a/base/file_util.cc +++ b/base/file_util.cc @@ -11,7 +11,6 @@ #include <fstream> -#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/string_util.h" @@ -19,7 +18,6 @@ #include "base/strings/string_piece.h" #include "base/utf_string_conversions.h" -using base::FileEnumerator; using base::FilePath; namespace { @@ -167,7 +165,7 @@ bool ReadFileToString(const FilePath& path, std::string* contents) { bool IsDirectoryEmpty(const FilePath& dir_path) { FileEnumerator files(dir_path, false, FileEnumerator::FILES | FileEnumerator::DIRECTORIES); - if (files.Next().empty()) + if (files.Next().value().empty()) return true; return false; } @@ -264,9 +262,30 @@ int GetUniquePathNumber( int64 ComputeDirectorySize(const FilePath& root_path) { int64 running_size = 0; FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); - while (!file_iter.Next().empty()) - running_size += file_iter.GetInfo().GetSize(); + for (FilePath current = file_iter.Next(); !current.empty(); + current = file_iter.Next()) { + FileEnumerator::FindInfo info; + file_iter.GetFindInfo(&info); +#if defined(OS_WIN) + LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh }; + running_size += li.QuadPart; +#else + running_size += info.stat.st_size; +#endif + } return running_size; } +/////////////////////////////////////////////// +// FileEnumerator +// +// Note: the main logic is in file_util_<platform>.cc + +bool FileEnumerator::ShouldSkip(const FilePath& path) { + FilePath::StringType basename = path.BaseName().value(); + return basename == FILE_PATH_LITERAL(".") || + (basename == FILE_PATH_LITERAL("..") && + !(INCLUDE_DOT_DOT & file_type_)); +} + } // namespace diff --git a/base/file_util.h b/base/file_util.h index b9bb6eb..4ffd05b 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -20,6 +20,7 @@ #include <stdio.h> #include <set> +#include <stack> #include <string> #include <vector> @@ -116,9 +117,9 @@ BASE_EXPORT bool CopyFileUnsafe(const base::FilePath& from_path, // Copies the given path, and optionally all subdirectories and their contents // as well. -// -// If there are files existing under to_path, always overwrite. Returns true -// if successful, false otherwise. Wildcards on the names are not supported. +// If there are files existing under to_path, always overwrite. +// Returns true if successful, false otherwise. +// Don't use wildcards on the names, it may stop working without notice. // // If you only need to copy a file use CopyFile, it's faster. BASE_EXPORT bool CopyDirectory(const base::FilePath& from_path, @@ -421,6 +422,113 @@ class ScopedFDClose { typedef scoped_ptr_malloc<int, ScopedFDClose> ScopedFD; #endif // OS_POSIX +// A class for enumerating the files in a provided path. The order of the +// results is not guaranteed. +// +// DO NOT USE FROM THE MAIN THREAD of your application unless it is a test +// program where latency does not matter. This class is blocking. +class BASE_EXPORT FileEnumerator { + public: +#if defined(OS_WIN) + typedef WIN32_FIND_DATA FindInfo; +#elif defined(OS_POSIX) + typedef struct { + struct stat stat; + std::string filename; + } FindInfo; +#endif + + enum FileType { + FILES = 1 << 0, + DIRECTORIES = 1 << 1, + INCLUDE_DOT_DOT = 1 << 2, +#if defined(OS_POSIX) + SHOW_SYM_LINKS = 1 << 4, +#endif + }; + + // |root_path| is the starting directory to search for. It may or may not end + // in a slash. + // + // If |recursive| is true, this will enumerate all matches in any + // subdirectories matched as well. It does a breadth-first search, so all + // files in one directory will be returned before any files in a + // subdirectory. + // + // |file_type|, a bit mask of FileType, specifies whether the enumerator + // should match files, directories, or both. + // + // |pattern| is an optional pattern for which files to match. This + // works like shell globbing. For example, "*.txt" or "Foo???.doc". + // However, be careful in specifying patterns that aren't cross platform + // since the underlying code uses OS-specific matching routines. In general, + // Windows matching is less featureful than others, so test there first. + // If unspecified, this will match all files. + // NOTE: the pattern only matches the contents of root_path, not files in + // recursive subdirectories. + // TODO(erikkay): Fix the pattern matching to work at all levels. + FileEnumerator(const base::FilePath& root_path, + bool recursive, + int file_type); + FileEnumerator(const base::FilePath& root_path, + bool recursive, + int file_type, + const base::FilePath::StringType& pattern); + ~FileEnumerator(); + + // Returns an empty string if there are no more results. + base::FilePath Next(); + + // Write the file info into |info|. + void GetFindInfo(FindInfo* info); + + // Looks inside a FindInfo and determines if it's a directory. + static bool IsDirectory(const FindInfo& info); + + static base::FilePath GetFilename(const FindInfo& find_info); + static int64 GetFilesize(const FindInfo& find_info); + static base::Time GetLastModifiedTime(const FindInfo& find_info); + + private: + // Returns true if the given path should be skipped in enumeration. + bool ShouldSkip(const base::FilePath& path); + + +#if defined(OS_WIN) + // True when find_data_ is valid. + bool has_find_data_; + WIN32_FIND_DATA find_data_; + HANDLE find_handle_; +#elif defined(OS_POSIX) + struct DirectoryEntryInfo { + base::FilePath filename; + struct stat stat; + }; + + // Read the filenames in source into the vector of DirectoryEntryInfo's + static bool ReadDirectory(std::vector<DirectoryEntryInfo>* entries, + const base::FilePath& source, bool show_links); + + // The files in the current directory + std::vector<DirectoryEntryInfo> directory_entries_; + + // The next entry to use from the directory_entries_ vector + size_t current_directory_entry_; +#endif + + base::FilePath root_path_; + bool recursive_; + int file_type_; + base::FilePath::StringType pattern_; // Empty when we want to find + // everything. + + // A stack that keeps track of which subdirectories we still need to + // enumerate in the breadth-first search. + std::stack<base::FilePath> pending_paths_; + + DISALLOW_COPY_AND_ASSIGN(FileEnumerator); +}; + #if defined(OS_LINUX) // Broad categories of file systems as returned by statfs() on Linux. enum FileSystemType { diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index 68516e4..8b36812 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -7,6 +7,7 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <fnmatch.h> #include <libgen.h> #include <limits.h> #include <stdio.h> @@ -31,7 +32,6 @@ #include <fstream> #include "base/basictypes.h" -#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -58,7 +58,6 @@ #include "base/chromeos/chromeos_version.h" #endif -using base::FileEnumerator; using base::FilePath; using base::MakeAbsoluteFilePath; @@ -191,7 +190,10 @@ bool Delete(const FilePath& path, bool recursive) { FileEnumerator::SHOW_SYM_LINKS); for (FilePath current = traversal.Next(); success && !current.empty(); current = traversal.Next()) { - if (traversal.GetInfo().IsDirectory()) + FileEnumerator::FindInfo info; + traversal.GetFindInfo(&info); + + if (S_ISDIR(info.stat.st_mode)) directories.push(current.value()); else success = (unlink(current.value().c_str()) == 0); @@ -235,13 +237,14 @@ bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); } -bool CopyDirectory(const base::FilePath& from_path, - const base::FilePath& to_path, +bool CopyDirectory(const FilePath& from_path, + const FilePath& to_path, bool recursive) { base::ThreadRestrictions::AssertIOAllowed(); // Some old callers of CopyDirectory want it to support wildcards. // After some discussion, we decided to fix those callers. // Break loudly here if anyone tries to do this. + // TODO(evanm): remove this once we're sure it's ok. DCHECK(to_path.value().find('*') == std::string::npos); DCHECK(from_path.value().find('*') == std::string::npos); @@ -278,9 +281,9 @@ bool CopyDirectory(const base::FilePath& from_path, // We have to mimic windows behavior here. |to_path| may not exist yet, // start the loop with |to_path|. - struct stat from_stat; + FileEnumerator::FindInfo info; FilePath current = from_path; - if (stat(from_path.value().c_str(), &from_stat) < 0) { + if (stat(from_path.value().c_str(), &info.stat) < 0) { DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " << from_path.value() << " errno = " << errno; success = false; @@ -296,7 +299,7 @@ bool CopyDirectory(const base::FilePath& from_path, // The Windows version of this function assumes that non-recursive calls // will always have a directory for from_path. - DCHECK(recursive || S_ISDIR(from_stat.st_mode)); + DCHECK(recursive || S_ISDIR(info.stat.st_mode)); while (success && !current.empty()) { // current is the source path, including from_path, so append @@ -309,14 +312,14 @@ bool CopyDirectory(const base::FilePath& from_path, } } - if (S_ISDIR(from_stat.st_mode)) { - if (mkdir(target_path.value().c_str(), from_stat.st_mode & 01777) != 0 && + if (S_ISDIR(info.stat.st_mode)) { + if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && errno != EEXIST) { DLOG(ERROR) << "CopyDirectory() couldn't create directory: " << target_path.value() << " errno = " << errno; success = false; } - } else if (S_ISREG(from_stat.st_mode)) { + } else if (S_ISREG(info.stat.st_mode)) { if (!CopyFile(current, target_path)) { DLOG(ERROR) << "CopyDirectory() couldn't create file: " << target_path.value(); @@ -328,8 +331,7 @@ bool CopyDirectory(const base::FilePath& from_path, } current = traversal.Next(); - if (!current.empty()) - from_stat = traversal.GetInfo().stat(); + traversal.GetFindInfo(&info); } return success; @@ -682,6 +684,156 @@ bool SetCurrentDirectory(const FilePath& path) { return !ret; } +/////////////////////////////////////////////// +// FileEnumerator + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type) + : current_directory_entry_(0), + root_path_(root_path), + recursive_(recursive), + file_type_(file_type) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + pending_paths_.push(root_path); +} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern) + : current_directory_entry_(0), + root_path_(root_path), + recursive_(recursive), + file_type_(file_type), + pattern_(root_path.Append(pattern).value()) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + // The Windows version of this code appends the pattern to the root_path, + // potentially only matching against items in the top-most directory. + // Do the same here. + if (pattern.empty()) + pattern_ = FilePath::StringType(); + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() { +} + +FilePath FileEnumerator::Next() { + ++current_directory_entry_; + + // While we've exhausted the entries in the current directory, do the next + while (current_directory_entry_ >= directory_entries_.size()) { + if (pending_paths_.empty()) + return FilePath(); + + root_path_ = pending_paths_.top(); + root_path_ = root_path_.StripTrailingSeparators(); + pending_paths_.pop(); + + std::vector<DirectoryEntryInfo> entries; + if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) + continue; + + directory_entries_.clear(); + current_directory_entry_ = 0; + for (std::vector<DirectoryEntryInfo>::const_iterator + i = entries.begin(); i != entries.end(); ++i) { + FilePath full_path = root_path_.Append(i->filename); + if (ShouldSkip(full_path)) + continue; + + if (pattern_.size() && + fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) + continue; + + if (recursive_ && S_ISDIR(i->stat.st_mode)) + pending_paths_.push(full_path); + + if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) || + (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES))) + directory_entries_.push_back(*i); + } + } + + return root_path_.Append(directory_entries_[current_directory_entry_ + ].filename); +} + +void FileEnumerator::GetFindInfo(FindInfo* info) { + DCHECK(info); + + if (current_directory_entry_ >= directory_entries_.size()) + return; + + DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; + memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); + info->filename.assign(cur_entry->filename.value()); +} + +// static +bool FileEnumerator::IsDirectory(const FindInfo& info) { + return S_ISDIR(info.stat.st_mode); +} + +// static +FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { + return FilePath(find_info.filename); +} + +// static +int64 FileEnumerator::GetFilesize(const FindInfo& find_info) { + return find_info.stat.st_size; +} + +// static +base::Time FileEnumerator::GetLastModifiedTime(const FindInfo& find_info) { + return base::Time::FromTimeT(find_info.stat.st_mtime); +} + +bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, + const FilePath& source, bool show_links) { + base::ThreadRestrictions::AssertIOAllowed(); + DIR* dir = opendir(source.value().c_str()); + if (!dir) + return false; + +#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ + !defined(OS_SOLARIS) && !defined(OS_ANDROID) + #error Port warning: depending on the definition of struct dirent, \ + additional space for pathname may be needed +#endif + + struct dirent dent_buf; + struct dirent* dent; + while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { + DirectoryEntryInfo info; + info.filename = FilePath(dent->d_name); + + FilePath full_name = source.Append(dent->d_name); + int ret; + if (show_links) + ret = lstat(full_name.value().c_str(), &info.stat); + else + ret = stat(full_name.value().c_str(), &info.stat); + if (ret < 0) { + // Print the stat() error message unless it was ENOENT and we're + // following symlinks. + if (!(errno == ENOENT && !show_links)) { + DPLOG(ERROR) << "Couldn't stat " + << source.Append(dent->d_name).value(); + } + memset(&info.stat, 0, sizeof(info.stat)); + } + entries->push_back(info); + } + + closedir(dir); + return true; +} + bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { FilePath real_path_result; if (!RealPath(path, &real_path_result)) diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc index 593ae81..5cfc677 100644 --- a/base/file_util_unittest.cc +++ b/base/file_util_unittest.cc @@ -18,7 +18,6 @@ #include "base/base_paths.h" #include "base/file_util.h" -#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" @@ -35,7 +34,6 @@ // This macro helps avoid wrapped lines in the test structs. #define FPL(x) FILE_PATH_LITERAL(x) -using base::FileEnumerator; using base::FilePath; namespace { @@ -136,7 +134,7 @@ void ChangePosixFilePermissions(const FilePath& path, const wchar_t bogus_content[] = L"I'm cannon fodder."; const int FILES_AND_DIRECTORIES = - FileEnumerator::FILES | FileEnumerator::DIRECTORIES; + file_util::FileEnumerator::FILES | file_util::FileEnumerator::DIRECTORIES; // file_util winds up using autoreleased objects on the Mac, so this needs // to be a PlatformTest @@ -154,7 +152,7 @@ class FileUtilTest : public PlatformTest { // interface to query whether a given file is present. class FindResultCollector { public: - explicit FindResultCollector(FileEnumerator& enumerator) { + explicit FindResultCollector(file_util::FileEnumerator& enumerator) { FilePath cur_file; while (!(cur_file = enumerator.Next()).value().empty()) { FilePath::StringType path = cur_file.value(); @@ -919,7 +917,8 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { EXPECT_FALSE(mode & file_util::FILE_PERMISSION_USER_MASK); // Make sure the file in the directory can't be enumerated. - FileEnumerator f1(subdir_path, true, FileEnumerator::FILES); + file_util::FileEnumerator f1(subdir_path, true, + file_util::FileEnumerator::FILES); EXPECT_TRUE(file_util::PathExists(subdir_path)); FindResultCollector c1(f1); EXPECT_EQ(c1.size(), 0); @@ -934,7 +933,8 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { mode & file_util::FILE_PERMISSION_USER_MASK); // Make sure the file in the directory can be enumerated. - FileEnumerator f2(subdir_path, true, FileEnumerator::FILES); + file_util::FileEnumerator f2(subdir_path, true, + file_util::FileEnumerator::FILES); FindResultCollector c2(f2); EXPECT_TRUE(c2.HasFile(file_name)); EXPECT_EQ(c2.size(), 1); @@ -1824,13 +1824,13 @@ TEST_F(FileUtilTest, DetectDirectoryTest) { TEST_F(FileUtilTest, FileEnumeratorTest) { // Test an empty directory. - FileEnumerator f0(temp_dir_.path(), true, FILES_AND_DIRECTORIES); + file_util::FileEnumerator f0(temp_dir_.path(), true, FILES_AND_DIRECTORIES); EXPECT_EQ(f0.Next().value(), FILE_PATH_LITERAL("")); EXPECT_EQ(f0.Next().value(), FILE_PATH_LITERAL("")); // Test an empty directory, non-recursively, including "..". - FileEnumerator f0_dotdot(temp_dir_.path(), false, - FILES_AND_DIRECTORIES | FileEnumerator::INCLUDE_DOT_DOT); + file_util::FileEnumerator f0_dotdot(temp_dir_.path(), false, + FILES_AND_DIRECTORIES | file_util::FileEnumerator::INCLUDE_DOT_DOT); EXPECT_EQ(temp_dir_.path().Append(FILE_PATH_LITERAL("..")).value(), f0_dotdot.Next().value()); EXPECT_EQ(FILE_PATH_LITERAL(""), @@ -1857,7 +1857,8 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { FilePath file2_abs = temp_dir_.path().Append(FILE_PATH_LITERAL("file2.txt")); // Only enumerate files. - FileEnumerator f1(temp_dir_.path(), true, FileEnumerator::FILES); + file_util::FileEnumerator f1(temp_dir_.path(), true, + file_util::FileEnumerator::FILES); FindResultCollector c1(f1); EXPECT_TRUE(c1.HasFile(file1)); EXPECT_TRUE(c1.HasFile(file2_abs)); @@ -1866,7 +1867,8 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c1.size(), 4); // Only enumerate directories. - FileEnumerator f2(temp_dir_.path(), true, FileEnumerator::DIRECTORIES); + file_util::FileEnumerator f2(temp_dir_.path(), true, + file_util::FileEnumerator::DIRECTORIES); FindResultCollector c2(f2); EXPECT_TRUE(c2.HasFile(dir1)); EXPECT_TRUE(c2.HasFile(dir2)); @@ -1874,17 +1876,17 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c2.size(), 3); // Only enumerate directories non-recursively. - FileEnumerator f2_non_recursive( - temp_dir_.path(), false, FileEnumerator::DIRECTORIES); + file_util::FileEnumerator f2_non_recursive( + temp_dir_.path(), false, file_util::FileEnumerator::DIRECTORIES); FindResultCollector c2_non_recursive(f2_non_recursive); EXPECT_TRUE(c2_non_recursive.HasFile(dir1)); EXPECT_TRUE(c2_non_recursive.HasFile(dir2)); EXPECT_EQ(c2_non_recursive.size(), 2); // Only enumerate directories, non-recursively, including "..". - FileEnumerator f2_dotdot(temp_dir_.path(), false, - FileEnumerator::DIRECTORIES | - FileEnumerator::INCLUDE_DOT_DOT); + file_util::FileEnumerator f2_dotdot(temp_dir_.path(), false, + file_util::FileEnumerator::DIRECTORIES | + file_util::FileEnumerator::INCLUDE_DOT_DOT); FindResultCollector c2_dotdot(f2_dotdot); EXPECT_TRUE(c2_dotdot.HasFile(dir1)); EXPECT_TRUE(c2_dotdot.HasFile(dir2)); @@ -1893,7 +1895,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c2_dotdot.size(), 3); // Enumerate files and directories. - FileEnumerator f3(temp_dir_.path(), true, FILES_AND_DIRECTORIES); + file_util::FileEnumerator f3(temp_dir_.path(), true, FILES_AND_DIRECTORIES); FindResultCollector c3(f3); EXPECT_TRUE(c3.HasFile(dir1)); EXPECT_TRUE(c3.HasFile(dir2)); @@ -1905,7 +1907,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c3.size(), 7); // Non-recursive operation. - FileEnumerator f4(temp_dir_.path(), false, FILES_AND_DIRECTORIES); + file_util::FileEnumerator f4(temp_dir_.path(), false, FILES_AND_DIRECTORIES); FindResultCollector c4(f4); EXPECT_TRUE(c4.HasFile(dir2)); EXPECT_TRUE(c4.HasFile(dir2)); @@ -1914,7 +1916,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c4.size(), 4); // Enumerate with a pattern. - FileEnumerator f5(temp_dir_.path(), true, FILES_AND_DIRECTORIES, + file_util::FileEnumerator f5(temp_dir_.path(), true, FILES_AND_DIRECTORIES, FILE_PATH_LITERAL("dir*")); FindResultCollector c5(f5); EXPECT_TRUE(c5.HasFile(dir1)); @@ -1926,7 +1928,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Make sure the destructor closes the find handle while in the middle of a // query to allow TearDown to delete the directory. - FileEnumerator f6(temp_dir_.path(), true, FILES_AND_DIRECTORIES); + file_util::FileEnumerator f6(temp_dir_.path(), true, FILES_AND_DIRECTORIES); EXPECT_FALSE(f6.Next().value().empty()); // Should have found something // (we don't care what). } diff --git a/base/file_util_win.cc b/base/file_util_win.cc index b166322..964302a 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -595,6 +595,139 @@ bool SetCurrentDirectory(const FilePath& directory) { return ret != 0; } +/////////////////////////////////////////////// +// FileEnumerator + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type) + : recursive_(recursive), + file_type_(file_type), + has_find_data_(false), + find_handle_(INVALID_HANDLE_VALUE) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + memset(&find_data_, 0, sizeof(find_data_)); + pending_paths_.push(root_path); +} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + int file_type, + const FilePath::StringType& pattern) + : recursive_(recursive), + file_type_(file_type), + has_find_data_(false), + pattern_(pattern), + find_handle_(INVALID_HANDLE_VALUE) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); + memset(&find_data_, 0, sizeof(find_data_)); + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() { + if (find_handle_ != INVALID_HANDLE_VALUE) + FindClose(find_handle_); +} + +void FileEnumerator::GetFindInfo(FindInfo* info) { + DCHECK(info); + + if (!has_find_data_) + return; + + memcpy(info, &find_data_, sizeof(*info)); +} + +// static +bool FileEnumerator::IsDirectory(const FindInfo& info) { + return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +// static +FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { + return FilePath(find_info.cFileName); +} + +// static +int64 FileEnumerator::GetFilesize(const FindInfo& find_info) { + ULARGE_INTEGER size; + size.HighPart = find_info.nFileSizeHigh; + size.LowPart = find_info.nFileSizeLow; + DCHECK_LE(size.QuadPart, std::numeric_limits<int64>::max()); + return static_cast<int64>(size.QuadPart); +} + +// static +base::Time FileEnumerator::GetLastModifiedTime(const FindInfo& find_info) { + return base::Time::FromFileTime(find_info.ftLastWriteTime); +} + +FilePath FileEnumerator::Next() { + base::ThreadRestrictions::AssertIOAllowed(); + + while (has_find_data_ || !pending_paths_.empty()) { + if (!has_find_data_) { + // The last find FindFirstFile operation is done, prepare a new one. + root_path_ = pending_paths_.top(); + pending_paths_.pop(); + + // Start a new find operation. + FilePath src = root_path_; + + if (pattern_.empty()) + src = src.Append(L"*"); // No pattern = match everything. + else + src = src.Append(pattern_); + + find_handle_ = FindFirstFile(src.value().c_str(), &find_data_); + has_find_data_ = true; + } else { + // Search for the next file/directory. + if (!FindNextFile(find_handle_, &find_data_)) { + FindClose(find_handle_); + find_handle_ = INVALID_HANDLE_VALUE; + } + } + + if (INVALID_HANDLE_VALUE == find_handle_) { + has_find_data_ = false; + + // This is reached when we have finished a directory and are advancing to + // the next one in the queue. We applied the pattern (if any) to the files + // in the root search directory, but for those directories which were + // matched, we want to enumerate all files inside them. This will happen + // when the handle is empty. + pattern_ = FilePath::StringType(); + + continue; + } + + FilePath cur_file(find_data_.cFileName); + if (ShouldSkip(cur_file)) + continue; + + // Construct the absolute filename. + cur_file = root_path_.Append(find_data_.cFileName); + + if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (recursive_) { + // If |cur_file| is a directory, and we are doing recursive searching, + // add it to pending_paths_ so we scan it after we finish scanning this + // directory. + pending_paths_.push(cur_file); + } + if (file_type_ & FileEnumerator::DIRECTORIES) + return cur_file; + } else if (file_type_ & FileEnumerator::FILES) { + return cur_file; + } + } + + return FilePath(); +} + bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { base::ThreadRestrictions::AssertIOAllowed(); FilePath mapped_file; diff --git a/base/files/file_enumerator.cc b/base/files/file_enumerator.cc deleted file mode 100644 index e49f465..0000000 --- a/base/files/file_enumerator.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_enumerator.h" - -#include "base/file_util.h" - -namespace base { - -FileEnumerator::FileInfo::~FileInfo() { -} - -bool FileEnumerator::ShouldSkip(const FilePath& path) { - FilePath::StringType basename = path.BaseName().value(); - return basename == FILE_PATH_LITERAL(".") || - (basename == FILE_PATH_LITERAL("..") && - !(INCLUDE_DOT_DOT & file_type_)); -} - -} // namespace base diff --git a/base/files/file_enumerator.h b/base/files/file_enumerator.h deleted file mode 100644 index 3834281..0000000 --- a/base/files/file_enumerator.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_FILE_ENUMERATOR_H_ -#define BASE_FILES_FILE_ENUMERATOR_H_ - -#include <stack> -#include <vector> - -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/files/file_path.h" -#include "base/time.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include <windows.h> -#elif defined(OS_POSIX) -#include <sys/stat.h> -#include <unistd.h> -#endif - -namespace base { - -// A class for enumerating the files in a provided path. The order of the -// results is not guaranteed. -// -// This is blocking. Do not use on critical threads. -// -// Example: -// -// base::FileEnumerator enum(my_dir, false, base::FileEnumerator::FILES, -// FILE_PATH_LITERAL("*.txt")); -// for (base::FilePath name = enum.Next(); !name.empty(); name = enum.Next()) -// ... -class BASE_EXPORT FileEnumerator { - public: - // Note: copy & assign supported. - class FileInfo { - public: - FileInfo(); - ~FileInfo(); - - bool IsDirectory() const; - - // The name of the file. This will not include any path information. This - // is in constrast to the value returned by FileEnumerator.Next() which - // includes the |root_path| passed into the FileEnumerator constructor. - FilePath GetName() const; - - int64 GetSize() const; - Time GetLastModifiedTime() const; - -#if defined(OS_WIN) - const WIN32_FIND_DATA& find_data() const { return find_data_; } -#elif defined(OS_POSIX) - const struct stat& stat() const { return stat_; } -#endif - - private: - friend class FileEnumerator; - -#if defined(OS_WIN) - WIN32_FIND_DATA find_data_; -#elif defined(OS_POSIX) - struct stat stat_; - FilePath filename_; -#endif - }; - - enum FileType { - FILES = 1 << 0, - DIRECTORIES = 1 << 1, - INCLUDE_DOT_DOT = 1 << 2, -#if defined(OS_POSIX) - SHOW_SYM_LINKS = 1 << 4, -#endif - }; - - // |root_path| is the starting directory to search for. It may or may not end - // in a slash. - // - // If |recursive| is true, this will enumerate all matches in any - // subdirectories matched as well. It does a breadth-first search, so all - // files in one directory will be returned before any files in a - // subdirectory. - // - // |file_type|, a bit mask of FileType, specifies whether the enumerator - // should match files, directories, or both. - // - // |pattern| is an optional pattern for which files to match. This - // works like shell globbing. For example, "*.txt" or "Foo???.doc". - // However, be careful in specifying patterns that aren't cross platform - // since the underlying code uses OS-specific matching routines. In general, - // Windows matching is less featureful than others, so test there first. - // If unspecified, this will match all files. - // NOTE: the pattern only matches the contents of root_path, not files in - // recursive subdirectories. - // TODO(erikkay): Fix the pattern matching to work at all levels. - FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type); - FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern); - ~FileEnumerator(); - - // Returns the next file or an empty string if there are no more results. - // - // The returned path will incorporate the |root_path| passed in the - // constructor: "<root_path>/file_name.txt". If the |root_path| is absolute, - // then so will be the result of Next(). - FilePath Next(); - - // Write the file info into |info|. - FileInfo GetInfo() const; - - private: - // Returns true if the given path should be skipped in enumeration. - bool ShouldSkip(const FilePath& path); - -#if defined(OS_WIN) - // True when find_data_ is valid. - bool has_find_data_; - WIN32_FIND_DATA find_data_; - HANDLE find_handle_; -#elif defined(OS_POSIX) - - // Read the filenames in source into the vector of DirectoryEntryInfo's - static bool ReadDirectory(std::vector<FileInfo>* entries, - const FilePath& source, bool show_links); - - // The files in the current directory - std::vector<FileInfo> directory_entries_; - - // The next entry to use from the directory_entries_ vector - size_t current_directory_entry_; -#endif - - FilePath root_path_; - bool recursive_; - int file_type_; - FilePath::StringType pattern_; // Empty when we want to find everything. - - // A stack that keeps track of which subdirectories we still need to - // enumerate in the breadth-first search. - std::stack<FilePath> pending_paths_; - - DISALLOW_COPY_AND_ASSIGN(FileEnumerator); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_ENUMERATOR_H_ diff --git a/base/files/file_enumerator_posix.cc b/base/files/file_enumerator_posix.cc deleted file mode 100644 index 7533a24..0000000 --- a/base/files/file_enumerator_posix.cc +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_enumerator.h" - -#include <dirent.h> -#include <errno.h> -#include <fnmatch.h> - -#include "base/logging.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -// FileEnumerator::FileInfo ---------------------------------------------------- - -FileEnumerator::FileInfo::FileInfo() { - memset(&stat_, 0, sizeof(stat_)); -} - -bool FileEnumerator::FileInfo::IsDirectory() const { - return S_ISDIR(stat_.st_mode); -} - -FilePath FileEnumerator::FileInfo::GetName() const { - return filename_; -} - -int64 FileEnumerator::FileInfo::GetSize() const { - return stat_.st_size; -} - -base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { - return base::Time::FromTimeT(stat_.st_mtime); -} - -// FileEnumerator -------------------------------------------------------------- - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type) - : current_directory_entry_(0), - root_path_(root_path), - recursive_(recursive), - file_type_(file_type) { - // INCLUDE_DOT_DOT must not be specified if recursive. - DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); - pending_paths_.push(root_path); -} - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern) - : current_directory_entry_(0), - root_path_(root_path), - recursive_(recursive), - file_type_(file_type), - pattern_(root_path.Append(pattern).value()) { - // INCLUDE_DOT_DOT must not be specified if recursive. - DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); - // The Windows version of this code appends the pattern to the root_path, - // potentially only matching against items in the top-most directory. - // Do the same here. - if (pattern.empty()) - pattern_ = FilePath::StringType(); - pending_paths_.push(root_path); -} - -FileEnumerator::~FileEnumerator() { -} - -FilePath FileEnumerator::Next() { - ++current_directory_entry_; - - // While we've exhausted the entries in the current directory, do the next - while (current_directory_entry_ >= directory_entries_.size()) { - if (pending_paths_.empty()) - return FilePath(); - - root_path_ = pending_paths_.top(); - root_path_ = root_path_.StripTrailingSeparators(); - pending_paths_.pop(); - - std::vector<FileInfo> entries; - if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) - continue; - - directory_entries_.clear(); - current_directory_entry_ = 0; - for (std::vector<FileInfo>::const_iterator i = entries.begin(); - i != entries.end(); ++i) { - FilePath full_path = root_path_.Append(i->filename_); - if (ShouldSkip(full_path)) - continue; - - if (pattern_.size() && - fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) - continue; - - if (recursive_ && S_ISDIR(i->stat_.st_mode)) - pending_paths_.push(full_path); - - if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) || - (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES))) - directory_entries_.push_back(*i); - } - } - - return root_path_.Append( - directory_entries_[current_directory_entry_].filename_); -} - -FileEnumerator::FileInfo FileEnumerator::GetInfo() const { - return directory_entries_[current_directory_entry_]; -} - -bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries, - const FilePath& source, bool show_links) { - base::ThreadRestrictions::AssertIOAllowed(); - DIR* dir = opendir(source.value().c_str()); - if (!dir) - return false; - -#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ - !defined(OS_SOLARIS) && !defined(OS_ANDROID) - #error Port warning: depending on the definition of struct dirent, \ - additional space for pathname may be needed -#endif - - struct dirent dent_buf; - struct dirent* dent; - while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { - FileInfo info; - info.filename_ = FilePath(dent->d_name); - - FilePath full_name = source.Append(dent->d_name); - int ret; - if (show_links) - ret = lstat(full_name.value().c_str(), &info.stat_); - else - ret = stat(full_name.value().c_str(), &info.stat_); - if (ret < 0) { - // Print the stat() error message unless it was ENOENT and we're - // following symlinks. - if (!(errno == ENOENT && !show_links)) { - DPLOG(ERROR) << "Couldn't stat " - << source.Append(dent->d_name).value(); - } - memset(&info.stat_, 0, sizeof(info.stat_)); - } - entries->push_back(info); - } - - closedir(dir); - return true; -} - -} // namespace base diff --git a/base/files/file_enumerator_win.cc b/base/files/file_enumerator_win.cc deleted file mode 100644 index 64c9845..0000000 --- a/base/files/file_enumerator_win.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_enumerator.h" - -#include <string.h> - -#include "base/logging.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -// FileEnumerator::FileInfo ---------------------------------------------------- - -FileEnumerator::FileInfo::FileInfo() { - memset(&find_data_, 0, sizeof(find_data_)); -} - -bool FileEnumerator::FileInfo::IsDirectory() const { - return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -} - -FilePath FileEnumerator::FileInfo::GetName() const { - return FilePath(find_data_.cFileName); -} - -int64 FileEnumerator::FileInfo::GetSize() const { - ULARGE_INTEGER size; - size.HighPart = find_data_.nFileSizeHigh; - size.LowPart = find_data_.nFileSizeLow; - DCHECK_LE(size.QuadPart, std::numeric_limits<int64>::max()); - return static_cast<int64>(size.QuadPart); -} - -base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { - return base::Time::FromFileTime(find_data_.ftLastWriteTime); -} - -// FileEnumerator -------------------------------------------------------------- - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type) - : recursive_(recursive), - file_type_(file_type), - has_find_data_(false), - find_handle_(INVALID_HANDLE_VALUE) { - // INCLUDE_DOT_DOT must not be specified if recursive. - DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); - memset(&find_data_, 0, sizeof(find_data_)); - pending_paths_.push(root_path); -} - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern) - : recursive_(recursive), - file_type_(file_type), - has_find_data_(false), - pattern_(pattern), - find_handle_(INVALID_HANDLE_VALUE) { - // INCLUDE_DOT_DOT must not be specified if recursive. - DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); - memset(&find_data_, 0, sizeof(find_data_)); - pending_paths_.push(root_path); -} - -FileEnumerator::~FileEnumerator() { - if (find_handle_ != INVALID_HANDLE_VALUE) - FindClose(find_handle_); -} - -FileEnumerator::FileInfo FileEnumerator::GetInfo() const { - if (!has_find_data_) { - NOTREACHED(); - return FileInfo(); - } - FileInfo ret; - memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)); - return ret; -} - -FilePath FileEnumerator::Next() { - base::ThreadRestrictions::AssertIOAllowed(); - - while (has_find_data_ || !pending_paths_.empty()) { - if (!has_find_data_) { - // The last find FindFirstFile operation is done, prepare a new one. - root_path_ = pending_paths_.top(); - pending_paths_.pop(); - - // Start a new find operation. - FilePath src = root_path_; - - if (pattern_.empty()) - src = src.Append(L"*"); // No pattern = match everything. - else - src = src.Append(pattern_); - - find_handle_ = FindFirstFile(src.value().c_str(), &find_data_); - has_find_data_ = true; - } else { - // Search for the next file/directory. - if (!FindNextFile(find_handle_, &find_data_)) { - FindClose(find_handle_); - find_handle_ = INVALID_HANDLE_VALUE; - } - } - - if (INVALID_HANDLE_VALUE == find_handle_) { - has_find_data_ = false; - - // This is reached when we have finished a directory and are advancing to - // the next one in the queue. We applied the pattern (if any) to the files - // in the root search directory, but for those directories which were - // matched, we want to enumerate all files inside them. This will happen - // when the handle is empty. - pattern_ = FilePath::StringType(); - - continue; - } - - FilePath cur_file(find_data_.cFileName); - if (ShouldSkip(cur_file)) - continue; - - // Construct the absolute filename. - cur_file = root_path_.Append(find_data_.cFileName); - - if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (recursive_) { - // If |cur_file| is a directory, and we are doing recursive searching, - // add it to pending_paths_ so we scan it after we finish scanning this - // directory. - pending_paths_.push(cur_file); - } - if (file_type_ & FileEnumerator::DIRECTORIES) - return cur_file; - } else if (file_type_ & FileEnumerator::FILES) { - return cur_file; - } - } - - return FilePath(); -} - -} // namespace base diff --git a/base/test/test_file_util_posix.cc b/base/test/test_file_util_posix.cc index d80a954..7c16cb18 100644 --- a/base/test/test_file_util_posix.cc +++ b/base/test/test_file_util_posix.cc @@ -12,12 +12,13 @@ #include <string> #include "base/file_util.h" -#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +using base::MakeAbsoluteFilePath; + namespace file_util { namespace { |