1 // Copyright (c) 2012 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/file_util.h"
14 #include "base/file_path.h"
15 #include "base/logging.h"
16 #include "base/stringprintf.h"
17 #include "base/string_piece.h"
18 #include "base/string_util.h"
19 #include "base/utf_string_conversions.h"
25 const FilePath::CharType kExtensionSeparator
= FILE_PATH_LITERAL('.');
27 // The maximum number of 'uniquified' files we will try to create.
28 // This is used when the filename we're trying to download is already in use,
29 // so we create a new unique filename by appending " (nnn)" before the
30 // extension, where 1 <= nnn <= kMaxUniqueFiles.
31 // Also used by code that cleans up said files.
32 static const int kMaxUniqueFiles
= 100;
38 bool g_bug108724_debug
= false;
40 bool EndsWithSeparator(const FilePath
& path
) {
41 FilePath::StringType value
= path
.value();
45 return FilePath::IsSeparator(value
[value
.size() - 1]);
48 bool EnsureEndsWithSeparator(FilePath
* path
) {
49 if (!DirectoryExists(*path
))
52 if (EndsWithSeparator(*path
))
55 FilePath::StringType
& path_str
=
56 const_cast<FilePath::StringType
&>(path
->value());
57 path_str
.append(&FilePath::kSeparators
[0], 1);
62 void InsertBeforeExtension(FilePath
* path
, const FilePath::StringType
& suffix
) {
63 FilePath::StringType
& value
=
64 const_cast<FilePath::StringType
&>(path
->value());
66 const FilePath::StringType::size_type last_dot
=
67 value
.rfind(kExtensionSeparator
);
68 const FilePath::StringType::size_type last_separator
=
69 value
.find_last_of(FilePath::StringType(FilePath::kSeparators
));
71 if (last_dot
== FilePath::StringType::npos
||
72 (last_separator
!= std::wstring::npos
&& last_dot
< last_separator
)) {
73 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
74 // We should just append the suffix to the entire path.
79 value
.insert(last_dot
, suffix
);
82 bool Move(const FilePath
& from_path
, const FilePath
& to_path
) {
83 if (from_path
.ReferencesParent() || to_path
.ReferencesParent())
85 return MoveUnsafe(from_path
, to_path
);
88 bool CopyFile(const FilePath
& from_path
, const FilePath
& to_path
) {
89 if (from_path
.ReferencesParent() || to_path
.ReferencesParent())
91 return CopyFileUnsafe(from_path
, to_path
);
94 bool ContentsEqual(const FilePath
& filename1
, const FilePath
& filename2
) {
95 // We open the file in binary format even if they are text files because
96 // we are just comparing that bytes are exactly same in both files and not
97 // doing anything smart with text formatting.
98 std::ifstream
file1(filename1
.value().c_str(),
99 std::ios::in
| std::ios::binary
);
100 std::ifstream
file2(filename2
.value().c_str(),
101 std::ios::in
| std::ios::binary
);
103 // Even if both files aren't openable (and thus, in some sense, "equal"),
104 // any unusable file yields a result of "false".
105 if (!file1
.is_open() || !file2
.is_open())
108 const int BUFFER_SIZE
= 2056;
109 char buffer1
[BUFFER_SIZE
], buffer2
[BUFFER_SIZE
];
111 file1
.read(buffer1
, BUFFER_SIZE
);
112 file2
.read(buffer2
, BUFFER_SIZE
);
114 if ((file1
.eof() != file2
.eof()) ||
115 (file1
.gcount() != file2
.gcount()) ||
116 (memcmp(buffer1
, buffer2
, file1
.gcount()))) {
121 } while (!file1
.eof() || !file2
.eof());
128 bool TextContentsEqual(const FilePath
& filename1
, const FilePath
& filename2
) {
129 std::ifstream
file1(filename1
.value().c_str(), std::ios::in
);
130 std::ifstream
file2(filename2
.value().c_str(), std::ios::in
);
132 // Even if both files aren't openable (and thus, in some sense, "equal"),
133 // any unusable file yields a result of "false".
134 if (!file1
.is_open() || !file2
.is_open())
138 std::string line1
, line2
;
139 getline(file1
, line1
);
140 getline(file2
, line2
);
142 // Check for mismatched EOF states, or any error state.
143 if ((file1
.eof() != file2
.eof()) ||
144 file1
.bad() || file2
.bad()) {
148 // Trim all '\r' and '\n' characters from the end of the line.
149 std::string::size_type end1
= line1
.find_last_not_of("\r\n");
150 if (end1
== std::string::npos
)
152 else if (end1
+ 1 < line1
.length())
153 line1
.erase(end1
+ 1);
155 std::string::size_type end2
= line2
.find_last_not_of("\r\n");
156 if (end2
== std::string::npos
)
158 else if (end2
+ 1 < line2
.length())
159 line2
.erase(end2
+ 1);
163 } while (!file1
.eof() || !file2
.eof());
168 bool ReadFileToString(const FilePath
& path
, std::string
* contents
) {
169 if (path
.ReferencesParent())
171 FILE* file
= OpenFile(path
, "rb");
178 while ((len
= fread(buf
, 1, sizeof(buf
), file
)) > 0) {
180 contents
->append(buf
, len
);
187 bool IsDirectoryEmpty(const FilePath
& dir_path
) {
188 FileEnumerator
files(dir_path
, false,
189 FileEnumerator::FILES
| FileEnumerator::DIRECTORIES
);
190 if (files
.Next().value().empty())
195 FILE* CreateAndOpenTemporaryFile(FilePath
* path
) {
197 if (!GetTempDir(&directory
))
200 return CreateAndOpenTemporaryFileInDir(directory
, path
);
203 bool GetFileSize(const FilePath
& file_path
, int64
* file_size
) {
204 base::PlatformFileInfo info
;
205 if (!GetFileInfo(file_path
, &info
))
207 *file_size
= info
.size
;
211 bool IsDot(const FilePath
& path
) {
212 return FILE_PATH_LITERAL(".") == path
.BaseName().value();
215 bool IsDotDot(const FilePath
& path
) {
216 return FILE_PATH_LITERAL("..") == path
.BaseName().value();
219 bool TouchFile(const FilePath
& path
,
220 const base::Time
& last_accessed
,
221 const base::Time
& last_modified
) {
222 int flags
= base::PLATFORM_FILE_OPEN
| base::PLATFORM_FILE_WRITE_ATTRIBUTES
;
225 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
226 if (DirectoryExists(path
))
227 flags
|= base::PLATFORM_FILE_BACKUP_SEMANTICS
;
230 const base::PlatformFile file
=
231 base::CreatePlatformFile(path
, flags
, NULL
, NULL
);
232 if (file
!= base::kInvalidPlatformFileValue
) {
233 bool result
= base::TouchPlatformFile(file
, last_accessed
, last_modified
);
234 base::ClosePlatformFile(file
);
241 bool SetLastModifiedTime(const FilePath
& path
,
242 const base::Time
& last_modified
) {
243 return TouchFile(path
, last_modified
, last_modified
);
246 bool CloseFile(FILE* file
) {
249 return fclose(file
) == 0;
252 bool TruncateFile(FILE* file
) {
255 long current_offset
= ftell(file
);
256 if (current_offset
== -1)
259 int fd
= _fileno(file
);
260 if (_chsize(fd
, current_offset
) != 0)
263 int fd
= fileno(file
);
264 if (ftruncate(fd
, current_offset
) != 0)
270 int GetUniquePathNumber(
271 const FilePath
& path
,
272 const FilePath::StringType
& suffix
) {
273 bool have_suffix
= !suffix
.empty();
274 if (!PathExists(path
) &&
275 (!have_suffix
|| !PathExists(FilePath(path
.value() + suffix
)))) {
280 for (int count
= 1; count
<= kMaxUniqueFiles
; ++count
) {
281 new_path
= path
.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count
));
282 if (!PathExists(new_path
) &&
283 (!have_suffix
|| !PathExists(FilePath(new_path
.value() + suffix
)))) {
291 bool ContainsPath(const FilePath
&parent
, const FilePath
& child
) {
292 FilePath abs_parent
= FilePath(parent
);
293 FilePath abs_child
= FilePath(child
);
295 if (!file_util::AbsolutePath(&abs_parent
) ||
296 !file_util::AbsolutePath(&abs_child
))
300 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
301 // a case-insensitive compare.
302 if (!StartsWith(abs_child
.value(), abs_parent
.value(), false))
304 if (!StartsWithASCII(abs_child
.value(), abs_parent
.value(), true))
308 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
309 // to check kSeparators[0].
310 if (abs_child
.value().length() <= abs_parent
.value().length() ||
311 abs_child
.value()[abs_parent
.value().length()] !=
312 FilePath::kSeparators
[0])
318 int64
ComputeDirectorySize(const FilePath
& root_path
) {
319 int64 running_size
= 0;
320 FileEnumerator
file_iter(root_path
, true, FileEnumerator::FILES
);
321 for (FilePath current
= file_iter
.Next(); !current
.empty();
322 current
= file_iter
.Next()) {
323 FileEnumerator::FindInfo info
;
324 file_iter
.GetFindInfo(&info
);
326 LARGE_INTEGER li
= { info
.nFileSizeLow
, info
.nFileSizeHigh
};
327 running_size
+= li
.QuadPart
;
329 running_size
+= info
.stat
.st_size
;
335 int64
ComputeFilesSize(const FilePath
& directory
,
336 const FilePath::StringType
& pattern
) {
337 int64 running_size
= 0;
338 FileEnumerator
file_iter(directory
, false, FileEnumerator::FILES
, pattern
);
339 for (FilePath current
= file_iter
.Next(); !current
.empty();
340 current
= file_iter
.Next()) {
341 FileEnumerator::FindInfo info
;
342 file_iter
.GetFindInfo(&info
);
344 LARGE_INTEGER li
= { info
.nFileSizeLow
, info
.nFileSizeHigh
};
345 running_size
+= li
.QuadPart
;
347 running_size
+= info
.stat
.st_size
;
353 ///////////////////////////////////////////////
356 MemoryMappedFile::~MemoryMappedFile() {
360 bool MemoryMappedFile::Initialize(const FilePath
& file_name
) {
364 if (!MapFileToMemory(file_name
)) {
372 bool MemoryMappedFile::Initialize(base::PlatformFile file
) {
378 if (!MapFileToMemoryInternal()) {
386 bool MemoryMappedFile::IsValid() const {
387 return data_
!= NULL
;
390 bool MemoryMappedFile::MapFileToMemory(const FilePath
& file_name
) {
391 file_
= base::CreatePlatformFile(
392 file_name
, base::PLATFORM_FILE_OPEN
| base::PLATFORM_FILE_READ
,
395 if (file_
== base::kInvalidPlatformFileValue
) {
396 DLOG(ERROR
) << "Couldn't open " << file_name
.value();
400 return MapFileToMemoryInternal();
403 ///////////////////////////////////////////////
406 // Note: the main logic is in file_util_<platform>.cc
408 bool FileEnumerator::ShouldSkip(const FilePath
& path
) {
409 FilePath::StringType basename
= path
.BaseName().value();
410 return IsDot(path
) || (IsDotDot(path
) && !(INCLUDE_DOT_DOT
& file_type_
));