Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4044)

Unified Diff: base/files/file_enumerator_posix.cc

Issue 2892173003: Add recursive pattern matching for subfolders in file_enumerator. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/files/file_enumerator_posix.cc
diff --git a/base/files/file_enumerator_posix.cc b/base/files/file_enumerator_posix.cc
index 2b54c6dcb3bccb39c123c57e21c22f3b2c375628..982ca1e16a677b7ca1c03b244ef8c61e4ab05682 100644
--- a/base/files/file_enumerator_posix.cc
+++ b/base/files/file_enumerator_posix.cc
@@ -8,12 +8,29 @@
#include <errno.h>
#include <fnmatch.h>
#include <stdint.h>
+#include <string.h>
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
namespace base {
+namespace {
+
+void GetStat(const FilePath& path, struct stat* st, bool show_links) {
+ DCHECK(st);
+ const int res = show_links ? lstat(path.value().c_str(), st)
+ : stat(path.value().c_str(), st);
+ if (res < 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" << path.value();
+ memset(st, 0, sizeof(*st));
Mark Mentovai 2017/05/19 17:16:11 Why this instead of a failure return value?
ivafanas 2017/05/22 04:42:28 I've just saved the previous behaviour: https://cs
+ }
+}
+
+} // namespace
// FileEnumerator::FileInfo ----------------------------------------------------
@@ -42,31 +59,36 @@ base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
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(root_path,
+ recursive,
+ file_type,
+ FilePath::StringType(),
+ FolderSearchPolicy::MATCH_ONLY) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern)
+ : FileEnumerator(root_path,
+ recursive,
+ file_type,
+ pattern,
+ FolderSearchPolicy::MATCH_ONLY) {}
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern,
+ FolderSearchPolicy folder_search_policy)
: current_directory_entry_(0),
root_path_(root_path),
recursive_(recursive),
file_type_(file_type),
- pattern_(root_path.Append(pattern).value()) {
+ pattern_(pattern),
+ folder_search_policy_(folder_search_policy) {
// 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);
}
@@ -74,6 +96,14 @@ FileEnumerator::~FileEnumerator() {
}
FilePath FileEnumerator::Next() {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+#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
+
++current_directory_entry_;
// While we've exhausted the entries in the current directory, do the next
@@ -85,29 +115,53 @@ FilePath FileEnumerator::Next() {
root_path_ = root_path_.StripTrailingSeparators();
pending_paths_.pop();
- std::vector<FileInfo> entries;
- if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
+ DIR* dir = opendir(root_path_.value().c_str());
+ if (!dir)
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))
+ struct dirent dent_buf;
+ struct dirent* dent;
+ while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
+ FileInfo info;
+ info.filename_ = FilePath(dent->d_name);
+
+ if (ShouldSkip(info.filename_))
continue;
- if (pattern_.size() &&
- fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
+ const bool is_pattern_matched = IsPatternMatched(info.filename_);
+
+ // MATCH_ONLY policy enumerates files and directories which matching
+ // pattern only. So we can early skip further checks.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
+ !is_pattern_matched)
continue;
- if (recursive_ && S_ISDIR(i->stat_.st_mode))
+ // Do not call OS stat/lstat if there is no sense to do it. If pattern is
+ // not matched (file will not appear in results) and search is not
+ // recursive (possible directory will not be added to penfing paths) -
+ // there is no sense to obtain item below.
+ if (!recursive_ && !is_pattern_matched)
+ continue;
+
+ const FilePath full_path = root_path_.Append(info.filename_);
+ GetStat(full_path, &info.stat_, file_type_ & SHOW_SYM_LINKS);
+
+ const bool is_dir = S_ISDIR(info.stat_.st_mode);
+
+ if (recursive_ && is_dir)
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);
+ if (IsTypeMatched(is_dir) && is_pattern_matched)
+ directory_entries_.push_back(std::move(info));
}
+ closedir(dir);
+
+ // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
+ // ALL policy enumerates files in all subfolders by origin pattern.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
+ pattern_.clear();
}
return root_path_.Append(
@@ -118,46 +172,9 @@ 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) && !defined(OS_AIX) && \
- !defined(OS_FUCHSIA)
-#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;
+bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
+ return pattern_.empty() ||
+ !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
}
} // namespace base

Powered by Google App Engine
This is Rietveld 408576698