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

Side by Side Diff: base/files/file_enumerator_posix.cc

Issue 2892173003: Add recursive pattern matching for subfolders in file_enumerator. (Closed)
Patch Set: Add recursive pattern matching for subfolders in file_enumerator Created 3 years, 6 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 unified diff | Download patch
« no previous file with comments | « base/files/file_enumerator.cc ('k') | base/files/file_enumerator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_enumerator.h" 5 #include "base/files/file_enumerator.h"
6 6
7 #include <dirent.h> 7 #include <dirent.h>
8 #include <errno.h> 8 #include <errno.h>
9 #include <fnmatch.h> 9 #include <fnmatch.h>
10 #include <stdint.h> 10 #include <stdint.h>
11 #include <string.h>
11 12
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/threading/thread_restrictions.h" 14 #include "base/threading/thread_restrictions.h"
14 #include "build/build_config.h" 15 #include "build/build_config.h"
15 16
16 namespace base { 17 namespace base {
18 namespace {
19
20 void GetStat(const FilePath& path, bool show_links, struct stat* st) {
21 DCHECK(st);
22 const int res = show_links ? lstat(path.value().c_str(), st)
23 : stat(path.value().c_str(), st);
24 if (res < 0) {
25 // Print the stat() error message unless it was ENOENT and we're following
26 // symlinks.
27 if (!(errno == ENOENT && !show_links))
28 DPLOG(ERROR) << "Couldn't stat" << path.value();
29 memset(st, 0, sizeof(*st));
30 }
31 }
32
33 } // namespace
17 34
18 // FileEnumerator::FileInfo ---------------------------------------------------- 35 // FileEnumerator::FileInfo ----------------------------------------------------
19 36
20 FileEnumerator::FileInfo::FileInfo() { 37 FileEnumerator::FileInfo::FileInfo() {
21 memset(&stat_, 0, sizeof(stat_)); 38 memset(&stat_, 0, sizeof(stat_));
22 } 39 }
23 40
24 bool FileEnumerator::FileInfo::IsDirectory() const { 41 bool FileEnumerator::FileInfo::IsDirectory() const {
25 return S_ISDIR(stat_.st_mode); 42 return S_ISDIR(stat_.st_mode);
26 } 43 }
27 44
28 FilePath FileEnumerator::FileInfo::GetName() const { 45 FilePath FileEnumerator::FileInfo::GetName() const {
29 return filename_; 46 return filename_;
30 } 47 }
31 48
32 int64_t FileEnumerator::FileInfo::GetSize() const { 49 int64_t FileEnumerator::FileInfo::GetSize() const {
33 return stat_.st_size; 50 return stat_.st_size;
34 } 51 }
35 52
36 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { 53 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
37 return base::Time::FromTimeT(stat_.st_mtime); 54 return base::Time::FromTimeT(stat_.st_mtime);
38 } 55 }
39 56
40 // FileEnumerator -------------------------------------------------------------- 57 // FileEnumerator --------------------------------------------------------------
41 58
42 FileEnumerator::FileEnumerator(const FilePath& root_path, 59 FileEnumerator::FileEnumerator(const FilePath& root_path,
43 bool recursive, 60 bool recursive,
44 int file_type) 61 int file_type)
45 : current_directory_entry_(0), 62 : FileEnumerator(root_path,
46 root_path_(root_path), 63 recursive,
47 recursive_(recursive), 64 file_type,
48 file_type_(file_type) { 65 FilePath::StringType(),
49 // INCLUDE_DOT_DOT must not be specified if recursive. 66 FolderSearchPolicy::MATCH_ONLY) {}
50 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
51 pending_paths_.push(root_path);
52 }
53 67
54 FileEnumerator::FileEnumerator(const FilePath& root_path, 68 FileEnumerator::FileEnumerator(const FilePath& root_path,
55 bool recursive, 69 bool recursive,
56 int file_type, 70 int file_type,
57 const FilePath::StringType& pattern) 71 const FilePath::StringType& pattern)
72 : FileEnumerator(root_path,
73 recursive,
74 file_type,
75 pattern,
76 FolderSearchPolicy::MATCH_ONLY) {}
77
78 FileEnumerator::FileEnumerator(const FilePath& root_path,
79 bool recursive,
80 int file_type,
81 const FilePath::StringType& pattern,
82 FolderSearchPolicy folder_search_policy)
58 : current_directory_entry_(0), 83 : current_directory_entry_(0),
59 root_path_(root_path), 84 root_path_(root_path),
60 recursive_(recursive), 85 recursive_(recursive),
61 file_type_(file_type), 86 file_type_(file_type),
62 pattern_(root_path.Append(pattern).value()) { 87 pattern_(pattern),
88 folder_search_policy_(folder_search_policy) {
63 // INCLUDE_DOT_DOT must not be specified if recursive. 89 // INCLUDE_DOT_DOT must not be specified if recursive.
64 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 90 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
65 // The Windows version of this code appends the pattern to the root_path, 91
66 // potentially only matching against items in the top-most directory.
67 // Do the same here.
68 if (pattern.empty())
69 pattern_ = FilePath::StringType();
70 pending_paths_.push(root_path); 92 pending_paths_.push(root_path);
71 } 93 }
72 94
73 FileEnumerator::~FileEnumerator() { 95 FileEnumerator::~FileEnumerator() {
74 } 96 }
75 97
76 FilePath FileEnumerator::Next() { 98 FilePath FileEnumerator::Next() {
99 base::ThreadRestrictions::AssertIOAllowed();
100
101 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
102 !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(OS_AIX) && \
103 !defined(OS_FUCHSIA)
104 #error Port warning: depending on the definition of struct dirent, \
105 additional space for pathname may be needed
106 #endif
107
77 ++current_directory_entry_; 108 ++current_directory_entry_;
78 109
79 // While we've exhausted the entries in the current directory, do the next 110 // While we've exhausted the entries in the current directory, do the next
80 while (current_directory_entry_ >= directory_entries_.size()) { 111 while (current_directory_entry_ >= directory_entries_.size()) {
81 if (pending_paths_.empty()) 112 if (pending_paths_.empty())
82 return FilePath(); 113 return FilePath();
83 114
84 root_path_ = pending_paths_.top(); 115 root_path_ = pending_paths_.top();
85 root_path_ = root_path_.StripTrailingSeparators(); 116 root_path_ = root_path_.StripTrailingSeparators();
86 pending_paths_.pop(); 117 pending_paths_.pop();
87 118
88 std::vector<FileInfo> entries; 119 DIR* dir = opendir(root_path_.value().c_str());
89 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) 120 if (!dir)
90 continue; 121 continue;
91 122
92 directory_entries_.clear(); 123 directory_entries_.clear();
93 current_directory_entry_ = 0; 124 current_directory_entry_ = 0;
94 for (std::vector<FileInfo>::const_iterator i = entries.begin(); 125 struct dirent dent_buf;
95 i != entries.end(); ++i) { 126 struct dirent* dent;
96 FilePath full_path = root_path_.Append(i->filename_); 127 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
scottmg 2017/06/29 23:44:21 Please make sure the readdir_r->readdir, and the "
ivafanas 2017/06/30 04:57:53 Done.
97 if (ShouldSkip(full_path)) 128 FileInfo info;
129 info.filename_ = FilePath(dent->d_name);
130
131 if (ShouldSkip(info.filename_))
98 continue; 132 continue;
99 133
100 if (pattern_.size() && 134 const bool is_pattern_matched = IsPatternMatched(info.filename_);
101 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) 135
136 // MATCH_ONLY policy enumerates files and directories which matching
137 // pattern only. So we can early skip further checks.
138 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
139 !is_pattern_matched)
102 continue; 140 continue;
103 141
104 if (recursive_ && S_ISDIR(i->stat_.st_mode)) 142 // Do not call OS stat/lstat if there is no sense to do it. If pattern is
143 // not matched (file will not appear in results) and search is not
144 // recursive (possible directory will not be added to pending paths) -
145 // there is no sense to obtain item below.
146 if (!recursive_ && !is_pattern_matched)
147 continue;
148
149 const FilePath full_path = root_path_.Append(info.filename_);
150 GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_);
151
152 const bool is_dir = S_ISDIR(info.stat_.st_mode);
153
154 if (recursive_ && is_dir)
105 pending_paths_.push(full_path); 155 pending_paths_.push(full_path);
106 156
107 if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) || 157 if (is_pattern_matched && IsTypeMatched(is_dir))
108 (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES))) 158 directory_entries_.push_back(std::move(info));
109 directory_entries_.push_back(*i);
110 } 159 }
160 closedir(dir);
161
162 // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
163 // ALL policy enumerates files in all subfolders by origin pattern.
164 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
165 pattern_.clear();
111 } 166 }
112 167
113 return root_path_.Append( 168 return root_path_.Append(
114 directory_entries_[current_directory_entry_].filename_); 169 directory_entries_[current_directory_entry_].filename_);
115 } 170 }
116 171
117 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { 172 FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
118 return directory_entries_[current_directory_entry_]; 173 return directory_entries_[current_directory_entry_];
119 } 174 }
120 175
121 bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries, 176 bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
122 const FilePath& source, bool show_links) { 177 return pattern_.empty() ||
123 base::ThreadRestrictions::AssertIOAllowed(); 178 !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
124 DIR* dir = opendir(source.value().c_str());
125 if (!dir)
126 return false;
127
128 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
129 !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(OS_AIX) && \
130 !defined(OS_FUCHSIA)
131 #error Port warning: depending on the definition of struct dirent, \
132 additional space for pathname may be needed
133 #endif
134
135 struct dirent dent_buf;
136 struct dirent* dent;
137 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
138 FileInfo info;
139 info.filename_ = FilePath(dent->d_name);
140
141 FilePath full_name = source.Append(dent->d_name);
142 int ret;
143 if (show_links)
144 ret = lstat(full_name.value().c_str(), &info.stat_);
145 else
146 ret = stat(full_name.value().c_str(), &info.stat_);
147 if (ret < 0) {
148 // Print the stat() error message unless it was ENOENT and we're
149 // following symlinks.
150 if (!(errno == ENOENT && !show_links)) {
151 DPLOG(ERROR) << "Couldn't stat "
152 << source.Append(dent->d_name).value();
153 }
154 memset(&info.stat_, 0, sizeof(info.stat_));
155 }
156 entries->push_back(info);
157 }
158
159 closedir(dir);
160 return true;
161 } 179 }
162 180
163 } // namespace base 181 } // namespace base
OLDNEW
« no previous file with comments | « base/files/file_enumerator.cc ('k') | base/files/file_enumerator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698