summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/file_util.cc19
-rw-r--r--base/file_util.h15
-rw-r--r--base/file_util_posix.cc24
-rw-r--r--base/file_util_unittest.cc75
-rw-r--r--base/file_util_win.cc12
-rw-r--r--chrome/common/zip.cc4
-rw-r--r--chrome/common/zip_unittest.cc4
-rw-r--r--net/base/directory_lister.cc5
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]);