blob: 5f186742a71aa3da5811faab50b04d7d69a21dab [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 +090029void PathComponents(const FilePath& path,
30 std::vector<FilePath::StringType>* components) {
31 DCHECK(components);
32 if (!components)
erikkay@google.comdfb51b22008-08-16 02:32:10 +090033 return;
erikkay@google.comdfb51b22008-08-16 02:32:10 +090034
estade@chromium.org97e37822008-11-27 13:03:57 +090035 FilePath::StringType path_str = path.value();
36 FilePath::StringType::size_type start = 0;
37 FilePath::StringType::size_type end =
38 path_str.find_first_of(FilePath::kSeparators);
39
40 // If the path starts with a separator, add it to components.
erikkay@google.comdfb51b22008-08-16 02:32:10 +090041 if (end == start) {
estade@chromium.org97e37822008-11-27 13:03:57 +090042 components->push_back(FilePath::StringType(path_str, 0, 1));
erikkay@google.comdfb51b22008-08-16 02:32:10 +090043 start = end + 1;
estade@chromium.org97e37822008-11-27 13:03:57 +090044 end = path_str.find_first_of(FilePath::kSeparators, start);
erikkay@google.comdfb51b22008-08-16 02:32:10 +090045 }
estade@chromium.org97e37822008-11-27 13:03:57 +090046 while (end != FilePath::StringType::npos) {
47 FilePath::StringType component =
48 FilePath::StringType(path_str, start, end - start);
erikkay@google.comdfb51b22008-08-16 02:32:10 +090049 components->push_back(component);
50 start = end + 1;
estade@chromium.org97e37822008-11-27 13:03:57 +090051 end = path_str.find_first_of(FilePath::kSeparators, start);
erikkay@google.comdfb51b22008-08-16 02:32:10 +090052 }
estade@chromium.org97e37822008-11-27 13:03:57 +090053
54 components->push_back(FilePath::StringType(path_str, start));
erikkay@google.comdfb51b22008-08-16 02:32:10 +090055}
estade@chromium.orgd778b8f2008-11-26 07:04:37 +090056
estade@chromium.org97e37822008-11-27 13:03:57 +090057bool EndsWithSeparator(const FilePath& path) {
58 FilePath::StringType value = path.value();
59 if (value.empty())
60 return false;
61
62 return FilePath::IsSeparator(value[value.size() - 1]);
initial.commit3f4a7322008-07-27 06:49:38 +090063}
64
estade@chromium.orgd778b8f2008-11-26 07:04:37 +090065bool EnsureEndsWithSeparator(FilePath* path) {
66 if (!DirectoryExists(*path))
67 return false;
68
69 if (EndsWithSeparator(*path))
70 return true;
71
72 FilePath::StringType& path_str =
73 const_cast<FilePath::StringType&>(path->value());
74 path_str.append(&FilePath::kSeparators[0], 1);
75
76 return true;
77}
78
thestig@chromium.org665cfcf2009-04-22 08:15:45 +090079void TrimTrailingSeparator(std::wstring* dir) {
80 while (dir->length() > 1 && EndsWithSeparator(dir))
81 dir->resize(dir->length() - 1);
82}
83
estade@chromium.orgae508942009-01-07 08:31:41 +090084FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
85 FilePath::StringType file_name = path.BaseName().value();
86 const FilePath::StringType::size_type last_dot =
87 file_name.rfind(kExtensionSeparator);
88 return FilePath::StringType(last_dot == FilePath::StringType::npos ?
89 FILE_PATH_LITERAL("") :
90 file_name, last_dot+1);
glen@chromium.org40eb4922008-11-04 01:14:46 +090091}
92
estade@chromium.org63343202008-12-05 05:46:06 +090093void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
94 FilePath::StringType& value =
95 const_cast<FilePath::StringType&>(path->value());
96
97 const FilePath::StringType::size_type last_dot =
98 value.rfind(kExtensionSeparator);
99 const FilePath::StringType::size_type last_separator =
100 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
101
102 if (last_dot == FilePath::StringType::npos ||
103 (last_separator != std::wstring::npos && last_dot < last_separator)) {
104 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
105 // We should just append the suffix to the entire path.
106 value.append(suffix);
107 return;
108 }
109
110 value.insert(last_dot, suffix);
111}
112
113void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
114 FilePath::StringType clean_extension;
115 // If the new extension is "" or ".", then we will just remove the current
116 // extension.
117 if (!extension.empty() &&
118 extension != FilePath::StringType(&kExtensionSeparator, 1)) {
119 if (extension[0] != kExtensionSeparator)
120 clean_extension.append(&kExtensionSeparator, 1);
121 clean_extension.append(extension);
122 }
123
124 FilePath::StringType& value =
125 const_cast<FilePath::StringType&>(path->value());
126 const FilePath::StringType::size_type last_dot =
127 value.rfind(kExtensionSeparator);
128 const FilePath::StringType::size_type last_separator =
129 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
130
131 // Erase the current extension, if any.
132 if ((last_dot > last_separator ||
133 last_separator == FilePath::StringType::npos) &&
134 last_dot != FilePath::StringType::npos)
135 value.erase(last_dot);
136
137 value.append(clean_extension);
138}
139
evanm@google.com874d1672008-10-31 08:54:04 +0900140bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commit3f4a7322008-07-27 06:49:38 +0900141 // We open the file in binary format even if they are text files because
142 // we are just comparing that bytes are exactly same in both files and not
143 // doing anything smart with text formatting.
evanm@google.com874d1672008-10-31 08:54:04 +0900144 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com9fc57d02008-08-09 05:16:08 +0900145 std::ios::in | std::ios::binary);
evanm@google.com874d1672008-10-31 08:54:04 +0900146 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com9fc57d02008-08-09 05:16:08 +0900147 std::ios::in | std::ios::binary);
estade@chromium.org97e37822008-11-27 13:03:57 +0900148
initial.commit3f4a7322008-07-27 06:49:38 +0900149 // Even if both files aren't openable (and thus, in some sense, "equal"),
150 // any unusable file yields a result of "false".
151 if (!file1.is_open() || !file2.is_open())
152 return false;
153
154 const int BUFFER_SIZE = 2056;
155 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
156 do {
157 file1.read(buffer1, BUFFER_SIZE);
158 file2.read(buffer2, BUFFER_SIZE);
159
160 if ((file1.eof() && !file2.eof()) ||
161 (!file1.eof() && file2.eof()) ||
162 (file1.gcount() != file2.gcount()) ||
163 (memcmp(buffer1, buffer2, file1.gcount()))) {
164 file1.close();
165 file2.close();
166 return false;
167 }
168 } while (!file1.eof() && !file2.eof());
169
170 file1.close();
171 file2.close();
172 return true;
173}
174
erikkay@google.com52460fb2009-01-28 09:22:46 +0900175bool ReadFileToString(const FilePath& path, std::string* contents) {
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900176 FILE* file = OpenFile(path, "rb");
177 if (!file) {
initial.commit3f4a7322008-07-27 06:49:38 +0900178 return false;
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900179 }
initial.commit3f4a7322008-07-27 06:49:38 +0900180
181 char buf[1 << 16];
182 size_t len;
183 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
184 contents->append(buf, len);
185 }
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900186 CloseFile(file);
initial.commit3f4a7322008-07-27 06:49:38 +0900187
188 return true;
189}
190
phajdan.jr@chromium.org8139fe12009-04-28 15:50:36 +0900191FILE* CreateAndOpenTemporaryFile(FilePath* path) {
192 FilePath directory;
193 if (!GetTempDir(&directory))
194 return false;
195
196 return CreateAndOpenTemporaryFileInDir(directory, path);
197}
198
dkegel@google.com44982682008-11-05 06:00:46 +0900199bool GetFileSize(const FilePath& file_path, int64* file_size) {
darin@google.com7f479f22008-09-26 10:04:08 +0900200 FileInfo info;
201 if (!GetFileInfo(file_path, &info))
202 return false;
203 *file_size = info.size;
204 return true;
205}
206
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900207bool CloseFile(FILE* file) {
sidchat@google.comd3b26432008-10-22 02:14:45 +0900208 if (file == NULL)
209 return true;
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900210 return fclose(file) == 0;
211}
212
jeremy@chromium.orga5f404d2009-01-28 04:08:39 +0900213bool TruncateFile(FILE* file) {
214 if (file == NULL)
215 return false;
216 long current_offset = ftell(file);
217 if (current_offset == -1)
218 return false;
219#if defined(OS_WIN)
220 int fd = _fileno(file);
221 if (_chsize(fd, current_offset) != 0)
222 return false;
223#else
224 int fd = fileno(file);
225 if (ftruncate(fd, current_offset) != 0)
226 return false;
227#endif
228 return true;
229}
230
aa@chromium.orga4dbdf22009-01-10 07:14:27 +0900231bool ContainsPath(const FilePath &parent, const FilePath& child) {
232 FilePath abs_parent = FilePath(parent);
233 FilePath abs_child = FilePath(child);
234
235 if (!file_util::AbsolutePath(&abs_parent) ||
236 !file_util::AbsolutePath(&abs_child))
237 return false;
238
239#if defined(OS_WIN)
240 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
241 // a case-insensitive compare.
242 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
243#else
244 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
245#endif
246 return false;
247
248 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
249 // to check kSeparators[0].
250 if (abs_child.value().length() <= abs_parent.value().length() ||
251 abs_child.value()[abs_parent.value().length()] !=
252 FilePath::kSeparators[0])
253 return false;
254
255 return true;
256}
257
estade@chromium.org2c233532008-12-13 08:43:03 +0900258///////////////////////////////////////////////
259// MemoryMappedFile
260
261MemoryMappedFile::~MemoryMappedFile() {
262 CloseHandles();
263}
264
265bool MemoryMappedFile::Initialize(const FilePath& file_name) {
266 if (IsValid())
267 return false;
268
269 if (!MapFileToMemory(file_name)) {
270 CloseHandles();
271 return false;
272 }
273
274 return true;
275}
276
277bool MemoryMappedFile::IsValid() {
278 return data_ != NULL;
279}
280
evanm@google.com874d1672008-10-31 08:54:04 +0900281// Deprecated functions ----------------------------------------------------
282
erikkay@google.com52460fb2009-01-28 09:22:46 +0900283bool ReadFileToString(const std::wstring& path, std::string* contents) {
284 return ReadFileToString(FilePath::FromWStringHack(path), contents);
285}
286
evanm@google.com874d1672008-10-31 08:54:04 +0900287bool AbsolutePath(std::wstring* path_str) {
evanm@google.com104f22e2008-10-31 11:03:07 +0900288 FilePath path(FilePath::FromWStringHack(*path_str));
evanm@google.com874d1672008-10-31 08:54:04 +0900289 if (!AbsolutePath(&path))
290 return false;
291 *path_str = path.ToWStringHack();
292 return true;
293}
estade@chromium.org97e37822008-11-27 13:03:57 +0900294void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
295 if (!path) {
296 NOTREACHED();
297 return; // Don't crash in this function in release builds.
298 }
299
300 if (!EndsWithSeparator(path))
301 path->push_back(FilePath::kSeparators[0]);
302 path->append(new_ending);
evanm@google.com874d1672008-10-31 08:54:04 +0900303}
304bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
305 bool recursive) {
306 return CopyDirectory(FilePath::FromWStringHack(from_path),
307 FilePath::FromWStringHack(to_path),
308 recursive);
309}
evanm@google.com874d1672008-10-31 08:54:04 +0900310bool ContentsEqual(const std::wstring& filename1,
311 const std::wstring& filename2) {
312 return ContentsEqual(FilePath::FromWStringHack(filename1),
313 FilePath::FromWStringHack(filename2));
314}
estade@chromium.org97e37822008-11-27 13:03:57 +0900315bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
316 return CopyFile(FilePath::FromWStringHack(from_path),
317 FilePath::FromWStringHack(to_path));
318}
evanm@google.com874d1672008-10-31 08:54:04 +0900319bool CreateDirectory(const std::wstring& full_path) {
320 return CreateDirectory(FilePath::FromWStringHack(full_path));
321}
erikkay@google.comcce83822008-12-24 05:20:10 +0900322bool CreateNewTempDirectory(const std::wstring& prefix,
323 std::wstring* new_temp_path) {
324#if defined(OS_WIN)
325 FilePath::StringType dir_prefix(prefix);
326#elif defined(OS_POSIX)
327 FilePath::StringType dir_prefix = WideToUTF8(prefix);
328#endif
329 FilePath temp_path;
330 if (!CreateNewTempDirectory(dir_prefix, &temp_path))
331 return false;
332 *new_temp_path = temp_path.ToWStringHack();
333 return true;
334}
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900335bool CreateTemporaryFileName(std::wstring* temp_file) {
336 FilePath temp_file_path;
337 if (!CreateTemporaryFileName(&temp_file_path))
338 return false;
339 *temp_file = temp_file_path.ToWStringHack();
340 return true;
341}
estade@chromium.org97e37822008-11-27 13:03:57 +0900342bool Delete(const std::wstring& path, bool recursive) {
343 return Delete(FilePath::FromWStringHack(path), recursive);
344}
345bool DirectoryExists(const std::wstring& path) {
346 return DirectoryExists(FilePath::FromWStringHack(path));
347}
348bool EndsWithSeparator(std::wstring* path) {
349 return EndsWithSeparator(FilePath::FromWStringHack(*path));
350}
351bool EndsWithSeparator(const std::wstring& path) {
352 return EndsWithSeparator(FilePath::FromWStringHack(path));
353}
evanm@google.com874d1672008-10-31 08:54:04 +0900354bool GetCurrentDirectory(std::wstring* path_str) {
355 FilePath path;
356 if (!GetCurrentDirectory(&path))
357 return false;
358 *path_str = path.ToWStringHack();
359 return true;
360}
estade@chromium.orgae508942009-01-07 08:31:41 +0900361std::wstring GetFileExtensionFromPath(const std::wstring& path) {
362 FilePath::StringType extension =
363 GetFileExtensionFromPath(FilePath::FromWStringHack(path));
364#if defined(OS_WIN)
365 return extension;
366#elif defined(OS_POSIX)
367 return UTF8ToWide(extension);
368#endif
369}
dkegel@google.com44982682008-11-05 06:00:46 +0900370bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
371 return GetFileInfo(FilePath::FromWStringHack(file_path), results);
372}
estade@chromium.org63343202008-12-05 05:46:06 +0900373std::wstring GetFilenameFromPath(const std::wstring& path) {
374 if (path.empty() || EndsWithSeparator(path))
375 return std::wstring();
376
estade@chromium.orgd872d682009-01-06 08:59:36 +0900377 return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
estade@chromium.org63343202008-12-05 05:46:06 +0900378}
dkegel@google.com44982682008-11-05 06:00:46 +0900379bool GetFileSize(const std::wstring& file_path, int64* file_size) {
380 return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
381}
evanm@google.com874d1672008-10-31 08:54:04 +0900382bool GetTempDir(std::wstring* path_str) {
383 FilePath path;
384 if (!GetTempDir(&path))
385 return false;
386 *path_str = path.ToWStringHack();
387 return true;
388}
estade@chromium.org97e37822008-11-27 13:03:57 +0900389bool Move(const std::wstring& from_path, const std::wstring& to_path) {
390 return Move(FilePath::FromWStringHack(from_path),
391 FilePath::FromWStringHack(to_path));
392}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900393FILE* OpenFile(const std::wstring& filename, const char* mode) {
394 return OpenFile(FilePath::FromWStringHack(filename), mode);
395}
estade@chromium.org97e37822008-11-27 13:03:57 +0900396bool PathExists(const std::wstring& path) {
397 return PathExists(FilePath::FromWStringHack(path));
398}
erikkay@google.comcce83822008-12-24 05:20:10 +0900399bool PathIsWritable(const std::wstring& path) {
400 return PathIsWritable(FilePath::FromWStringHack(path));
401}
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900402int ReadFile(const std::wstring& filename, char* data, int size) {
403 return ReadFile(FilePath::FromWStringHack(filename), data, size);
404}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900405bool SetCurrentDirectory(const std::wstring& directory) {
406 return SetCurrentDirectory(FilePath::FromWStringHack(directory));
407}
estade@chromium.org97e37822008-11-27 13:03:57 +0900408void UpOneDirectory(std::wstring* dir) {
409 FilePath path = FilePath::FromWStringHack(*dir);
410 FilePath directory = path.DirName();
411 // If there is no separator, we will get back kCurrentDirectory.
412 // In this case don't change |dir|.
413 if (directory.value() != FilePath::kCurrentDirectory)
414 *dir = directory.ToWStringHack();
415}
416void UpOneDirectoryOrEmpty(std::wstring* dir) {
417 FilePath path = FilePath::FromWStringHack(*dir);
418 FilePath directory = path.DirName();
419 // If there is no separator, we will get back kCurrentDirectory.
420 // In this case, clear dir.
421 if (directory == path || directory.value() == FilePath::kCurrentDirectory)
422 dir->clear();
423 else
424 *dir = directory.ToWStringHack();
425}
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900426int WriteFile(const std::wstring& filename, const char* data, int size) {
427 return WriteFile(FilePath::FromWStringHack(filename), data, size);
428}
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900429
430///////////////////////////////////////////////
431// FileEnumerator
432//
433// Note: the main logic is in file_util_<platform>.cc
434
435bool FileEnumerator::ShouldSkip(const FilePath& path) {
436 FilePath::StringType basename = path.BaseName().value();
437 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
438}
439
440bool FileEnumerator::IsDot(const FilePath& path) {
441 return FILE_PATH_LITERAL(".") == path.BaseName().value();
442}
443
444bool FileEnumerator::IsDotDot(const FilePath& path) {
445 return FILE_PATH_LITERAL("..") == path.BaseName().value();
446}
447
initial.commit3f4a7322008-07-27 06:49:38 +0900448} // namespace