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
3 // found in the LICENSE file.
5 #include "base/files/file_enumerator.h"
11 #include "base/logging.h"
12 #include "base/threading/thread_restrictions.h"
16 // FileEnumerator::FileInfo ----------------------------------------------------
18 FileEnumerator::FileInfo::FileInfo() {
19 memset(&stat_
, 0, sizeof(stat_
));
22 bool FileEnumerator::FileInfo::IsDirectory() const {
23 return S_ISDIR(stat_
.st_mode
);
26 FilePath
FileEnumerator::FileInfo::GetName() const {
30 int64
FileEnumerator::FileInfo::GetSize() const {
34 base::Time
FileEnumerator::FileInfo::GetLastModifiedTime() const {
35 return base::Time::FromTimeT(stat_
.st_mtime
);
38 // FileEnumerator --------------------------------------------------------------
40 FileEnumerator::FileEnumerator(const FilePath
& root_path
,
43 : current_directory_entry_(0),
44 root_path_(root_path
),
45 recursive_(recursive
),
46 file_type_(file_type
) {
47 // INCLUDE_DOT_DOT must not be specified if recursive.
48 DCHECK(!(recursive
&& (INCLUDE_DOT_DOT
& file_type_
)));
49 pending_paths_
.push(root_path
);
52 FileEnumerator::FileEnumerator(const FilePath
& root_path
,
55 const FilePath::StringType
& pattern
)
56 : current_directory_entry_(0),
57 root_path_(root_path
),
58 recursive_(recursive
),
59 file_type_(file_type
),
60 pattern_(root_path
.Append(pattern
).value()) {
61 // INCLUDE_DOT_DOT must not be specified if recursive.
62 DCHECK(!(recursive
&& (INCLUDE_DOT_DOT
& file_type_
)));
63 // The Windows version of this code appends the pattern to the root_path,
64 // potentially only matching against items in the top-most directory.
67 pattern_
= FilePath::StringType();
68 pending_paths_
.push(root_path
);
71 FileEnumerator::~FileEnumerator() {
74 FilePath
FileEnumerator::Next() {
75 ++current_directory_entry_
;
77 // While we've exhausted the entries in the current directory, do the next
78 while (current_directory_entry_
>= directory_entries_
.size()) {
79 if (pending_paths_
.empty())
82 root_path_
= pending_paths_
.top();
83 root_path_
= root_path_
.StripTrailingSeparators();
86 std::vector
<FileInfo
> entries
;
87 if (!ReadDirectory(&entries
, root_path_
, file_type_
& SHOW_SYM_LINKS
))
90 directory_entries_
.clear();
91 current_directory_entry_
= 0;
92 for (std::vector
<FileInfo
>::const_iterator i
= entries
.begin();
93 i
!= entries
.end(); ++i
) {
94 FilePath full_path
= root_path_
.Append(i
->filename_
);
95 if (ShouldSkip(full_path
))
98 if (pattern_
.size() &&
99 fnmatch(pattern_
.c_str(), full_path
.value().c_str(), FNM_NOESCAPE
))
102 if (recursive_
&& S_ISDIR(i
->stat_
.st_mode
))
103 pending_paths_
.push(full_path
);
105 if ((S_ISDIR(i
->stat_
.st_mode
) && (file_type_
& DIRECTORIES
)) ||
106 (!S_ISDIR(i
->stat_
.st_mode
) && (file_type_
& FILES
)))
107 directory_entries_
.push_back(*i
);
111 return root_path_
.Append(
112 directory_entries_
[current_directory_entry_
].filename_
);
115 FileEnumerator::FileInfo
FileEnumerator::GetInfo() const {
116 return directory_entries_
[current_directory_entry_
];
119 bool FileEnumerator::ReadDirectory(std::vector
<FileInfo
>* entries
,
120 const FilePath
& source
, bool show_links
) {
121 base::ThreadRestrictions::AssertIOAllowed();
122 DIR* dir
= opendir(source
.value().c_str());
126 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
127 !defined(OS_SOLARIS) && !defined(OS_ANDROID)
128 #error Port warning: depending on the definition of struct dirent, \
129 additional space for pathname may be needed
132 struct dirent dent_buf
;
134 while (readdir_r(dir
, &dent_buf
, &dent
) == 0 && dent
) {
136 info
.filename_
= FilePath(dent
->d_name
);
138 FilePath full_name
= source
.Append(dent
->d_name
);
141 ret
= lstat(full_name
.value().c_str(), &info
.stat_
);
143 ret
= stat(full_name
.value().c_str(), &info
.stat_
);
145 // Print the stat() error message unless it was ENOENT and we're
146 // following symlinks.
147 if (!(errno
== ENOENT
&& !show_links
)) {
148 DPLOG(ERROR
) << "Couldn't stat "
149 << source
.Append(dent
->d_name
).value();
151 memset(&info
.stat_
, 0, sizeof(info
.stat_
));
153 entries
->push_back(info
);