diff options
-rw-r--r-- | base/file_util.cc | 19 | ||||
-rw-r--r-- | base/file_util.h | 15 | ||||
-rw-r--r-- | base/file_util_posix.cc | 24 | ||||
-rw-r--r-- | base/file_util_unittest.cc | 75 | ||||
-rw-r--r-- | base/file_util_win.cc | 12 | ||||
-rw-r--r-- | chrome/common/zip.cc | 4 | ||||
-rw-r--r-- | chrome/common/zip_unittest.cc | 4 | ||||
-rw-r--r-- | net/base/directory_lister.cc | 5 |
8 files changed, 136 insertions, 22 deletions
diff --git a/base/file_util.cc b/base/file_util.cc index 8eec3ac..5f18674 100644 --- a/base/file_util.cc +++ b/base/file_util.cc @@ -426,4 +426,23 @@ void UpOneDirectoryOrEmpty(std::wstring* dir) { int WriteFile(const std::wstring& filename, const char* data, int size) { return WriteFile(FilePath::FromWStringHack(filename), data, 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 IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_)); +} + +bool FileEnumerator::IsDot(const FilePath& path) { + return FILE_PATH_LITERAL(".") == path.BaseName().value(); +} + +bool FileEnumerator::IsDotDot(const FilePath& path) { + return FILE_PATH_LITERAL("..") == path.BaseName().value(); +} + } // namespace diff --git a/base/file_util.h b/base/file_util.h index 3686a18..39d8f09 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -409,9 +409,9 @@ class FileEnumerator { #endif enum FILE_TYPE { - FILES = 0x1, - DIRECTORIES = 0x2, - FILES_AND_DIRECTORIES = 0x3 + FILES = 1 << 0, + DIRECTORIES = 1 << 1, + INCLUDE_DOT_DOT = 1 << 2, }; // |root_path| is the starting directory to search for. It may or may not end @@ -463,6 +463,15 @@ class FileEnumerator { // enumerate in the breadth-first search. std::stack<FilePath> pending_paths_; + // Returns true if the given path should be skipped in enumeration. + bool ShouldSkip(const FilePath& path); + + // Returns true if the given path's base name is ".". + bool IsDot(const FilePath& path); + + // Returns true if the given path's base name is "..". + bool IsDotDot(const FilePath& path); + #if defined(OS_WIN) WIN32_FIND_DATA find_data_; HANDLE find_handle_; diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index 7f40c17..92b9d00 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -522,6 +522,8 @@ FileEnumerator::FileEnumerator(const FilePath& root_path, file_type_(file_type), is_in_find_op_(false), fts_(NULL) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); pending_paths_.push(root_path); } @@ -534,6 +536,8 @@ FileEnumerator::FileEnumerator(const FilePath& root_path, pattern_(root_path.value()), is_in_find_op_(false), fts_(NULL) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); // The Windows version of this code only matches against items in the top-most // directory, and we're comparing fnmatch against full paths, so this is the // easiest way to get the right pattern. @@ -556,6 +560,14 @@ void FileEnumerator::GetFindInfo(FindInfo* info) { info->filename.assign(fts_ent_->fts_name); } +int CompareFiles(const FTSENT** a, const FTSENT** b) { + // Order lexicographically, ignoring case and whether they are files or + // directories. + // TODO(yuzo): make this case-sensitive, directories-then-files, and + // internationalized. + return base::strcasecmp((*a)->fts_name, (*b)->fts_name); +} + // As it stands, this method calls itself recursively when the next item of // the fts enumeration doesn't match (type, pattern, etc.). In the case of // large directories with many files this can be quite deep. @@ -571,11 +583,11 @@ FilePath FileEnumerator::Next() { pending_paths_.pop(); // Start a new find operation. - int ftsflags = FTS_LOGICAL; + int ftsflags = FTS_LOGICAL | FTS_SEEDOT; char top_dir[PATH_MAX]; base::strlcpy(top_dir, root_path_.value().c_str(), arraysize(top_dir)); char* dir_list[2] = { top_dir, NULL }; - fts_ = fts_open(dir_list, ftsflags, NULL); + fts_ = fts_open(dir_list, ftsflags, CompareFiles); if (!fts_) return Next(); is_in_find_op_ = true; @@ -604,6 +616,9 @@ FilePath FileEnumerator::Next() { } FilePath cur_file(fts_ent_->fts_path); + if (ShouldSkip(cur_file)) + return Next(); + if (fts_ent_->fts_info == FTS_D) { // If not recursive, then prune children. if (!recursive_) @@ -611,6 +626,11 @@ FilePath FileEnumerator::Next() { return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next(); } else if (fts_ent_->fts_info == FTS_F) { return (file_type_ & FileEnumerator::FILES) ? cur_file : Next(); + } else if (fts_ent_->fts_info == FTS_DOT) { + if ((file_type_ & FileEnumerator::DIRECTORIES) && IsDotDot(cur_file)) { + return cur_file; + } + return Next(); } // TODO(erikkay) - verify that the other fts_info types aren't interesting return Next(); diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc index 736b0b5..3ecd1cf 100644 --- a/base/file_util_unittest.cc +++ b/base/file_util_unittest.cc @@ -30,6 +30,11 @@ namespace { +const file_util::FileEnumerator::FILE_TYPE FILES_AND_DIRECTORIES = + static_cast<file_util::FileEnumerator::FILE_TYPE>( + 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 class FileUtilTest : public PlatformTest { @@ -902,11 +907,19 @@ TEST_F(FileUtilTest, ReplaceExtensionTestWithPathSeparators) { TEST_F(FileUtilTest, FileEnumeratorTest) { // Test an empty directory. - file_util::FileEnumerator f0(test_dir_, true, - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + file_util::FileEnumerator f0(test_dir_, 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 "..". + file_util::FileEnumerator f0_dotdot(test_dir_, false, + static_cast<file_util::FileEnumerator::FILE_TYPE>( + FILES_AND_DIRECTORIES | file_util::FileEnumerator::INCLUDE_DOT_DOT)); + EXPECT_EQ(test_dir_.Append(FILE_PATH_LITERAL("..")).value(), + f0_dotdot.Next().value()); + EXPECT_EQ(FILE_PATH_LITERAL(""), + f0_dotdot.Next().value()); + // create the directories FilePath dir1 = test_dir_.Append(FILE_PATH_LITERAL("dir1")); EXPECT_TRUE(file_util::CreateDirectory(dir1)); @@ -955,9 +968,20 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c2_non_recursive.HasFile(dir2)); EXPECT_EQ(c2_non_recursive.size(), 2); + // Only enumerate directories, non-recursively, including "..". + file_util::FileEnumerator f2_dotdot( + test_dir_, false, + static_cast<file_util::FileEnumerator::FILE_TYPE>( + 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)); + EXPECT_TRUE(c2_dotdot.HasFile(test_dir_.Append(FILE_PATH_LITERAL("..")))); + EXPECT_EQ(c2_dotdot.size(), 3); + // Enumerate files and directories. - file_util::FileEnumerator f3(test_dir_, true, - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + file_util::FileEnumerator f3(test_dir_, true, FILES_AND_DIRECTORIES); FindResultCollector c3(f3); EXPECT_TRUE(c3.HasFile(dir1)); EXPECT_TRUE(c3.HasFile(dir2)); @@ -969,8 +993,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c3.size(), 7); // Non-recursive operation. - file_util::FileEnumerator f4(test_dir_, false, - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + file_util::FileEnumerator f4(test_dir_, false, FILES_AND_DIRECTORIES); FindResultCollector c4(f4); EXPECT_TRUE(c4.HasFile(dir2)); EXPECT_TRUE(c4.HasFile(dir2)); @@ -979,8 +1002,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(c4.size(), 4); // Enumerate with a pattern. - file_util::FileEnumerator f5(test_dir_, true, - file_util::FileEnumerator::FILES_AND_DIRECTORIES, + file_util::FileEnumerator f5(test_dir_, true, FILES_AND_DIRECTORIES, FILE_PATH_LITERAL("dir*")); FindResultCollector c5(f5); EXPECT_TRUE(c5.HasFile(dir1)); @@ -992,12 +1014,45 @@ 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. - file_util::FileEnumerator f6(test_dir_, true, - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + file_util::FileEnumerator f6(test_dir_, true, FILES_AND_DIRECTORIES); EXPECT_FALSE(f6.Next().value().empty()); // Should have found something // (we don't care what). } +TEST_F(FileUtilTest, FileEnumeratorOrderTest) { + FilePath fileA = test_dir_.Append(FILE_PATH_LITERAL("a")); + FilePath fileB = test_dir_.Append(FILE_PATH_LITERAL("B")); + FilePath dirC = test_dir_.Append(FILE_PATH_LITERAL("C")); + FilePath dirD = test_dir_.Append(FILE_PATH_LITERAL("d")); + FilePath dirE = test_dir_.Append(FILE_PATH_LITERAL("e")); + FilePath fileF = test_dir_.Append(FILE_PATH_LITERAL("f")); + + // Create files/directories in near random order. + CreateTextFile(fileF, L""); + CreateTextFile(fileA, L""); + CreateTextFile(fileB, L""); + EXPECT_TRUE(file_util::CreateDirectory(dirE)); + EXPECT_TRUE(file_util::CreateDirectory(dirC)); + EXPECT_TRUE(file_util::CreateDirectory(dirD)); + + // Files and directories are enumerated in the lexicographical order, + // ignoring case and whether they are files or directories. + file_util::FileEnumerator enumerator(test_dir_, false, FILES_AND_DIRECTORIES); + FilePath cur_file = enumerator.Next(); + EXPECT_EQ(fileA.value(), cur_file.value()); + cur_file = enumerator.Next(); + EXPECT_EQ(fileB.value(), cur_file.value()); + cur_file = enumerator.Next(); + EXPECT_EQ(dirC.value(), cur_file.value()); + cur_file = enumerator.Next(); + EXPECT_EQ(dirD.value(), cur_file.value()); + cur_file = enumerator.Next(); + EXPECT_EQ(dirE.value(), cur_file.value()); + cur_file = enumerator.Next(); + EXPECT_EQ(fileF.value(), cur_file.value()); + cur_file = enumerator.Next(); + EXPECT_EQ(FILE_PATH_LITERAL(""), cur_file.value()); +} void PathComponents(const std::wstring& path, std::vector<std::wstring>* components) { diff --git a/base/file_util_win.cc b/base/file_util_win.cc index f59f87d..91b9e2b 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -419,8 +419,9 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination, } bool IsDirectoryEmpty(const std::wstring& dir_path) { - FileEnumerator files(FilePath(dir_path), - false, FileEnumerator::FILES_AND_DIRECTORIES); + FileEnumerator files(FilePath(dir_path), false, + static_cast<FileEnumerator::FILE_TYPE>( + FileEnumerator::FILES | FileEnumerator::DIRECTORIES)); if (files.Next().value().empty()) return true; return false; @@ -675,6 +676,8 @@ FileEnumerator::FileEnumerator(const FilePath& root_path, file_type_(file_type), is_in_find_op_(false), find_handle_(INVALID_HANDLE_VALUE) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); pending_paths_.push(root_path); } @@ -687,6 +690,8 @@ FileEnumerator::FileEnumerator(const FilePath& root_path, is_in_find_op_(false), pattern_(pattern), find_handle_(INVALID_HANDLE_VALUE) { + // INCLUDE_DOT_DOT must not be specified if recursive. + DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); pending_paths_.push(root_path); } @@ -746,8 +751,7 @@ FilePath FileEnumerator::Next() { } FilePath cur_file(find_data_.cFileName); - // Skip over . and .. - if (L"." == cur_file.value() || L".." == cur_file.value()) + if (ShouldSkip(cur_file)) return Next(); // Construct the absolute filename. diff --git a/chrome/common/zip.cc b/chrome/common/zip.cc index 9b34dcb..9ac6da3 100644 --- a/chrome/common/zip.cc +++ b/chrome/common/zip.cc @@ -281,7 +281,9 @@ bool Zip(const FilePath& src_dir, const FilePath& dest_file) { bool success = true; file_util::FileEnumerator file_enumerator( src_dir, true, // recursive - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + static_cast<file_util::FileEnumerator::FILE_TYPE>( + file_util::FileEnumerator::FILES | + file_util::FileEnumerator::DIRECTORIES)); for (FilePath path = file_enumerator.Next(); !path.value().empty(); path = file_enumerator.Next()) { if (!AddEntryToZip(zip_file, path, src_dir)) { diff --git a/chrome/common/zip_unittest.cc b/chrome/common/zip_unittest.cc index 17ab1fa..0cffcf5 100644 --- a/chrome/common/zip_unittest.cc +++ b/chrome/common/zip_unittest.cc @@ -54,7 +54,9 @@ class ZipTest : public PlatformTest { ASSERT_TRUE(Unzip(path, test_dir_)); file_util::FileEnumerator files(test_dir_, true, - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + static_cast<file_util::FileEnumerator::FILE_TYPE>( + file_util::FileEnumerator::FILES | + file_util::FileEnumerator::DIRECTORIES)); FilePath next_path = files.Next(); size_t count = 0; while (!next_path.value().empty()) { diff --git a/net/base/directory_lister.cc b/net/base/directory_lister.cc index 581c087..616fc82 100644 --- a/net/base/directory_lister.cc +++ b/net/base/directory_lister.cc @@ -85,7 +85,10 @@ void DirectoryLister::ThreadMain() { } file_util::FileEnumerator file_enum(dir_, false, - file_util::FileEnumerator::FILES_AND_DIRECTORIES); + static_cast<file_util::FileEnumerator::FILE_TYPE>( + file_util::FileEnumerator::FILES | + file_util::FileEnumerator::DIRECTORIES | + file_util::FileEnumerator::INCLUDE_DOT_DOT)); while (!canceled_ && !(file_enum.Next().value().empty())) { file_enum.GetFindInfo(&e->data[e->count]); |