WebKit merge 94748:94770
[chromium-blink-merge.git] / base / file_util.cc
blobe9f1d4e61f93bb910e8925b71d00fd6d2d51d300
1 // Copyright (c) 2011 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"
7 #if defined(OS_WIN)
8 #include <io.h>
9 #endif
10 #include <stdio.h>
12 #include <fstream>
14 #include "base/file_path.h"
15 #include "base/logging.h"
16 #include "base/string_piece.h"
17 #include "base/string_util.h"
18 #include "base/utf_string_conversions.h"
20 namespace {
22 const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
24 } // namespace
26 namespace file_util {
28 bool EndsWithSeparator(const FilePath& path) {
29 FilePath::StringType value = path.value();
30 if (value.empty())
31 return false;
33 return FilePath::IsSeparator(value[value.size() - 1]);
36 bool EnsureEndsWithSeparator(FilePath* path) {
37 if (!DirectoryExists(*path))
38 return false;
40 if (EndsWithSeparator(*path))
41 return true;
43 FilePath::StringType& path_str =
44 const_cast<FilePath::StringType&>(path->value());
45 path_str.append(&FilePath::kSeparators[0], 1);
47 return true;
50 void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
51 FilePath::StringType& value =
52 const_cast<FilePath::StringType&>(path->value());
54 const FilePath::StringType::size_type last_dot =
55 value.rfind(kExtensionSeparator);
56 const FilePath::StringType::size_type last_separator =
57 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
59 if (last_dot == FilePath::StringType::npos ||
60 (last_separator != std::wstring::npos && last_dot < last_separator)) {
61 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
62 // We should just append the suffix to the entire path.
63 value.append(suffix);
64 return;
67 value.insert(last_dot, suffix);
70 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
71 // We open the file in binary format even if they are text files because
72 // we are just comparing that bytes are exactly same in both files and not
73 // doing anything smart with text formatting.
74 std::ifstream file1(filename1.value().c_str(),
75 std::ios::in | std::ios::binary);
76 std::ifstream file2(filename2.value().c_str(),
77 std::ios::in | std::ios::binary);
79 // Even if both files aren't openable (and thus, in some sense, "equal"),
80 // any unusable file yields a result of "false".
81 if (!file1.is_open() || !file2.is_open())
82 return false;
84 const int BUFFER_SIZE = 2056;
85 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
86 do {
87 file1.read(buffer1, BUFFER_SIZE);
88 file2.read(buffer2, BUFFER_SIZE);
90 if ((file1.eof() != file2.eof()) ||
91 (file1.gcount() != file2.gcount()) ||
92 (memcmp(buffer1, buffer2, file1.gcount()))) {
93 file1.close();
94 file2.close();
95 return false;
97 } while (!file1.eof() || !file2.eof());
99 file1.close();
100 file2.close();
101 return true;
104 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
105 std::ifstream file1(filename1.value().c_str(), std::ios::in);
106 std::ifstream file2(filename2.value().c_str(), std::ios::in);
108 // Even if both files aren't openable (and thus, in some sense, "equal"),
109 // any unusable file yields a result of "false".
110 if (!file1.is_open() || !file2.is_open())
111 return false;
113 do {
114 std::string line1, line2;
115 getline(file1, line1);
116 getline(file2, line2);
118 // Check for mismatched EOF states, or any error state.
119 if ((file1.eof() != file2.eof()) ||
120 file1.bad() || file2.bad()) {
121 return false;
124 // Trim all '\r' and '\n' characters from the end of the line.
125 std::string::size_type end1 = line1.find_last_not_of("\r\n");
126 if (end1 == std::string::npos)
127 line1.clear();
128 else if (end1 + 1 < line1.length())
129 line1.erase(end1 + 1);
131 std::string::size_type end2 = line2.find_last_not_of("\r\n");
132 if (end2 == std::string::npos)
133 line2.clear();
134 else if (end2 + 1 < line2.length())
135 line2.erase(end2 + 1);
137 if (line1 != line2)
138 return false;
139 } while (!file1.eof() || !file2.eof());
141 return true;
144 bool ReadFileToString(const FilePath& path, std::string* contents) {
145 FILE* file = OpenFile(path, "rb");
146 if (!file) {
147 return false;
150 char buf[1 << 16];
151 size_t len;
152 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
153 if (contents)
154 contents->append(buf, len);
156 CloseFile(file);
158 return true;
161 bool IsDirectoryEmpty(const FilePath& dir_path) {
162 FileEnumerator files(dir_path, false,
163 static_cast<FileEnumerator::FileType>(
164 FileEnumerator::FILES | FileEnumerator::DIRECTORIES));
165 if (files.Next().value().empty())
166 return true;
167 return false;
170 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
171 FilePath directory;
172 if (!GetTempDir(&directory))
173 return NULL;
175 return CreateAndOpenTemporaryFileInDir(directory, path);
178 bool GetFileSize(const FilePath& file_path, int64* file_size) {
179 base::PlatformFileInfo info;
180 if (!GetFileInfo(file_path, &info))
181 return false;
182 *file_size = info.size;
183 return true;
186 bool IsDot(const FilePath& path) {
187 return FILE_PATH_LITERAL(".") == path.BaseName().value();
190 bool IsDotDot(const FilePath& path) {
191 return FILE_PATH_LITERAL("..") == path.BaseName().value();
194 bool TouchFile(const FilePath& path,
195 const base::Time& last_accessed,
196 const base::Time& last_modified) {
197 base::PlatformFile file =
198 base::CreatePlatformFile(path,
199 base::PLATFORM_FILE_OPEN |
200 base::PLATFORM_FILE_WRITE_ATTRIBUTES,
201 NULL, NULL);
202 if (file != base::kInvalidPlatformFileValue) {
203 bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
204 base::ClosePlatformFile(file);
205 return result;
208 return false;
211 bool SetLastModifiedTime(const FilePath& path,
212 const base::Time& last_modified) {
213 return TouchFile(path, last_modified, last_modified);
216 bool CloseFile(FILE* file) {
217 if (file == NULL)
218 return true;
219 return fclose(file) == 0;
222 bool TruncateFile(FILE* file) {
223 if (file == NULL)
224 return false;
225 long current_offset = ftell(file);
226 if (current_offset == -1)
227 return false;
228 #if defined(OS_WIN)
229 int fd = _fileno(file);
230 if (_chsize(fd, current_offset) != 0)
231 return false;
232 #else
233 int fd = fileno(file);
234 if (ftruncate(fd, current_offset) != 0)
235 return false;
236 #endif
237 return true;
240 bool ContainsPath(const FilePath &parent, const FilePath& child) {
241 FilePath abs_parent = FilePath(parent);
242 FilePath abs_child = FilePath(child);
244 if (!file_util::AbsolutePath(&abs_parent) ||
245 !file_util::AbsolutePath(&abs_child))
246 return false;
248 #if defined(OS_WIN)
249 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
250 // a case-insensitive compare.
251 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
252 #else
253 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
254 #endif
255 return false;
257 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
258 // to check kSeparators[0].
259 if (abs_child.value().length() <= abs_parent.value().length() ||
260 abs_child.value()[abs_parent.value().length()] !=
261 FilePath::kSeparators[0])
262 return false;
264 return true;
267 int64 ComputeDirectorySize(const FilePath& root_path) {
268 int64 running_size = 0;
269 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
270 for (FilePath current = file_iter.Next(); !current.empty();
271 current = file_iter.Next()) {
272 FileEnumerator::FindInfo info;
273 file_iter.GetFindInfo(&info);
274 #if defined(OS_WIN)
275 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
276 running_size += li.QuadPart;
277 #else
278 running_size += info.stat.st_size;
279 #endif
281 return running_size;
284 int64 ComputeFilesSize(const FilePath& directory,
285 const FilePath::StringType& pattern) {
286 int64 running_size = 0;
287 FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
288 for (FilePath current = file_iter.Next(); !current.empty();
289 current = file_iter.Next()) {
290 FileEnumerator::FindInfo info;
291 file_iter.GetFindInfo(&info);
292 #if defined(OS_WIN)
293 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
294 running_size += li.QuadPart;
295 #else
296 running_size += info.stat.st_size;
297 #endif
299 return running_size;
302 ///////////////////////////////////////////////
303 // MemoryMappedFile
305 MemoryMappedFile::~MemoryMappedFile() {
306 CloseHandles();
309 bool MemoryMappedFile::Initialize(const FilePath& file_name) {
310 if (IsValid())
311 return false;
313 if (!MapFileToMemory(file_name)) {
314 CloseHandles();
315 return false;
318 return true;
321 bool MemoryMappedFile::Initialize(base::PlatformFile file) {
322 if (IsValid())
323 return false;
325 file_ = file;
327 if (!MapFileToMemoryInternal()) {
328 CloseHandles();
329 return false;
332 return true;
335 bool MemoryMappedFile::IsValid() const {
336 return data_ != NULL;
339 bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
340 file_ = base::CreatePlatformFile(
341 file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
342 NULL, NULL);
344 if (file_ == base::kInvalidPlatformFileValue) {
345 LOG(ERROR) << "Couldn't open " << file_name.value();
346 return false;
349 return MapFileToMemoryInternal();
352 // Deprecated functions ----------------------------------------------------
354 #if defined(OS_WIN)
355 void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
356 if (!path) {
357 NOTREACHED();
358 return; // Don't crash in this function in release builds.
361 if (!EndsWithSeparator(FilePath(*path)))
362 path->push_back(FilePath::kSeparators[0]);
363 path->append(new_ending);
366 bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
367 bool recursive) {
368 return CopyDirectory(FilePath::FromWStringHack(from_path),
369 FilePath::FromWStringHack(to_path),
370 recursive);
372 bool Delete(const std::wstring& path, bool recursive) {
373 return Delete(FilePath::FromWStringHack(path), recursive);
375 std::wstring GetFileExtensionFromPath(const std::wstring& path) {
376 std::wstring file_name = FilePath(path).BaseName().value();
377 const std::wstring::size_type last_dot = file_name.rfind(kExtensionSeparator);
378 return std::wstring(last_dot == std::wstring::npos ? L""
379 : file_name, last_dot + 1);
381 FILE* OpenFile(const std::wstring& filename, const char* mode) {
382 return OpenFile(FilePath::FromWStringHack(filename), mode);
384 int ReadFile(const std::wstring& filename, char* data, int size) {
385 return ReadFile(FilePath::FromWStringHack(filename), data, size);
387 int WriteFile(const std::wstring& filename, const char* data, int size) {
388 return WriteFile(FilePath::FromWStringHack(filename), data, size);
390 #endif // OS_WIN
392 ///////////////////////////////////////////////
393 // FileEnumerator
395 // Note: the main logic is in file_util_<platform>.cc
397 bool FileEnumerator::ShouldSkip(const FilePath& path) {
398 FilePath::StringType basename = path.BaseName().value();
399 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
402 } // namespace