OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/files/file_enumerator.h" |
| 6 |
| 7 #include <dirent.h> |
| 8 #include <errno.h> |
| 9 #include <fnmatch.h> |
| 10 |
| 11 #include "base/logging.h" |
| 12 #include "base/threading/thread_restrictions.h" |
| 13 #include "base/time.h" |
| 14 |
| 15 using base::FilePath; |
| 16 using base::Time; |
| 17 |
| 18 namespace file_util { |
| 19 |
| 20 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 21 bool recursive, |
| 22 int file_type) |
| 23 : current_directory_entry_(0), |
| 24 root_path_(root_path), |
| 25 recursive_(recursive), |
| 26 file_type_(file_type) { |
| 27 // INCLUDE_DOT_DOT must not be specified if recursive. |
| 28 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
| 29 pending_paths_.push(root_path); |
| 30 } |
| 31 |
| 32 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 33 bool recursive, |
| 34 int file_type, |
| 35 const FilePath::StringType& pattern) |
| 36 : current_directory_entry_(0), |
| 37 root_path_(root_path), |
| 38 recursive_(recursive), |
| 39 file_type_(file_type), |
| 40 pattern_(root_path.Append(pattern).value()) { |
| 41 // INCLUDE_DOT_DOT must not be specified if recursive. |
| 42 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
| 43 // The Windows version of this code appends the pattern to the root_path, |
| 44 // potentially only matching against items in the top-most directory. |
| 45 // Do the same here. |
| 46 if (pattern.empty()) |
| 47 pattern_ = FilePath::StringType(); |
| 48 pending_paths_.push(root_path); |
| 49 } |
| 50 |
| 51 FileEnumerator::~FileEnumerator() { |
| 52 } |
| 53 |
| 54 FilePath FileEnumerator::Next() { |
| 55 ++current_directory_entry_; |
| 56 |
| 57 // While we've exhausted the entries in the current directory, do the next |
| 58 while (current_directory_entry_ >= directory_entries_.size()) { |
| 59 if (pending_paths_.empty()) |
| 60 return FilePath(); |
| 61 |
| 62 root_path_ = pending_paths_.top(); |
| 63 root_path_ = root_path_.StripTrailingSeparators(); |
| 64 pending_paths_.pop(); |
| 65 |
| 66 std::vector<DirectoryEntryInfo> entries; |
| 67 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) |
| 68 continue; |
| 69 |
| 70 directory_entries_.clear(); |
| 71 current_directory_entry_ = 0; |
| 72 for (std::vector<DirectoryEntryInfo>::const_iterator |
| 73 i = entries.begin(); i != entries.end(); ++i) { |
| 74 FilePath full_path = root_path_.Append(i->filename); |
| 75 if (ShouldSkip(full_path)) |
| 76 continue; |
| 77 |
| 78 if (pattern_.size() && |
| 79 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) |
| 80 continue; |
| 81 |
| 82 if (recursive_ && S_ISDIR(i->stat.st_mode)) |
| 83 pending_paths_.push(full_path); |
| 84 |
| 85 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) || |
| 86 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES))) |
| 87 directory_entries_.push_back(*i); |
| 88 } |
| 89 } |
| 90 |
| 91 return root_path_.Append(directory_entries_[current_directory_entry_ |
| 92 ].filename); |
| 93 } |
| 94 |
| 95 void FileEnumerator::GetFindInfo(FindInfo* info) { |
| 96 DCHECK(info); |
| 97 |
| 98 if (current_directory_entry_ >= directory_entries_.size()) |
| 99 return; |
| 100 |
| 101 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; |
| 102 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); |
| 103 info->filename.assign(cur_entry->filename.value()); |
| 104 } |
| 105 |
| 106 // static |
| 107 bool FileEnumerator::IsDirectory(const FindInfo& info) { |
| 108 return S_ISDIR(info.stat.st_mode); |
| 109 } |
| 110 |
| 111 // static |
| 112 FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { |
| 113 return FilePath(find_info.filename); |
| 114 } |
| 115 |
| 116 // static |
| 117 int64 FileEnumerator::GetFilesize(const FindInfo& find_info) { |
| 118 return find_info.stat.st_size; |
| 119 } |
| 120 |
| 121 // static |
| 122 Time FileEnumerator::GetLastModifiedTime(const FindInfo& find_info) { |
| 123 return Time::FromTimeT(find_info.stat.st_mtime); |
| 124 } |
| 125 |
| 126 bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, |
| 127 const FilePath& source, bool show_links) { |
| 128 base::ThreadRestrictions::AssertIOAllowed(); |
| 129 DIR* dir = opendir(source.value().c_str()); |
| 130 if (!dir) |
| 131 return false; |
| 132 |
| 133 struct dirent dent_buf; |
| 134 struct dirent* dent; |
| 135 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
| 136 DirectoryEntryInfo info; |
| 137 info.filename = FilePath(dent->d_name); |
| 138 |
| 139 FilePath full_name = source.Append(dent->d_name); |
| 140 int ret; |
| 141 if (show_links) |
| 142 ret = lstat(full_name.value().c_str(), &info.stat); |
| 143 else |
| 144 ret = stat(full_name.value().c_str(), &info.stat); |
| 145 if (ret < 0) { |
| 146 // Print the stat() error message unless it was ENOENT and we're |
| 147 // following symlinks. |
| 148 if (!(errno == ENOENT && !show_links)) { |
| 149 DPLOG(ERROR) << "Couldn't stat " |
| 150 << source.Append(dent->d_name).value(); |
| 151 } |
| 152 memset(&info.stat, 0, sizeof(info.stat)); |
| 153 } |
| 154 entries->push_back(info); |
| 155 } |
| 156 |
| 157 closedir(dir); |
| 158 return true; |
| 159 } |
| 160 |
| 161 } // namespace file_util |
OLD | NEW |