blob: 505dfb4c07a9fd74d3e0b10ee1837811f825d903 [file] [log] [blame]
sra@chromium.org329849d2009-04-08 06:02:11 +09001// Copyright (c) 2006-2009 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"
initial.commit3f4a7322008-07-27 06:49:38 +090016#include "base/string_util.h"
initial.commit3f4a7322008-07-27 06:49:38 +090017
estade@chromium.org97e37822008-11-27 13:03:57 +090018#include "base/string_piece.h"
19#include "base/sys_string_conversions.h"
20
estade@chromium.org63343202008-12-05 05:46:06 +090021namespace {
22
23const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
24
willchan@chromium.org2f0b26b2009-04-25 02:44:39 +090025} // namespace
estade@chromium.org63343202008-12-05 05:46:06 +090026
initial.commit3f4a7322008-07-27 06:49:38 +090027namespace file_util {
28
estade@chromium.org97e37822008-11-27 13:03:57 +090029bool EndsWithSeparator(const FilePath& path) {
30 FilePath::StringType value = path.value();
31 if (value.empty())
32 return false;
33
34 return FilePath::IsSeparator(value[value.size() - 1]);
initial.commit3f4a7322008-07-27 06:49:38 +090035}
36
estade@chromium.orgd778b8f2008-11-26 07:04:37 +090037bool EnsureEndsWithSeparator(FilePath* path) {
38 if (!DirectoryExists(*path))
39 return false;
40
41 if (EndsWithSeparator(*path))
42 return true;
43
44 FilePath::StringType& path_str =
45 const_cast<FilePath::StringType&>(path->value());
46 path_str.append(&FilePath::kSeparators[0], 1);
47
48 return true;
49}
50
thestig@chromium.org665cfcf2009-04-22 08:15:45 +090051void TrimTrailingSeparator(std::wstring* dir) {
52 while (dir->length() > 1 && EndsWithSeparator(dir))
53 dir->resize(dir->length() - 1);
54}
55
estade@chromium.orgae508942009-01-07 08:31:41 +090056FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
57 FilePath::StringType file_name = path.BaseName().value();
58 const FilePath::StringType::size_type last_dot =
59 file_name.rfind(kExtensionSeparator);
60 return FilePath::StringType(last_dot == FilePath::StringType::npos ?
61 FILE_PATH_LITERAL("") :
62 file_name, last_dot+1);
glen@chromium.org40eb4922008-11-04 01:14:46 +090063}
64
estade@chromium.org63343202008-12-05 05:46:06 +090065void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
66 FilePath::StringType& value =
67 const_cast<FilePath::StringType&>(path->value());
68
69 const FilePath::StringType::size_type last_dot =
70 value.rfind(kExtensionSeparator);
71 const FilePath::StringType::size_type last_separator =
72 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
73
74 if (last_dot == FilePath::StringType::npos ||
75 (last_separator != std::wstring::npos && last_dot < last_separator)) {
76 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
77 // We should just append the suffix to the entire path.
78 value.append(suffix);
79 return;
80 }
81
82 value.insert(last_dot, suffix);
83}
84
85void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
86 FilePath::StringType clean_extension;
87 // If the new extension is "" or ".", then we will just remove the current
88 // extension.
89 if (!extension.empty() &&
90 extension != FilePath::StringType(&kExtensionSeparator, 1)) {
91 if (extension[0] != kExtensionSeparator)
92 clean_extension.append(&kExtensionSeparator, 1);
93 clean_extension.append(extension);
94 }
95
96 FilePath::StringType& value =
97 const_cast<FilePath::StringType&>(path->value());
98 const FilePath::StringType::size_type last_dot =
99 value.rfind(kExtensionSeparator);
100 const FilePath::StringType::size_type last_separator =
101 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
102
103 // Erase the current extension, if any.
104 if ((last_dot > last_separator ||
105 last_separator == FilePath::StringType::npos) &&
106 last_dot != FilePath::StringType::npos)
107 value.erase(last_dot);
108
109 value.append(clean_extension);
110}
111
evanm@google.com874d1672008-10-31 08:54:04 +0900112bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commit3f4a7322008-07-27 06:49:38 +0900113 // We open the file in binary format even if they are text files because
114 // we are just comparing that bytes are exactly same in both files and not
115 // doing anything smart with text formatting.
evanm@google.com874d1672008-10-31 08:54:04 +0900116 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com9fc57d02008-08-09 05:16:08 +0900117 std::ios::in | std::ios::binary);
evanm@google.com874d1672008-10-31 08:54:04 +0900118 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com9fc57d02008-08-09 05:16:08 +0900119 std::ios::in | std::ios::binary);
estade@chromium.org97e37822008-11-27 13:03:57 +0900120
initial.commit3f4a7322008-07-27 06:49:38 +0900121 // Even if both files aren't openable (and thus, in some sense, "equal"),
122 // any unusable file yields a result of "false".
123 if (!file1.is_open() || !file2.is_open())
124 return false;
125
126 const int BUFFER_SIZE = 2056;
127 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
128 do {
129 file1.read(buffer1, BUFFER_SIZE);
130 file2.read(buffer2, BUFFER_SIZE);
131
mark@chromium.org95c9ec92009-06-27 06:17:24 +0900132 if ((file1.eof() != file2.eof()) ||
initial.commit3f4a7322008-07-27 06:49:38 +0900133 (file1.gcount() != file2.gcount()) ||
134 (memcmp(buffer1, buffer2, file1.gcount()))) {
135 file1.close();
136 file2.close();
137 return false;
138 }
mark@chromium.org95c9ec92009-06-27 06:17:24 +0900139 } while (!file1.eof() || !file2.eof());
initial.commit3f4a7322008-07-27 06:49:38 +0900140
141 file1.close();
142 file2.close();
143 return true;
144}
145
mark@chromium.org95c9ec92009-06-27 06:17:24 +0900146bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
147 std::ifstream file1(filename1.value().c_str(), std::ios::in);
148 std::ifstream file2(filename2.value().c_str(), std::ios::in);
149
150 // Even if both files aren't openable (and thus, in some sense, "equal"),
151 // any unusable file yields a result of "false".
152 if (!file1.is_open() || !file2.is_open())
153 return false;
154
155 do {
156 std::string line1, line2;
157 getline(file1, line1);
158 getline(file2, line2);
159
160 // Check for mismatched EOF states, or any error state.
161 if ((file1.eof() != file2.eof()) ||
162 file1.bad() || file2.bad()) {
163 return false;
164 }
165
166 // Trim all '\r' and '\n' characters from the end of the line.
167 std::string::size_type end1 = line1.find_last_not_of("\r\n");
168 if (end1 == std::string::npos)
169 line1.clear();
170 else if (end1 + 1 < line1.length())
171 line1.erase(end1 + 1);
172
173 std::string::size_type end2 = line2.find_last_not_of("\r\n");
174 if (end2 == std::string::npos)
175 line2.clear();
176 else if (end2 + 1 < line2.length())
177 line2.erase(end2 + 1);
178
179 if (line1 != line2)
180 return false;
181 } while (!file1.eof() || !file2.eof());
182
183 return true;
184}
185
erikkay@google.com52460fb2009-01-28 09:22:46 +0900186bool ReadFileToString(const FilePath& path, std::string* contents) {
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900187 FILE* file = OpenFile(path, "rb");
188 if (!file) {
initial.commit3f4a7322008-07-27 06:49:38 +0900189 return false;
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900190 }
initial.commit3f4a7322008-07-27 06:49:38 +0900191
192 char buf[1 << 16];
193 size_t len;
194 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
195 contents->append(buf, len);
196 }
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900197 CloseFile(file);
initial.commit3f4a7322008-07-27 06:49:38 +0900198
199 return true;
200}
201
phajdan.jr@chromium.org8139fe12009-04-28 15:50:36 +0900202FILE* CreateAndOpenTemporaryFile(FilePath* path) {
203 FilePath directory;
204 if (!GetTempDir(&directory))
205 return false;
206
207 return CreateAndOpenTemporaryFileInDir(directory, path);
208}
209
dkegel@google.com44982682008-11-05 06:00:46 +0900210bool GetFileSize(const FilePath& file_path, int64* file_size) {
darin@google.com7f479f22008-09-26 10:04:08 +0900211 FileInfo info;
212 if (!GetFileInfo(file_path, &info))
213 return false;
214 *file_size = info.size;
215 return true;
216}
217
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900218bool CloseFile(FILE* file) {
sidchat@google.comd3b26432008-10-22 02:14:45 +0900219 if (file == NULL)
220 return true;
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900221 return fclose(file) == 0;
222}
223
jeremy@chromium.orga5f404d2009-01-28 04:08:39 +0900224bool TruncateFile(FILE* file) {
225 if (file == NULL)
226 return false;
227 long current_offset = ftell(file);
228 if (current_offset == -1)
229 return false;
230#if defined(OS_WIN)
231 int fd = _fileno(file);
232 if (_chsize(fd, current_offset) != 0)
233 return false;
234#else
235 int fd = fileno(file);
236 if (ftruncate(fd, current_offset) != 0)
237 return false;
238#endif
239 return true;
240}
241
aa@chromium.orga4dbdf22009-01-10 07:14:27 +0900242bool ContainsPath(const FilePath &parent, const FilePath& child) {
243 FilePath abs_parent = FilePath(parent);
244 FilePath abs_child = FilePath(child);
245
246 if (!file_util::AbsolutePath(&abs_parent) ||
247 !file_util::AbsolutePath(&abs_child))
248 return false;
249
250#if defined(OS_WIN)
251 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
252 // a case-insensitive compare.
253 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
254#else
255 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
256#endif
257 return false;
258
259 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
260 // to check kSeparators[0].
261 if (abs_child.value().length() <= abs_parent.value().length() ||
262 abs_child.value()[abs_parent.value().length()] !=
263 FilePath::kSeparators[0])
264 return false;
265
266 return true;
267}
268
estade@chromium.org2c233532008-12-13 08:43:03 +0900269///////////////////////////////////////////////
270// MemoryMappedFile
271
272MemoryMappedFile::~MemoryMappedFile() {
273 CloseHandles();
274}
275
276bool MemoryMappedFile::Initialize(const FilePath& file_name) {
277 if (IsValid())
278 return false;
279
280 if (!MapFileToMemory(file_name)) {
281 CloseHandles();
282 return false;
283 }
284
285 return true;
286}
287
288bool MemoryMappedFile::IsValid() {
289 return data_ != NULL;
290}
291
evanm@google.com874d1672008-10-31 08:54:04 +0900292// Deprecated functions ----------------------------------------------------
293
erikkay@google.com52460fb2009-01-28 09:22:46 +0900294bool ReadFileToString(const std::wstring& path, std::string* contents) {
295 return ReadFileToString(FilePath::FromWStringHack(path), contents);
296}
297
evanm@google.com874d1672008-10-31 08:54:04 +0900298bool AbsolutePath(std::wstring* path_str) {
evanm@google.com104f22e2008-10-31 11:03:07 +0900299 FilePath path(FilePath::FromWStringHack(*path_str));
evanm@google.com874d1672008-10-31 08:54:04 +0900300 if (!AbsolutePath(&path))
301 return false;
302 *path_str = path.ToWStringHack();
303 return true;
304}
estade@chromium.org97e37822008-11-27 13:03:57 +0900305void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
306 if (!path) {
307 NOTREACHED();
308 return; // Don't crash in this function in release builds.
309 }
310
311 if (!EndsWithSeparator(path))
312 path->push_back(FilePath::kSeparators[0]);
313 path->append(new_ending);
evanm@google.com874d1672008-10-31 08:54:04 +0900314}
315bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
316 bool recursive) {
317 return CopyDirectory(FilePath::FromWStringHack(from_path),
318 FilePath::FromWStringHack(to_path),
319 recursive);
320}
evanm@google.com874d1672008-10-31 08:54:04 +0900321bool ContentsEqual(const std::wstring& filename1,
322 const std::wstring& filename2) {
323 return ContentsEqual(FilePath::FromWStringHack(filename1),
324 FilePath::FromWStringHack(filename2));
325}
estade@chromium.org97e37822008-11-27 13:03:57 +0900326bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
327 return CopyFile(FilePath::FromWStringHack(from_path),
328 FilePath::FromWStringHack(to_path));
329}
evanm@google.com874d1672008-10-31 08:54:04 +0900330bool CreateDirectory(const std::wstring& full_path) {
331 return CreateDirectory(FilePath::FromWStringHack(full_path));
332}
erikkay@google.comcce83822008-12-24 05:20:10 +0900333bool CreateNewTempDirectory(const std::wstring& prefix,
334 std::wstring* new_temp_path) {
335#if defined(OS_WIN)
336 FilePath::StringType dir_prefix(prefix);
337#elif defined(OS_POSIX)
338 FilePath::StringType dir_prefix = WideToUTF8(prefix);
339#endif
340 FilePath temp_path;
341 if (!CreateNewTempDirectory(dir_prefix, &temp_path))
342 return false;
343 *new_temp_path = temp_path.ToWStringHack();
344 return true;
345}
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900346bool CreateTemporaryFileName(std::wstring* temp_file) {
347 FilePath temp_file_path;
348 if (!CreateTemporaryFileName(&temp_file_path))
349 return false;
350 *temp_file = temp_file_path.ToWStringHack();
351 return true;
352}
estade@chromium.org97e37822008-11-27 13:03:57 +0900353bool Delete(const std::wstring& path, bool recursive) {
354 return Delete(FilePath::FromWStringHack(path), recursive);
355}
356bool DirectoryExists(const std::wstring& path) {
357 return DirectoryExists(FilePath::FromWStringHack(path));
358}
359bool EndsWithSeparator(std::wstring* path) {
360 return EndsWithSeparator(FilePath::FromWStringHack(*path));
361}
362bool EndsWithSeparator(const std::wstring& path) {
363 return EndsWithSeparator(FilePath::FromWStringHack(path));
364}
evanm@google.com874d1672008-10-31 08:54:04 +0900365bool GetCurrentDirectory(std::wstring* path_str) {
366 FilePath path;
367 if (!GetCurrentDirectory(&path))
368 return false;
369 *path_str = path.ToWStringHack();
370 return true;
371}
estade@chromium.orgae508942009-01-07 08:31:41 +0900372std::wstring GetFileExtensionFromPath(const std::wstring& path) {
373 FilePath::StringType extension =
374 GetFileExtensionFromPath(FilePath::FromWStringHack(path));
375#if defined(OS_WIN)
376 return extension;
377#elif defined(OS_POSIX)
378 return UTF8ToWide(extension);
379#endif
380}
dkegel@google.com44982682008-11-05 06:00:46 +0900381bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
382 return GetFileInfo(FilePath::FromWStringHack(file_path), results);
383}
estade@chromium.org63343202008-12-05 05:46:06 +0900384std::wstring GetFilenameFromPath(const std::wstring& path) {
385 if (path.empty() || EndsWithSeparator(path))
386 return std::wstring();
387
estade@chromium.orgd872d682009-01-06 08:59:36 +0900388 return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
estade@chromium.org63343202008-12-05 05:46:06 +0900389}
dkegel@google.com44982682008-11-05 06:00:46 +0900390bool GetFileSize(const std::wstring& file_path, int64* file_size) {
391 return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
392}
evanm@google.com874d1672008-10-31 08:54:04 +0900393bool GetTempDir(std::wstring* path_str) {
394 FilePath path;
395 if (!GetTempDir(&path))
396 return false;
397 *path_str = path.ToWStringHack();
398 return true;
399}
estade@chromium.org97e37822008-11-27 13:03:57 +0900400bool Move(const std::wstring& from_path, const std::wstring& to_path) {
401 return Move(FilePath::FromWStringHack(from_path),
402 FilePath::FromWStringHack(to_path));
403}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900404FILE* OpenFile(const std::wstring& filename, const char* mode) {
405 return OpenFile(FilePath::FromWStringHack(filename), mode);
406}
estade@chromium.org97e37822008-11-27 13:03:57 +0900407bool PathExists(const std::wstring& path) {
408 return PathExists(FilePath::FromWStringHack(path));
409}
erikkay@google.comcce83822008-12-24 05:20:10 +0900410bool PathIsWritable(const std::wstring& path) {
411 return PathIsWritable(FilePath::FromWStringHack(path));
412}
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900413int ReadFile(const std::wstring& filename, char* data, int size) {
414 return ReadFile(FilePath::FromWStringHack(filename), data, size);
415}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900416bool SetCurrentDirectory(const std::wstring& directory) {
417 return SetCurrentDirectory(FilePath::FromWStringHack(directory));
418}
estade@chromium.org97e37822008-11-27 13:03:57 +0900419void UpOneDirectory(std::wstring* dir) {
420 FilePath path = FilePath::FromWStringHack(*dir);
421 FilePath directory = path.DirName();
422 // If there is no separator, we will get back kCurrentDirectory.
423 // In this case don't change |dir|.
424 if (directory.value() != FilePath::kCurrentDirectory)
425 *dir = directory.ToWStringHack();
426}
427void UpOneDirectoryOrEmpty(std::wstring* dir) {
428 FilePath path = FilePath::FromWStringHack(*dir);
429 FilePath directory = path.DirName();
430 // If there is no separator, we will get back kCurrentDirectory.
431 // In this case, clear dir.
432 if (directory == path || directory.value() == FilePath::kCurrentDirectory)
433 dir->clear();
434 else
435 *dir = directory.ToWStringHack();
436}
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900437int WriteFile(const std::wstring& filename, const char* data, int size) {
438 return WriteFile(FilePath::FromWStringHack(filename), data, size);
439}
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900440
441///////////////////////////////////////////////
442// FileEnumerator
443//
444// Note: the main logic is in file_util_<platform>.cc
445
446bool FileEnumerator::ShouldSkip(const FilePath& path) {
447 FilePath::StringType basename = path.BaseName().value();
448 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
449}
450
451bool FileEnumerator::IsDot(const FilePath& path) {
452 return FILE_PATH_LITERAL(".") == path.BaseName().value();
453}
454
455bool FileEnumerator::IsDotDot(const FilePath& path) {
456 return FILE_PATH_LITERAL("..") == path.BaseName().value();
457}
458
initial.commit3f4a7322008-07-27 06:49:38 +0900459} // namespace