blob: fe33c32faa44b896908e6985146451cb3f80c978 [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
[email protected]836f1342008-10-01 17:40:137#include <stdio.h>
8
initial.commitd7cae122008-07-26 21:49:389#include <fstream>
initial.commitd7cae122008-07-26 21:49:3810
[email protected]640517f2008-10-30 23:54:0411#include "base/file_path.h"
initial.commitd7cae122008-07-26 21:49:3812#include "base/logging.h"
initial.commitd7cae122008-07-26 21:49:3813#include "base/string_util.h"
initial.commitd7cae122008-07-26 21:49:3814#include "unicode/uniset.h"
15
[email protected]b9e04f02008-11-27 04:03:5716#include "base/string_piece.h"
17#include "base/sys_string_conversions.h"
18
[email protected]ceeb87e2008-12-04 20:46:0619namespace {
20
21const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
22
23}
24
initial.commitd7cae122008-07-26 21:49:3825namespace file_util {
26
[email protected]b9e04f02008-11-27 04:03:5727void PathComponents(const FilePath& path,
28 std::vector<FilePath::StringType>* components) {
29 DCHECK(components);
30 if (!components)
[email protected]37088fef2008-08-15 17:32:1031 return;
[email protected]37088fef2008-08-15 17:32:1032
[email protected]b9e04f02008-11-27 04:03:5733 FilePath::StringType path_str = path.value();
34 FilePath::StringType::size_type start = 0;
35 FilePath::StringType::size_type end =
36 path_str.find_first_of(FilePath::kSeparators);
37
38 // If the path starts with a separator, add it to components.
[email protected]37088fef2008-08-15 17:32:1039 if (end == start) {
[email protected]b9e04f02008-11-27 04:03:5740 components->push_back(FilePath::StringType(path_str, 0, 1));
[email protected]37088fef2008-08-15 17:32:1041 start = end + 1;
[email protected]b9e04f02008-11-27 04:03:5742 end = path_str.find_first_of(FilePath::kSeparators, start);
[email protected]37088fef2008-08-15 17:32:1043 }
[email protected]b9e04f02008-11-27 04:03:5744 while (end != FilePath::StringType::npos) {
45 FilePath::StringType component =
46 FilePath::StringType(path_str, start, end - start);
[email protected]37088fef2008-08-15 17:32:1047 components->push_back(component);
48 start = end + 1;
[email protected]b9e04f02008-11-27 04:03:5749 end = path_str.find_first_of(FilePath::kSeparators, start);
[email protected]37088fef2008-08-15 17:32:1050 }
[email protected]b9e04f02008-11-27 04:03:5751
52 components->push_back(FilePath::StringType(path_str, start));
[email protected]37088fef2008-08-15 17:32:1053}
[email protected]7e263942008-11-25 22:04:3754
[email protected]b9e04f02008-11-27 04:03:5755bool EndsWithSeparator(const FilePath& path) {
56 FilePath::StringType value = path.value();
57 if (value.empty())
58 return false;
59
60 return FilePath::IsSeparator(value[value.size() - 1]);
initial.commitd7cae122008-07-26 21:49:3861}
62
[email protected]7e263942008-11-25 22:04:3763bool EnsureEndsWithSeparator(FilePath* path) {
64 if (!DirectoryExists(*path))
65 return false;
66
67 if (EndsWithSeparator(*path))
68 return true;
69
70 FilePath::StringType& path_str =
71 const_cast<FilePath::StringType&>(path->value());
72 path_str.append(&FilePath::kSeparators[0], 1);
73
74 return true;
75}
76
initial.commitd7cae122008-07-26 21:49:3877void TrimTrailingSeparator(std::wstring* dir) {
[email protected]37088fef2008-08-15 17:32:1078 while (dir->length() > 1 && EndsWithSeparator(dir))
initial.commitd7cae122008-07-26 21:49:3879 dir->resize(dir->length() - 1);
80}
81
[email protected]5af2edb92008-08-08 20:16:0882std::wstring GetFileExtensionFromPath(const std::wstring& path) {
83 std::wstring file_name = GetFilenameFromPath(path);
84 std::wstring::size_type last_dot = file_name.rfind(L'.');
[email protected]52ab8f902008-11-03 16:14:4685 return std::wstring(last_dot == std::wstring::npos ?
86 L"" :
87 file_name, last_dot+1);
88}
89
90std::wstring GetFilenameWithoutExtensionFromPath(const std::wstring& path) {
91 std::wstring file_name = GetFilenameFromPath(path);
92 std::wstring::size_type last_dot = file_name.rfind(L'.');
93 return file_name.substr(0, last_dot);
initial.commitd7cae122008-07-26 21:49:3894}
95
[email protected]ceeb87e2008-12-04 20:46:0696void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
97 FilePath::StringType& value =
98 const_cast<FilePath::StringType&>(path->value());
99
100 const FilePath::StringType::size_type last_dot =
101 value.rfind(kExtensionSeparator);
102 const FilePath::StringType::size_type last_separator =
103 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
104
105 if (last_dot == FilePath::StringType::npos ||
106 (last_separator != std::wstring::npos && last_dot < last_separator)) {
107 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
108 // We should just append the suffix to the entire path.
109 value.append(suffix);
110 return;
111 }
112
113 value.insert(last_dot, suffix);
114}
115
116void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
117 FilePath::StringType clean_extension;
118 // If the new extension is "" or ".", then we will just remove the current
119 // extension.
120 if (!extension.empty() &&
121 extension != FilePath::StringType(&kExtensionSeparator, 1)) {
122 if (extension[0] != kExtensionSeparator)
123 clean_extension.append(&kExtensionSeparator, 1);
124 clean_extension.append(extension);
125 }
126
127 FilePath::StringType& value =
128 const_cast<FilePath::StringType&>(path->value());
129 const FilePath::StringType::size_type last_dot =
130 value.rfind(kExtensionSeparator);
131 const FilePath::StringType::size_type last_separator =
132 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
133
134 // Erase the current extension, if any.
135 if ((last_dot > last_separator ||
136 last_separator == FilePath::StringType::npos) &&
137 last_dot != FilePath::StringType::npos)
138 value.erase(last_dot);
139
140 value.append(clean_extension);
141}
142
initial.commitd7cae122008-07-26 21:49:38143void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) {
144 DCHECK(file_name);
145
[email protected]d324ab332008-08-18 16:00:38146 // Control characters, formatting characters, non-characters, and
initial.commitd7cae122008-07-26 21:49:38147 // some printable ASCII characters regarded as dangerous ('"*/:<>?\\').
148 // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx
149 // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx
150 // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they
151 // are legitimate in Arabic and some S/SE Asian scripts. However, when used
152 // elsewhere, they can be confusing/problematic.
153 // Also, consider wrapping the set with our Singleton class to create and
154 // freeze it only once. Note that there's a trade-off between memory and
155 // speed.
156
157 UErrorCode status = U_ZERO_ERROR;
[email protected]5af2edb92008-08-08 20:16:08158#if defined(WCHAR_T_IS_UTF16)
initial.commitd7cae122008-07-26 21:49:38159 UnicodeSet illegal_characters(UnicodeString(
160 L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status);
161#else
162 UnicodeSet illegal_characters(UNICODE_STRING_SIMPLE(
163 "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), status);
164#endif
165 DCHECK(U_SUCCESS(status));
166 // Add non-characters. If this becomes a performance bottleneck by
167 // any chance, check |ucs4 & 0xFFFEu == 0xFFFEu|, instead.
168 illegal_characters.add(0xFDD0, 0xFDEF);
169 for (int i = 0; i <= 0x10; ++i) {
170 int plane_base = 0x10000 * i;
171 illegal_characters.add(plane_base + 0xFFFE, plane_base + 0xFFFF);
172 }
173 illegal_characters.freeze();
174 DCHECK(!illegal_characters.contains(replace_char) && replace_char < 0x10000);
175
176 // Remove leading and trailing whitespace.
177 TrimWhitespace(*file_name, TRIM_ALL, file_name);
178
179 std::wstring::size_type i = 0;
180 std::wstring::size_type length = file_name->size();
[email protected]5af2edb92008-08-08 20:16:08181 const wchar_t* wstr = file_name->data();
182#if defined(WCHAR_T_IS_UTF16)
initial.commitd7cae122008-07-26 21:49:38183 // Using |span| method of UnicodeSet might speed things up a bit, but
184 // it's not likely to matter here.
initial.commitd7cae122008-07-26 21:49:38185 std::wstring temp;
186 temp.reserve(length);
187 while (i < length) {
188 UChar32 ucs4;
189 std::wstring::size_type prev = i;
190 U16_NEXT(wstr, i, length, ucs4);
191 if (illegal_characters.contains(ucs4)) {
192 temp.push_back(replace_char);
193 } else if (ucs4 < 0x10000) {
194 temp.push_back(ucs4);
195 } else {
196 temp.push_back(wstr[prev]);
197 temp.push_back(wstr[prev + 1]);
198 }
199 }
200 file_name->swap(temp);
[email protected]5af2edb92008-08-08 20:16:08201#elif defined(WCHAR_T_IS_UTF32)
initial.commitd7cae122008-07-26 21:49:38202 while (i < length) {
203 if (illegal_characters.contains(wstr[i])) {
[email protected]5af2edb92008-08-08 20:16:08204 (*file_name)[i] = replace_char;
initial.commitd7cae122008-07-26 21:49:38205 }
[email protected]37088fef2008-08-15 17:32:10206 ++i;
initial.commitd7cae122008-07-26 21:49:38207 }
208#else
209#error wchar_t* should be either UTF-16 or UTF-32
210#endif
211}
212
[email protected]640517f2008-10-30 23:54:04213bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commitd7cae122008-07-26 21:49:38214 // We open the file in binary format even if they are text files because
215 // we are just comparing that bytes are exactly same in both files and not
216 // doing anything smart with text formatting.
[email protected]640517f2008-10-30 23:54:04217 std::ifstream file1(filename1.value().c_str(),
[email protected]5af2edb92008-08-08 20:16:08218 std::ios::in | std::ios::binary);
[email protected]640517f2008-10-30 23:54:04219 std::ifstream file2(filename2.value().c_str(),
[email protected]5af2edb92008-08-08 20:16:08220 std::ios::in | std::ios::binary);
[email protected]b9e04f02008-11-27 04:03:57221
initial.commitd7cae122008-07-26 21:49:38222 // Even if both files aren't openable (and thus, in some sense, "equal"),
223 // any unusable file yields a result of "false".
224 if (!file1.is_open() || !file2.is_open())
225 return false;
226
227 const int BUFFER_SIZE = 2056;
228 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
229 do {
230 file1.read(buffer1, BUFFER_SIZE);
231 file2.read(buffer2, BUFFER_SIZE);
232
233 if ((file1.eof() && !file2.eof()) ||
234 (!file1.eof() && file2.eof()) ||
235 (file1.gcount() != file2.gcount()) ||
236 (memcmp(buffer1, buffer2, file1.gcount()))) {
237 file1.close();
238 file2.close();
239 return false;
240 }
241 } while (!file1.eof() && !file2.eof());
242
243 file1.close();
244 file2.close();
245 return true;
246}
247
248bool ReadFileToString(const std::wstring& path, std::string* contents) {
[email protected]836f1342008-10-01 17:40:13249 FILE* file = OpenFile(path, "rb");
250 if (!file) {
initial.commitd7cae122008-07-26 21:49:38251 return false;
[email protected]836f1342008-10-01 17:40:13252 }
initial.commitd7cae122008-07-26 21:49:38253
254 char buf[1 << 16];
255 size_t len;
256 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
257 contents->append(buf, len);
258 }
[email protected]836f1342008-10-01 17:40:13259 CloseFile(file);
initial.commitd7cae122008-07-26 21:49:38260
261 return true;
262}
263
[email protected]eac0709a2008-11-04 21:00:46264bool GetFileSize(const FilePath& file_path, int64* file_size) {
[email protected]f5e3da4d2008-09-26 01:04:08265 FileInfo info;
266 if (!GetFileInfo(file_path, &info))
267 return false;
268 *file_size = info.size;
269 return true;
270}
271
[email protected]836f1342008-10-01 17:40:13272bool CloseFile(FILE* file) {
[email protected]a1a19502008-10-21 17:14:45273 if (file == NULL)
274 return true;
[email protected]836f1342008-10-01 17:40:13275 return fclose(file) == 0;
276}
277
[email protected]7856bb82008-12-12 23:43:03278///////////////////////////////////////////////
279// MemoryMappedFile
280
281MemoryMappedFile::~MemoryMappedFile() {
282 CloseHandles();
283}
284
285bool MemoryMappedFile::Initialize(const FilePath& file_name) {
286 if (IsValid())
287 return false;
288
289 if (!MapFileToMemory(file_name)) {
290 CloseHandles();
291 return false;
292 }
293
294 return true;
295}
296
297bool MemoryMappedFile::IsValid() {
298 return data_ != NULL;
299}
300
[email protected]640517f2008-10-30 23:54:04301// Deprecated functions ----------------------------------------------------
302
303bool AbsolutePath(std::wstring* path_str) {
[email protected]4a2952f2008-10-31 02:03:07304 FilePath path(FilePath::FromWStringHack(*path_str));
[email protected]640517f2008-10-30 23:54:04305 if (!AbsolutePath(&path))
306 return false;
307 *path_str = path.ToWStringHack();
308 return true;
309}
[email protected]b9e04f02008-11-27 04:03:57310void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
311 if (!path) {
312 NOTREACHED();
313 return; // Don't crash in this function in release builds.
314 }
315
316 if (!EndsWithSeparator(path))
317 path->push_back(FilePath::kSeparators[0]);
318 path->append(new_ending);
[email protected]640517f2008-10-30 23:54:04319}
320bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
321 bool recursive) {
322 return CopyDirectory(FilePath::FromWStringHack(from_path),
323 FilePath::FromWStringHack(to_path),
324 recursive);
325}
[email protected]640517f2008-10-30 23:54:04326bool ContentsEqual(const std::wstring& filename1,
327 const std::wstring& filename2) {
328 return ContentsEqual(FilePath::FromWStringHack(filename1),
329 FilePath::FromWStringHack(filename2));
330}
[email protected]b9e04f02008-11-27 04:03:57331bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
332 return CopyFile(FilePath::FromWStringHack(from_path),
333 FilePath::FromWStringHack(to_path));
334}
[email protected]640517f2008-10-30 23:54:04335bool CreateDirectory(const std::wstring& full_path) {
336 return CreateDirectory(FilePath::FromWStringHack(full_path));
337}
[email protected]7e1fde6a2008-12-23 20:20:10338bool CreateNewTempDirectory(const std::wstring& prefix,
339 std::wstring* new_temp_path) {
340#if defined(OS_WIN)
341 FilePath::StringType dir_prefix(prefix);
342#elif defined(OS_POSIX)
343 FilePath::StringType dir_prefix = WideToUTF8(prefix);
344#endif
345 FilePath temp_path;
346 if (!CreateNewTempDirectory(dir_prefix, &temp_path))
347 return false;
348 *new_temp_path = temp_path.ToWStringHack();
349 return true;
350}
[email protected]392264c2008-11-11 00:01:38351bool CreateTemporaryFileName(std::wstring* temp_file) {
352 FilePath temp_file_path;
353 if (!CreateTemporaryFileName(&temp_file_path))
354 return false;
355 *temp_file = temp_file_path.ToWStringHack();
356 return true;
357}
[email protected]b9e04f02008-11-27 04:03:57358bool Delete(const std::wstring& path, bool recursive) {
359 return Delete(FilePath::FromWStringHack(path), recursive);
360}
361bool DirectoryExists(const std::wstring& path) {
362 return DirectoryExists(FilePath::FromWStringHack(path));
363}
364bool EndsWithSeparator(std::wstring* path) {
365 return EndsWithSeparator(FilePath::FromWStringHack(*path));
366}
367bool EndsWithSeparator(const std::wstring& path) {
368 return EndsWithSeparator(FilePath::FromWStringHack(path));
369}
[email protected]640517f2008-10-30 23:54:04370bool GetCurrentDirectory(std::wstring* path_str) {
371 FilePath path;
372 if (!GetCurrentDirectory(&path))
373 return false;
374 *path_str = path.ToWStringHack();
375 return true;
376}
[email protected]eac0709a2008-11-04 21:00:46377bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
378 return GetFileInfo(FilePath::FromWStringHack(file_path), results);
379}
[email protected]ceeb87e2008-12-04 20:46:06380std::wstring GetFilenameFromPath(const std::wstring& path) {
381 if (path.empty() || EndsWithSeparator(path))
382 return std::wstring();
383
384 return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
385}
[email protected]eac0709a2008-11-04 21:00:46386bool GetFileSize(const std::wstring& file_path, int64* file_size) {
387 return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
388}
[email protected]640517f2008-10-30 23:54:04389bool GetTempDir(std::wstring* path_str) {
390 FilePath path;
391 if (!GetTempDir(&path))
392 return false;
393 *path_str = path.ToWStringHack();
394 return true;
395}
[email protected]b9e04f02008-11-27 04:03:57396bool Move(const std::wstring& from_path, const std::wstring& to_path) {
397 return Move(FilePath::FromWStringHack(from_path),
398 FilePath::FromWStringHack(to_path));
399}
[email protected]a9cd2a652008-11-17 21:01:19400FILE* OpenFile(const std::wstring& filename, const char* mode) {
401 return OpenFile(FilePath::FromWStringHack(filename), mode);
402}
[email protected]b9e04f02008-11-27 04:03:57403bool PathExists(const std::wstring& path) {
404 return PathExists(FilePath::FromWStringHack(path));
405}
[email protected]7e1fde6a2008-12-23 20:20:10406bool PathIsWritable(const std::wstring& path) {
407 return PathIsWritable(FilePath::FromWStringHack(path));
408}
[email protected]a9cd2a652008-11-17 21:01:19409bool SetCurrentDirectory(const std::wstring& directory) {
410 return SetCurrentDirectory(FilePath::FromWStringHack(directory));
411}
[email protected]b9e04f02008-11-27 04:03:57412void TrimFilename(std::wstring* path) {
413 if (EndsWithSeparator(path)) {
414 TrimTrailingSeparator(path);
415 } else {
416 *path = FilePath::FromWStringHack(*path).DirName().ToWStringHack();
417 }
418}
419void 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}
initial.commitd7cae122008-07-26 21:49:38437} // namespace
license.botbf09a502008-08-24 00:55:55438