blob: 7cf67dd28e169dfb9bfdbeee2ecc1440419ed568 [file] [log] [blame]
tfarina@chromium.org34828222010-05-26 10:40:12 +09001// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
5#include "base/file_util.h"
6
jeremy@chromium.orga5f404d2009-01-28 04:08:39 +09007#if defined(OS_WIN)
8#include <io.h>
9#endif
mark@chromium.orgd1bafc62008-10-02 02:40:13 +090010#include <stdio.h>
11
initial.commit3f4a7322008-07-27 06:49:38 +090012#include <fstream>
initial.commit3f4a7322008-07-27 06:49:38 +090013
evanm@google.com874d1672008-10-31 08:54:04 +090014#include "base/file_path.h"
initial.commit3f4a7322008-07-27 06:49:38 +090015#include "base/logging.h"
estade@chromium.org97e37822008-11-27 13:03:57 +090016#include "base/string_piece.h"
brettw@chromium.org50c94652009-10-07 11:10:20 +090017#include "base/string_util.h"
18#include "base/utf_string_conversions.h"
estade@chromium.org97e37822008-11-27 13:03:57 +090019
estade@chromium.org63343202008-12-05 05:46:06 +090020namespace {
21
22const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
23
willchan@chromium.org2f0b26b2009-04-25 02:44:39 +090024} // namespace
estade@chromium.org63343202008-12-05 05:46:06 +090025
initial.commit3f4a7322008-07-27 06:49:38 +090026namespace file_util {
27
estade@chromium.org97e37822008-11-27 13:03:57 +090028bool EndsWithSeparator(const FilePath& path) {
29 FilePath::StringType value = path.value();
30 if (value.empty())
31 return false;
32
33 return FilePath::IsSeparator(value[value.size() - 1]);
initial.commit3f4a7322008-07-27 06:49:38 +090034}
35
estade@chromium.orgd778b8f2008-11-26 07:04:37 +090036bool EnsureEndsWithSeparator(FilePath* path) {
37 if (!DirectoryExists(*path))
38 return false;
39
40 if (EndsWithSeparator(*path))
41 return true;
42
43 FilePath::StringType& path_str =
44 const_cast<FilePath::StringType&>(path->value());
45 path_str.append(&FilePath::kSeparators[0], 1);
46
47 return true;
48}
49
estade@chromium.orgae508942009-01-07 08:31:41 +090050FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
51 FilePath::StringType file_name = path.BaseName().value();
52 const FilePath::StringType::size_type last_dot =
53 file_name.rfind(kExtensionSeparator);
54 return FilePath::StringType(last_dot == FilePath::StringType::npos ?
55 FILE_PATH_LITERAL("") :
56 file_name, last_dot+1);
glen@chromium.org40eb4922008-11-04 01:14:46 +090057}
58
estade@chromium.org63343202008-12-05 05:46:06 +090059void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
60 FilePath::StringType& value =
61 const_cast<FilePath::StringType&>(path->value());
62
63 const FilePath::StringType::size_type last_dot =
64 value.rfind(kExtensionSeparator);
65 const FilePath::StringType::size_type last_separator =
66 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
67
68 if (last_dot == FilePath::StringType::npos ||
69 (last_separator != std::wstring::npos && last_dot < last_separator)) {
70 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
71 // We should just append the suffix to the entire path.
72 value.append(suffix);
73 return;
74 }
75
76 value.insert(last_dot, suffix);
77}
78
evanm@google.com874d1672008-10-31 08:54:04 +090079bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commit3f4a7322008-07-27 06:49:38 +090080 // We open the file in binary format even if they are text files because
81 // we are just comparing that bytes are exactly same in both files and not
82 // doing anything smart with text formatting.
evanm@google.com874d1672008-10-31 08:54:04 +090083 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com9fc57d02008-08-09 05:16:08 +090084 std::ios::in | std::ios::binary);
evanm@google.com874d1672008-10-31 08:54:04 +090085 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com9fc57d02008-08-09 05:16:08 +090086 std::ios::in | std::ios::binary);
estade@chromium.org97e37822008-11-27 13:03:57 +090087
initial.commit3f4a7322008-07-27 06:49:38 +090088 // Even if both files aren't openable (and thus, in some sense, "equal"),
89 // any unusable file yields a result of "false".
90 if (!file1.is_open() || !file2.is_open())
91 return false;
92
93 const int BUFFER_SIZE = 2056;
94 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
95 do {
96 file1.read(buffer1, BUFFER_SIZE);
97 file2.read(buffer2, BUFFER_SIZE);
98
mark@chromium.org95c9ec92009-06-27 06:17:24 +090099 if ((file1.eof() != file2.eof()) ||
initial.commit3f4a7322008-07-27 06:49:38 +0900100 (file1.gcount() != file2.gcount()) ||
101 (memcmp(buffer1, buffer2, file1.gcount()))) {
102 file1.close();
103 file2.close();
104 return false;
105 }
mark@chromium.org95c9ec92009-06-27 06:17:24 +0900106 } while (!file1.eof() || !file2.eof());
initial.commit3f4a7322008-07-27 06:49:38 +0900107
108 file1.close();
109 file2.close();
110 return true;
111}
112
mark@chromium.org95c9ec92009-06-27 06:17:24 +0900113bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
114 std::ifstream file1(filename1.value().c_str(), std::ios::in);
115 std::ifstream file2(filename2.value().c_str(), std::ios::in);
116
117 // Even if both files aren't openable (and thus, in some sense, "equal"),
118 // any unusable file yields a result of "false".
119 if (!file1.is_open() || !file2.is_open())
120 return false;
121
122 do {
123 std::string line1, line2;
124 getline(file1, line1);
125 getline(file2, line2);
126
127 // Check for mismatched EOF states, or any error state.
128 if ((file1.eof() != file2.eof()) ||
129 file1.bad() || file2.bad()) {
130 return false;
131 }
132
133 // Trim all '\r' and '\n' characters from the end of the line.
134 std::string::size_type end1 = line1.find_last_not_of("\r\n");
135 if (end1 == std::string::npos)
136 line1.clear();
137 else if (end1 + 1 < line1.length())
138 line1.erase(end1 + 1);
139
140 std::string::size_type end2 = line2.find_last_not_of("\r\n");
141 if (end2 == std::string::npos)
142 line2.clear();
143 else if (end2 + 1 < line2.length())
144 line2.erase(end2 + 1);
145
146 if (line1 != line2)
147 return false;
148 } while (!file1.eof() || !file2.eof());
149
150 return true;
151}
152
erikkay@google.com52460fb2009-01-28 09:22:46 +0900153bool ReadFileToString(const FilePath& path, std::string* contents) {
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900154 FILE* file = OpenFile(path, "rb");
155 if (!file) {
initial.commit3f4a7322008-07-27 06:49:38 +0900156 return false;
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900157 }
initial.commit3f4a7322008-07-27 06:49:38 +0900158
159 char buf[1 << 16];
160 size_t len;
161 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
evan@chromium.orga75b2e82010-05-19 20:07:55 +0900162 if (contents)
163 contents->append(buf, len);
initial.commit3f4a7322008-07-27 06:49:38 +0900164 }
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900165 CloseFile(file);
initial.commit3f4a7322008-07-27 06:49:38 +0900166
167 return true;
168}
169
tfarina@chromium.org34828222010-05-26 10:40:12 +0900170bool IsDirectoryEmpty(const FilePath& dir_path) {
171 FileEnumerator files(dir_path, false,
172 static_cast<FileEnumerator::FILE_TYPE>(
173 FileEnumerator::FILES | FileEnumerator::DIRECTORIES));
174 if (files.Next().value().empty())
175 return true;
176 return false;
177}
178
phajdan.jr@chromium.org8139fe12009-04-28 15:50:36 +0900179FILE* CreateAndOpenTemporaryFile(FilePath* path) {
180 FilePath directory;
181 if (!GetTempDir(&directory))
evan@chromium.org2abe0b42010-06-11 07:56:23 +0900182 return NULL;
phajdan.jr@chromium.org8139fe12009-04-28 15:50:36 +0900183
184 return CreateAndOpenTemporaryFileInDir(directory, path);
185}
186
dkegel@google.com44982682008-11-05 06:00:46 +0900187bool GetFileSize(const FilePath& file_path, int64* file_size) {
dumi@chromium.org97ae2612010-09-03 11:28:37 +0900188 base::PlatformFileInfo info;
darin@google.com7f479f22008-09-26 10:04:08 +0900189 if (!GetFileInfo(file_path, &info))
190 return false;
191 *file_size = info.size;
192 return true;
193}
194
estade@chromium.orga99d0e12010-02-12 08:27:47 +0900195bool IsDot(const FilePath& path) {
196 return FILE_PATH_LITERAL(".") == path.BaseName().value();
197}
198
199bool IsDotDot(const FilePath& path) {
200 return FILE_PATH_LITERAL("..") == path.BaseName().value();
201}
202
dumi@chromium.orgc941a182010-09-24 08:28:22 +0900203bool TouchFile(const FilePath& path,
204 const base::Time& last_accessed,
205 const base::Time& last_modified) {
206 base::PlatformFile file =
207 base::CreatePlatformFile(path,
208 base::PLATFORM_FILE_OPEN |
209 base::PLATFORM_FILE_WRITE_ATTRIBUTES,
210 NULL, NULL);
211 if (file != base::kInvalidPlatformFileValue) {
212 bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
213 base::ClosePlatformFile(file);
214 return result;
215 }
216
217 return false;
218}
219
220bool SetLastModifiedTime(const FilePath& path,
221 const base::Time& last_modified) {
222 return TouchFile(path, last_modified, last_modified);
223}
224
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900225bool CloseFile(FILE* file) {
sidchat@google.comd3b26432008-10-22 02:14:45 +0900226 if (file == NULL)
227 return true;
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900228 return fclose(file) == 0;
229}
230
jeremy@chromium.orga5f404d2009-01-28 04:08:39 +0900231bool TruncateFile(FILE* file) {
232 if (file == NULL)
233 return false;
234 long current_offset = ftell(file);
235 if (current_offset == -1)
236 return false;
237#if defined(OS_WIN)
238 int fd = _fileno(file);
239 if (_chsize(fd, current_offset) != 0)
240 return false;
241#else
242 int fd = fileno(file);
243 if (ftruncate(fd, current_offset) != 0)
244 return false;
245#endif
246 return true;
247}
248
aa@chromium.orga4dbdf22009-01-10 07:14:27 +0900249bool ContainsPath(const FilePath &parent, const FilePath& child) {
250 FilePath abs_parent = FilePath(parent);
251 FilePath abs_child = FilePath(child);
252
253 if (!file_util::AbsolutePath(&abs_parent) ||
254 !file_util::AbsolutePath(&abs_child))
255 return false;
256
257#if defined(OS_WIN)
258 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
259 // a case-insensitive compare.
260 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
261#else
262 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
263#endif
264 return false;
265
266 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
267 // to check kSeparators[0].
268 if (abs_child.value().length() <= abs_parent.value().length() ||
269 abs_child.value()[abs_parent.value().length()] !=
270 FilePath::kSeparators[0])
271 return false;
272
273 return true;
274}
275
cpu@chromium.org83f07be2010-03-25 06:56:26 +0900276int64 ComputeDirectorySize(const FilePath& root_path) {
277 int64 running_size = 0;
278 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
279 for (FilePath current = file_iter.Next(); !current.empty();
280 current = file_iter.Next()) {
281 FileEnumerator::FindInfo info;
282 file_iter.GetFindInfo(&info);
283#if defined(OS_WIN)
284 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
285 running_size += li.QuadPart;
286#else
287 running_size += info.stat.st_size;
288#endif
289 }
290 return running_size;
291}
292
rvargas@google.comaa24e112010-06-12 07:53:43 +0900293int64 ComputeFilesSize(const FilePath& directory,
294 const FilePath::StringType& pattern) {
295 int64 running_size = 0;
296 FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
297 for (FilePath current = file_iter.Next(); !current.empty();
298 current = file_iter.Next()) {
299 FileEnumerator::FindInfo info;
300 file_iter.GetFindInfo(&info);
301#if defined(OS_WIN)
302 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
303 running_size += li.QuadPart;
304#else
305 running_size += info.stat.st_size;
306#endif
307 }
308 return running_size;
309}
310
estade@chromium.org2c233532008-12-13 08:43:03 +0900311///////////////////////////////////////////////
312// MemoryMappedFile
313
314MemoryMappedFile::~MemoryMappedFile() {
315 CloseHandles();
316}
317
erg@google.com37c078e2011-01-11 09:50:59 +0900318bool MemoryMappedFile::Initialize(const FilePath& file_name) {
319 if (IsValid())
320 return false;
321
322 if (!MapFileToMemory(file_name)) {
323 CloseHandles();
324 return false;
325 }
326
327 return true;
328}
329
estade@chromium.org3ac32062009-11-17 07:55:17 +0900330bool MemoryMappedFile::Initialize(base::PlatformFile file) {
331 if (IsValid())
332 return false;
333
334 file_ = file;
335
336 if (!MapFileToMemoryInternal()) {
337 CloseHandles();
338 return false;
339 }
340
341 return true;
342}
343
tommi@chromium.org39209952011-02-24 06:42:48 +0900344bool MemoryMappedFile::IsValid() const {
erg@google.com37c078e2011-01-11 09:50:59 +0900345 return data_ != NULL;
estade@chromium.org2c233532008-12-13 08:43:03 +0900346}
347
estade@chromium.org3ac32062009-11-17 07:55:17 +0900348bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
dumi@chromium.org50f197d2010-09-01 04:30:27 +0900349 file_ = base::CreatePlatformFile(
350 file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
351 NULL, NULL);
estade@chromium.org3ac32062009-11-17 07:55:17 +0900352
353 if (file_ == base::kInvalidPlatformFileValue) {
354 LOG(ERROR) << "Couldn't open " << file_name.value();
355 return false;
356 }
357
358 return MapFileToMemoryInternal();
359}
360
evanm@google.com874d1672008-10-31 08:54:04 +0900361// Deprecated functions ----------------------------------------------------
362
evan@chromium.org66893d62010-10-14 07:43:06 +0900363#if defined(OS_WIN)
estade@chromium.org97e37822008-11-27 13:03:57 +0900364void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
365 if (!path) {
366 NOTREACHED();
367 return; // Don't crash in this function in release builds.
368 }
369
tfarina@chromium.org4a258932010-06-21 12:05:19 +0900370 if (!EndsWithSeparator(FilePath(*path)))
estade@chromium.org97e37822008-11-27 13:03:57 +0900371 path->push_back(FilePath::kSeparators[0]);
372 path->append(new_ending);
evanm@google.com874d1672008-10-31 08:54:04 +0900373}
evan@chromium.org1db7f942010-02-27 00:11:55 +0900374
evanm@google.com874d1672008-10-31 08:54:04 +0900375bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
376 bool recursive) {
377 return CopyDirectory(FilePath::FromWStringHack(from_path),
378 FilePath::FromWStringHack(to_path),
379 recursive);
380}
estade@chromium.org97e37822008-11-27 13:03:57 +0900381bool Delete(const std::wstring& path, bool recursive) {
382 return Delete(FilePath::FromWStringHack(path), recursive);
383}
estade@chromium.orgae508942009-01-07 08:31:41 +0900384std::wstring GetFileExtensionFromPath(const std::wstring& path) {
385 FilePath::StringType extension =
386 GetFileExtensionFromPath(FilePath::FromWStringHack(path));
estade@chromium.orgae508942009-01-07 08:31:41 +0900387 return extension;
estade@chromium.orgae508942009-01-07 08:31:41 +0900388}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900389FILE* OpenFile(const std::wstring& filename, const char* mode) {
390 return OpenFile(FilePath::FromWStringHack(filename), mode);
391}
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900392int ReadFile(const std::wstring& filename, char* data, int size) {
393 return ReadFile(FilePath::FromWStringHack(filename), data, size);
394}
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900395int WriteFile(const std::wstring& filename, const char* data, int size) {
396 return WriteFile(FilePath::FromWStringHack(filename), data, size);
397}
evan@chromium.org66893d62010-10-14 07:43:06 +0900398#endif // OS_WIN
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900399
400///////////////////////////////////////////////
401// FileEnumerator
402//
403// Note: the main logic is in file_util_<platform>.cc
404
405bool FileEnumerator::ShouldSkip(const FilePath& path) {
406 FilePath::StringType basename = path.BaseName().value();
407 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
408}
409
initial.commit3f4a7322008-07-27 06:49:38 +0900410} // namespace