[go: nahoru, domu]

blob: 8ecb1cc1d5640c727b57a09219cc5c2b46eff0d8 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commitd7cae122008-07-26 21:49:384
5#include "base/file_util.h"
6
jeremy@chromium.orgc2c998c2009-01-27 19:08:397#if defined(OS_WIN)
8#include <io.h>
9#endif
mark@chromium.org836f1342008-10-01 17:40:1310#include <stdio.h>
11
initial.commitd7cae122008-07-26 21:49:3812#include <fstream>
initial.commitd7cae122008-07-26 21:49:3813
evanm@google.com640517f2008-10-30 23:54:0414#include "base/file_path.h"
initial.commitd7cae122008-07-26 21:49:3815#include "base/logging.h"
initial.commitd7cae122008-07-26 21:49:3816#include "base/string_util.h"
initial.commitd7cae122008-07-26 21:49:3817#include "unicode/uniset.h"
18
estade@chromium.orgb9e04f02008-11-27 04:03:5719#include "base/string_piece.h"
20#include "base/sys_string_conversions.h"
21
estade@chromium.orgceeb87e2008-12-04 20:46:0622namespace {
23
24const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
25
26}
27
initial.commitd7cae122008-07-26 21:49:3828namespace file_util {
29
estade@chromium.orgb9e04f02008-11-27 04:03:5730void PathComponents(const FilePath& path,
31 std::vector<FilePath::StringType>* components) {
32 DCHECK(components);
33 if (!components)
erikkay@google.com37088fef2008-08-15 17:32:1034 return;
erikkay@google.com37088fef2008-08-15 17:32:1035
estade@chromium.orgb9e04f02008-11-27 04:03:5736 FilePath::StringType path_str = path.value();
37 FilePath::StringType::size_type start = 0;
38 FilePath::StringType::size_type end =
39 path_str.find_first_of(FilePath::kSeparators);
40
41 // If the path starts with a separator, add it to components.
erikkay@google.com37088fef2008-08-15 17:32:1042 if (end == start) {
estade@chromium.orgb9e04f02008-11-27 04:03:5743 components->push_back(FilePath::StringType(path_str, 0, 1));
erikkay@google.com37088fef2008-08-15 17:32:1044 start = end + 1;
estade@chromium.orgb9e04f02008-11-27 04:03:5745 end = path_str.find_first_of(FilePath::kSeparators, start);
erikkay@google.com37088fef2008-08-15 17:32:1046 }
estade@chromium.orgb9e04f02008-11-27 04:03:5747 while (end != FilePath::StringType::npos) {
48 FilePath::StringType component =
49 FilePath::StringType(path_str, start, end - start);
erikkay@google.com37088fef2008-08-15 17:32:1050 components->push_back(component);
51 start = end + 1;
estade@chromium.orgb9e04f02008-11-27 04:03:5752 end = path_str.find_first_of(FilePath::kSeparators, start);
erikkay@google.com37088fef2008-08-15 17:32:1053 }
estade@chromium.orgb9e04f02008-11-27 04:03:5754
55 components->push_back(FilePath::StringType(path_str, start));
erikkay@google.com37088fef2008-08-15 17:32:1056}
estade@chromium.org7e263942008-11-25 22:04:3757
estade@chromium.orgb9e04f02008-11-27 04:03:5758bool EndsWithSeparator(const FilePath& path) {
59 FilePath::StringType value = path.value();
60 if (value.empty())
61 return false;
62
63 return FilePath::IsSeparator(value[value.size() - 1]);
estade@chromium.org7e263942008-11-25 22:04:3764}
65
66bool EnsureEndsWithSeparator(FilePath* path) {
67 if (!DirectoryExists(*path))
68 return false;
69
70 if (EndsWithSeparator(*path))
71 return true;
72
73 FilePath::StringType& path_str =
74 const_cast<FilePath::StringType&>(path->value());
75 path_str.append(&FilePath::kSeparators[0], 1);
76
77 return true;
initial.commitd7cae122008-07-26 21:49:3878}
79
80void TrimTrailingSeparator(std::wstring* dir) {
erikkay@google.com37088fef2008-08-15 17:32:1081 while (dir->length() > 1 && EndsWithSeparator(dir))
initial.commitd7cae122008-07-26 21:49:3882 dir->resize(dir->length() - 1);
83}
84
estade@chromium.org7ae7c2cb2009-01-06 23:31:4185FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
86 FilePath::StringType file_name = path.BaseName().value();
87 const FilePath::StringType::size_type last_dot =
88 file_name.rfind(kExtensionSeparator);
89 return FilePath::StringType(last_dot == FilePath::StringType::npos ?
90 FILE_PATH_LITERAL("") :
91 file_name, last_dot+1);
glen@chromium.org52ab8f902008-11-03 16:14:4692}
93
94std::wstring GetFilenameWithoutExtensionFromPath(const std::wstring& path) {
95 std::wstring file_name = GetFilenameFromPath(path);
96 std::wstring::size_type last_dot = file_name.rfind(L'.');
97 return file_name.substr(0, last_dot);
estade@chromium.orgceeb87e2008-12-04 20:46:0698}
99
100void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
101 FilePath::StringType& value =
102 const_cast<FilePath::StringType&>(path->value());
103
104 const FilePath::StringType::size_type last_dot =
105 value.rfind(kExtensionSeparator);
106 const FilePath::StringType::size_type last_separator =
107 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
108
109 if (last_dot == FilePath::StringType::npos ||
110 (last_separator != std::wstring::npos && last_dot < last_separator)) {
111 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
112 // We should just append the suffix to the entire path.
113 value.append(suffix);
114 return;
115 }
116
117 value.insert(last_dot, suffix);
118}
119
120void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
121 FilePath::StringType clean_extension;
122 // If the new extension is "" or ".", then we will just remove the current
123 // extension.
124 if (!extension.empty() &&
125 extension != FilePath::StringType(&kExtensionSeparator, 1)) {
126 if (extension[0] != kExtensionSeparator)
127 clean_extension.append(&kExtensionSeparator, 1);
128 clean_extension.append(extension);
129 }
130
131 FilePath::StringType& value =
132 const_cast<FilePath::StringType&>(path->value());
133 const FilePath::StringType::size_type last_dot =
134 value.rfind(kExtensionSeparator);
135 const FilePath::StringType::size_type last_separator =
136 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
137
138 // Erase the current extension, if any.
139 if ((last_dot > last_separator ||
140 last_separator == FilePath::StringType::npos) &&
141 last_dot != FilePath::StringType::npos)
142 value.erase(last_dot);
143
144 value.append(clean_extension);
initial.commitd7cae122008-07-26 21:49:38145}
146
147void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) {
148 DCHECK(file_name);
149
ericroman@google.comd324ab332008-08-18 16:00:38150 // Control characters, formatting characters, non-characters, and
initial.commitd7cae122008-07-26 21:49:38151 // some printable ASCII characters regarded as dangerous ('"*/:<>?\\').
152 // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx
153 // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx
154 // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they
155 // are legitimate in Arabic and some S/SE Asian scripts. However, when used
156 // elsewhere, they can be confusing/problematic.
157 // Also, consider wrapping the set with our Singleton class to create and
158 // freeze it only once. Note that there's a trade-off between memory and
159 // speed.
160
161 UErrorCode status = U_ZERO_ERROR;
erikkay@google.com5af2edb92008-08-08 20:16:08162#if defined(WCHAR_T_IS_UTF16)
initial.commitd7cae122008-07-26 21:49:38163 UnicodeSet illegal_characters(UnicodeString(
164 L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status);
165#else
166 UnicodeSet illegal_characters(UNICODE_STRING_SIMPLE(
167 "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), status);
168#endif
169 DCHECK(U_SUCCESS(status));
170 // Add non-characters. If this becomes a performance bottleneck by
171 // any chance, check |ucs4 & 0xFFFEu == 0xFFFEu|, instead.
172 illegal_characters.add(0xFDD0, 0xFDEF);
173 for (int i = 0; i <= 0x10; ++i) {
174 int plane_base = 0x10000 * i;
175 illegal_characters.add(plane_base + 0xFFFE, plane_base + 0xFFFF);
176 }
177 illegal_characters.freeze();
178 DCHECK(!illegal_characters.contains(replace_char) && replace_char < 0x10000);
179
180 // Remove leading and trailing whitespace.
181 TrimWhitespace(*file_name, TRIM_ALL, file_name);
182
183 std::wstring::size_type i = 0;
184 std::wstring::size_type length = file_name->size();
erikkay@google.com5af2edb92008-08-08 20:16:08185 const wchar_t* wstr = file_name->data();
186#if defined(WCHAR_T_IS_UTF16)
initial.commitd7cae122008-07-26 21:49:38187 // Using |span| method of UnicodeSet might speed things up a bit, but
188 // it's not likely to matter here.
initial.commitd7cae122008-07-26 21:49:38189 std::wstring temp;
190 temp.reserve(length);
191 while (i < length) {
192 UChar32 ucs4;
193 std::wstring::size_type prev = i;
194 U16_NEXT(wstr, i, length, ucs4);
195 if (illegal_characters.contains(ucs4)) {
196 temp.push_back(replace_char);
197 } else if (ucs4 < 0x10000) {
198 temp.push_back(ucs4);
199 } else {
200 temp.push_back(wstr[prev]);
201 temp.push_back(wstr[prev + 1]);
202 }
203 }
204 file_name->swap(temp);
erikkay@google.com5af2edb92008-08-08 20:16:08205#elif defined(WCHAR_T_IS_UTF32)
initial.commitd7cae122008-07-26 21:49:38206 while (i < length) {
207 if (illegal_characters.contains(wstr[i])) {
erikkay@google.com5af2edb92008-08-08 20:16:08208 (*file_name)[i] = replace_char;
initial.commitd7cae122008-07-26 21:49:38209 }
erikkay@google.com37088fef2008-08-15 17:32:10210 ++i;
initial.commitd7cae122008-07-26 21:49:38211 }
212#else
213#error wchar_t* should be either UTF-16 or UTF-32
214#endif
215}
216
evanm@google.com640517f2008-10-30 23:54:04217bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commitd7cae122008-07-26 21:49:38218 // We open the file in binary format even if they are text files because
219 // we are just comparing that bytes are exactly same in both files and not
220 // doing anything smart with text formatting.
evanm@google.com640517f2008-10-30 23:54:04221 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08222 std::ios::in | std::ios::binary);
evanm@google.com640517f2008-10-30 23:54:04223 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08224 std::ios::in | std::ios::binary);
estade@chromium.orgb9e04f02008-11-27 04:03:57225
initial.commitd7cae122008-07-26 21:49:38226 // Even if both files aren't openable (and thus, in some sense, "equal"),
227 // any unusable file yields a result of "false".
228 if (!file1.is_open() || !file2.is_open())
229 return false;
230
231 const int BUFFER_SIZE = 2056;
232 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
233 do {
234 file1.read(buffer1, BUFFER_SIZE);
235 file2.read(buffer2, BUFFER_SIZE);
236
237 if ((file1.eof() && !file2.eof()) ||
238 (!file1.eof() && file2.eof()) ||
239 (file1.gcount() != file2.gcount()) ||
240 (memcmp(buffer1, buffer2, file1.gcount()))) {
241 file1.close();
242 file2.close();
243 return false;
244 }
245 } while (!file1.eof() && !file2.eof());
246
247 file1.close();
248 file2.close();
249 return true;
250}
251
252bool ReadFileToString(const std::wstring& path, std::string* contents) {
mark@chromium.org836f1342008-10-01 17:40:13253 FILE* file = OpenFile(path, "rb");
254 if (!file) {
initial.commitd7cae122008-07-26 21:49:38255 return false;
mark@chromium.org836f1342008-10-01 17:40:13256 }
initial.commitd7cae122008-07-26 21:49:38257
258 char buf[1 << 16];
259 size_t len;
260 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
261 contents->append(buf, len);
262 }
mark@chromium.org836f1342008-10-01 17:40:13263 CloseFile(file);
initial.commitd7cae122008-07-26 21:49:38264
265 return true;
initial.commitd7cae122008-07-26 21:49:38266}
267
dkegel@google.comeac0709a2008-11-04 21:00:46268bool GetFileSize(const FilePath& file_path, int64* file_size) {
darin@google.comf5e3da4d2008-09-26 01:04:08269 FileInfo info;
270 if (!GetFileInfo(file_path, &info))
271 return false;
272 *file_size = info.size;
273 return true;
274}
275
mark@chromium.org836f1342008-10-01 17:40:13276bool CloseFile(FILE* file) {
sidchat@google.coma1a19502008-10-21 17:14:45277 if (file == NULL)
278 return true;
mark@chromium.org836f1342008-10-01 17:40:13279 return fclose(file) == 0;
jeremy@chromium.org6e01dae2009-01-27 17:13:02280}
281
jeremy@chromium.orgc2c998c2009-01-27 19:08:39282bool TruncateFile(FILE* file) {
283 if (file == NULL)
284 return false;
285 long current_offset = ftell(file);
286 if (current_offset == -1)
287 return false;
288#if defined(OS_WIN)
289 int fd = _fileno(file);
290 if (_chsize(fd, current_offset) != 0)
291 return false;
292#else
293 int fd = fileno(file);
294 if (ftruncate(fd, current_offset) != 0)
295 return false;
296#endif
297 return true;
298}
299
aa@chromium.orgee5c29da2009-01-09 22:14:27300bool ContainsPath(const FilePath &parent, const FilePath& child) {
301 FilePath abs_parent = FilePath(parent);
302 FilePath abs_child = FilePath(child);
303
304 if (!file_util::AbsolutePath(&abs_parent) ||
305 !file_util::AbsolutePath(&abs_child))
306 return false;
307
308#if defined(OS_WIN)
309 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
310 // a case-insensitive compare.
311 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
312#else
313 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
314#endif
315 return false;
316
317 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
318 // to check kSeparators[0].
319 if (abs_child.value().length() <= abs_parent.value().length() ||
320 abs_child.value()[abs_parent.value().length()] !=
321 FilePath::kSeparators[0])
322 return false;
323
324 return true;
325}
326
estade@chromium.org7856bb82008-12-12 23:43:03327///////////////////////////////////////////////
328// MemoryMappedFile
329
330MemoryMappedFile::~MemoryMappedFile() {
331 CloseHandles();
332}
333
334bool MemoryMappedFile::Initialize(const FilePath& file_name) {
335 if (IsValid())
336 return false;
337
338 if (!MapFileToMemory(file_name)) {
339 CloseHandles();
340 return false;
341 }
342
343 return true;
344}
345
346bool MemoryMappedFile::IsValid() {
347 return data_ != NULL;
348}
349
evanm@google.com640517f2008-10-30 23:54:04350// Deprecated functions ----------------------------------------------------
351
352bool AbsolutePath(std::wstring* path_str) {
evanm@google.com4a2952f2008-10-31 02:03:07353 FilePath path(FilePath::FromWStringHack(*path_str));
evanm@google.com640517f2008-10-30 23:54:04354 if (!AbsolutePath(&path))
355 return false;
356 *path_str = path.ToWStringHack();
357 return true;
358}
estade@chromium.orgb9e04f02008-11-27 04:03:57359void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
360 if (!path) {
361 NOTREACHED();
362 return; // Don't crash in this function in release builds.
363 }
364
365 if (!EndsWithSeparator(path))
366 path->push_back(FilePath::kSeparators[0]);
367 path->append(new_ending);
evanm@google.com640517f2008-10-30 23:54:04368}
369bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
370 bool recursive) {
371 return CopyDirectory(FilePath::FromWStringHack(from_path),
372 FilePath::FromWStringHack(to_path),
373 recursive);
374}
evanm@google.com640517f2008-10-30 23:54:04375bool ContentsEqual(const std::wstring& filename1,
376 const std::wstring& filename2) {
377 return ContentsEqual(FilePath::FromWStringHack(filename1),
378 FilePath::FromWStringHack(filename2));
estade@chromium.orgb9e04f02008-11-27 04:03:57379}
380bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
381 return CopyFile(FilePath::FromWStringHack(from_path),
382 FilePath::FromWStringHack(to_path));
evanm@google.com640517f2008-10-30 23:54:04383}
384bool CreateDirectory(const std::wstring& full_path) {
385 return CreateDirectory(FilePath::FromWStringHack(full_path));
386}
erikkay@google.com7e1fde6a2008-12-23 20:20:10387bool CreateNewTempDirectory(const std::wstring& prefix,
388 std::wstring* new_temp_path) {
389#if defined(OS_WIN)
390 FilePath::StringType dir_prefix(prefix);
391#elif defined(OS_POSIX)
392 FilePath::StringType dir_prefix = WideToUTF8(prefix);
393#endif
394 FilePath temp_path;
395 if (!CreateNewTempDirectory(dir_prefix, &temp_path))
396 return false;
397 *new_temp_path = temp_path.ToWStringHack();
398 return true;
399}
estade@chromium.org392264c2008-11-11 00:01:38400bool CreateTemporaryFileName(std::wstring* temp_file) {
401 FilePath temp_file_path;
402 if (!CreateTemporaryFileName(&temp_file_path))
403 return false;
404 *temp_file = temp_file_path.ToWStringHack();
405 return true;
estade@chromium.orgb9e04f02008-11-27 04:03:57406}
407bool Delete(const std::wstring& path, bool recursive) {
408 return Delete(FilePath::FromWStringHack(path), recursive);
409}
410bool DirectoryExists(const std::wstring& path) {
411 return DirectoryExists(FilePath::FromWStringHack(path));
412}
413bool EndsWithSeparator(std::wstring* path) {
414 return EndsWithSeparator(FilePath::FromWStringHack(*path));
415}
416bool EndsWithSeparator(const std::wstring& path) {
417 return EndsWithSeparator(FilePath::FromWStringHack(path));
estade@chromium.org392264c2008-11-11 00:01:38418}
evanm@google.com640517f2008-10-30 23:54:04419bool GetCurrentDirectory(std::wstring* path_str) {
420 FilePath path;
421 if (!GetCurrentDirectory(&path))
422 return false;
423 *path_str = path.ToWStringHack();
424 return true;
425}
estade@chromium.org7ae7c2cb2009-01-06 23:31:41426std::wstring GetFileExtensionFromPath(const std::wstring& path) {
427 FilePath::StringType extension =
428 GetFileExtensionFromPath(FilePath::FromWStringHack(path));
429#if defined(OS_WIN)
430 return extension;
431#elif defined(OS_POSIX)
432 return UTF8ToWide(extension);
433#endif
434}
dkegel@google.comeac0709a2008-11-04 21:00:46435bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
436 return GetFileInfo(FilePath::FromWStringHack(file_path), results);
437}
estade@chromium.orgceeb87e2008-12-04 20:46:06438std::wstring GetFilenameFromPath(const std::wstring& path) {
439 if (path.empty() || EndsWithSeparator(path))
440 return std::wstring();
441
estade@chromium.org5e207882009-01-05 23:59:36442 return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
estade@chromium.orgceeb87e2008-12-04 20:46:06443}
dkegel@google.comeac0709a2008-11-04 21:00:46444bool GetFileSize(const std::wstring& file_path, int64* file_size) {
445 return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
446}
evanm@google.com640517f2008-10-30 23:54:04447bool GetTempDir(std::wstring* path_str) {
448 FilePath path;
449 if (!GetTempDir(&path))
450 return false;
451 *path_str = path.ToWStringHack();
452 return true;
453}
estade@chromium.orgb9e04f02008-11-27 04:03:57454bool Move(const std::wstring& from_path, const std::wstring& to_path) {
455 return Move(FilePath::FromWStringHack(from_path),
456 FilePath::FromWStringHack(to_path));
457}
estade@chromium.orga9cd2a652008-11-17 21:01:19458FILE* OpenFile(const std::wstring& filename, const char* mode) {
459 return OpenFile(FilePath::FromWStringHack(filename), mode);
estade@chromium.orgb9e04f02008-11-27 04:03:57460}
461bool PathExists(const std::wstring& path) {
462 return PathExists(FilePath::FromWStringHack(path));
estade@chromium.orga9cd2a652008-11-17 21:01:19463}
erikkay@google.com7e1fde6a2008-12-23 20:20:10464bool PathIsWritable(const std::wstring& path) {
465 return PathIsWritable(FilePath::FromWStringHack(path));
466}
estade@chromium.orga9cd2a652008-11-17 21:01:19467bool SetCurrentDirectory(const std::wstring& directory) {
468 return SetCurrentDirectory(FilePath::FromWStringHack(directory));
469}
estade@chromium.orgb9e04f02008-11-27 04:03:57470void TrimFilename(std::wstring* path) {
471 if (EndsWithSeparator(path)) {
472 TrimTrailingSeparator(path);
473 } else {
474 *path = FilePath::FromWStringHack(*path).DirName().ToWStringHack();
475 }
476}
477void UpOneDirectory(std::wstring* dir) {
478 FilePath path = FilePath::FromWStringHack(*dir);
479 FilePath directory = path.DirName();
480 // If there is no separator, we will get back kCurrentDirectory.
481 // In this case don't change |dir|.
482 if (directory.value() != FilePath::kCurrentDirectory)
483 *dir = directory.ToWStringHack();
484}
485void UpOneDirectoryOrEmpty(std::wstring* dir) {
486 FilePath path = FilePath::FromWStringHack(*dir);
487 FilePath directory = path.DirName();
488 // If there is no separator, we will get back kCurrentDirectory.
489 // In this case, clear dir.
490 if (directory == path || directory.value() == FilePath::kCurrentDirectory)
491 dir->clear();
492 else
493 *dir = directory.ToWStringHack();
494}
initial.commitd7cae122008-07-26 21:49:38495} // namespace
license.botbf09a502008-08-24 00:55:55496