blob: 187e7b00368d57a03c46eb75054245334d3e3c06 [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
initial.commitd7cae122008-07-26 21:49:3819namespace file_util {
20
[email protected]b9e04f02008-11-27 04:03:5721void PathComponents(const FilePath& path,
22 std::vector<FilePath::StringType>* components) {
23 DCHECK(components);
24 if (!components)
[email protected]37088fef2008-08-15 17:32:1025 return;
[email protected]37088fef2008-08-15 17:32:1026
[email protected]b9e04f02008-11-27 04:03:5727 FilePath::StringType path_str = path.value();
28 FilePath::StringType::size_type start = 0;
29 FilePath::StringType::size_type end =
30 path_str.find_first_of(FilePath::kSeparators);
31
32 // If the path starts with a separator, add it to components.
[email protected]37088fef2008-08-15 17:32:1033 if (end == start) {
[email protected]b9e04f02008-11-27 04:03:5734 components->push_back(FilePath::StringType(path_str, 0, 1));
[email protected]37088fef2008-08-15 17:32:1035 start = end + 1;
[email protected]b9e04f02008-11-27 04:03:5736 end = path_str.find_first_of(FilePath::kSeparators, start);
[email protected]37088fef2008-08-15 17:32:1037 }
[email protected]b9e04f02008-11-27 04:03:5738 while (end != FilePath::StringType::npos) {
39 FilePath::StringType component =
40 FilePath::StringType(path_str, start, end - start);
[email protected]37088fef2008-08-15 17:32:1041 components->push_back(component);
42 start = end + 1;
[email protected]b9e04f02008-11-27 04:03:5743 end = path_str.find_first_of(FilePath::kSeparators, start);
[email protected]37088fef2008-08-15 17:32:1044 }
[email protected]b9e04f02008-11-27 04:03:5745
46 components->push_back(FilePath::StringType(path_str, start));
[email protected]37088fef2008-08-15 17:32:1047}
[email protected]7e263942008-11-25 22:04:3748
[email protected]b9e04f02008-11-27 04:03:5749bool EndsWithSeparator(const FilePath& path) {
50 FilePath::StringType value = path.value();
51 if (value.empty())
52 return false;
53
54 return FilePath::IsSeparator(value[value.size() - 1]);
initial.commitd7cae122008-07-26 21:49:3855}
56
[email protected]7e263942008-11-25 22:04:3757bool EnsureEndsWithSeparator(FilePath* path) {
58 if (!DirectoryExists(*path))
59 return false;
60
61 if (EndsWithSeparator(*path))
62 return true;
63
64 FilePath::StringType& path_str =
65 const_cast<FilePath::StringType&>(path->value());
66 path_str.append(&FilePath::kSeparators[0], 1);
67
68 return true;
69}
70
initial.commitd7cae122008-07-26 21:49:3871void TrimTrailingSeparator(std::wstring* dir) {
[email protected]37088fef2008-08-15 17:32:1072 while (dir->length() > 1 && EndsWithSeparator(dir))
initial.commitd7cae122008-07-26 21:49:3873 dir->resize(dir->length() - 1);
74}
75
[email protected]5af2edb92008-08-08 20:16:0876std::wstring GetFilenameFromPath(const std::wstring& path) {
[email protected]e6621132008-08-15 18:00:4877 // TODO(erikkay): fix this - it's not using kPathSeparator, but win unit test
78 // are exercising '/' as a path separator as well.
79 std::wstring::size_type pos = path.find_last_of(L"\\/");
[email protected]52ab8f902008-11-03 16:14:4680 return std::wstring(path, pos == std::wstring::npos ? 0 : pos + 1);
initial.commitd7cae122008-07-26 21:49:3881}
82
[email protected]5af2edb92008-08-08 20:16:0883std::wstring GetFileExtensionFromPath(const std::wstring& path) {
84 std::wstring file_name = GetFilenameFromPath(path);
85 std::wstring::size_type last_dot = file_name.rfind(L'.');
[email protected]52ab8f902008-11-03 16:14:4686 return std::wstring(last_dot == std::wstring::npos ?
87 L"" :
88 file_name, last_dot+1);
89}
90
91std::wstring GetFilenameWithoutExtensionFromPath(const std::wstring& path) {
92 std::wstring file_name = GetFilenameFromPath(path);
93 std::wstring::size_type last_dot = file_name.rfind(L'.');
94 return file_name.substr(0, last_dot);
initial.commitd7cae122008-07-26 21:49:3895}
96
initial.commitd7cae122008-07-26 21:49:3897void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) {
98 DCHECK(file_name);
99
[email protected]d324ab332008-08-18 16:00:38100 // Control characters, formatting characters, non-characters, and
initial.commitd7cae122008-07-26 21:49:38101 // some printable ASCII characters regarded as dangerous ('"*/:<>?\\').
102 // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx
103 // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx
104 // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they
105 // are legitimate in Arabic and some S/SE Asian scripts. However, when used
106 // elsewhere, they can be confusing/problematic.
107 // Also, consider wrapping the set with our Singleton class to create and
108 // freeze it only once. Note that there's a trade-off between memory and
109 // speed.
110
111 UErrorCode status = U_ZERO_ERROR;
[email protected]5af2edb92008-08-08 20:16:08112#if defined(WCHAR_T_IS_UTF16)
initial.commitd7cae122008-07-26 21:49:38113 UnicodeSet illegal_characters(UnicodeString(
114 L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status);
115#else
116 UnicodeSet illegal_characters(UNICODE_STRING_SIMPLE(
117 "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), status);
118#endif
119 DCHECK(U_SUCCESS(status));
120 // Add non-characters. If this becomes a performance bottleneck by
121 // any chance, check |ucs4 & 0xFFFEu == 0xFFFEu|, instead.
122 illegal_characters.add(0xFDD0, 0xFDEF);
123 for (int i = 0; i <= 0x10; ++i) {
124 int plane_base = 0x10000 * i;
125 illegal_characters.add(plane_base + 0xFFFE, plane_base + 0xFFFF);
126 }
127 illegal_characters.freeze();
128 DCHECK(!illegal_characters.contains(replace_char) && replace_char < 0x10000);
129
130 // Remove leading and trailing whitespace.
131 TrimWhitespace(*file_name, TRIM_ALL, file_name);
132
133 std::wstring::size_type i = 0;
134 std::wstring::size_type length = file_name->size();
[email protected]5af2edb92008-08-08 20:16:08135 const wchar_t* wstr = file_name->data();
136#if defined(WCHAR_T_IS_UTF16)
initial.commitd7cae122008-07-26 21:49:38137 // Using |span| method of UnicodeSet might speed things up a bit, but
138 // it's not likely to matter here.
initial.commitd7cae122008-07-26 21:49:38139 std::wstring temp;
140 temp.reserve(length);
141 while (i < length) {
142 UChar32 ucs4;
143 std::wstring::size_type prev = i;
144 U16_NEXT(wstr, i, length, ucs4);
145 if (illegal_characters.contains(ucs4)) {
146 temp.push_back(replace_char);
147 } else if (ucs4 < 0x10000) {
148 temp.push_back(ucs4);
149 } else {
150 temp.push_back(wstr[prev]);
151 temp.push_back(wstr[prev + 1]);
152 }
153 }
154 file_name->swap(temp);
[email protected]5af2edb92008-08-08 20:16:08155#elif defined(WCHAR_T_IS_UTF32)
initial.commitd7cae122008-07-26 21:49:38156 while (i < length) {
157 if (illegal_characters.contains(wstr[i])) {
[email protected]5af2edb92008-08-08 20:16:08158 (*file_name)[i] = replace_char;
initial.commitd7cae122008-07-26 21:49:38159 }
[email protected]37088fef2008-08-15 17:32:10160 ++i;
initial.commitd7cae122008-07-26 21:49:38161 }
162#else
163#error wchar_t* should be either UTF-16 or UTF-32
164#endif
165}
166
[email protected]640517f2008-10-30 23:54:04167bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commitd7cae122008-07-26 21:49:38168 // We open the file in binary format even if they are text files because
169 // we are just comparing that bytes are exactly same in both files and not
170 // doing anything smart with text formatting.
[email protected]640517f2008-10-30 23:54:04171 std::ifstream file1(filename1.value().c_str(),
[email protected]5af2edb92008-08-08 20:16:08172 std::ios::in | std::ios::binary);
[email protected]640517f2008-10-30 23:54:04173 std::ifstream file2(filename2.value().c_str(),
[email protected]5af2edb92008-08-08 20:16:08174 std::ios::in | std::ios::binary);
[email protected]b9e04f02008-11-27 04:03:57175
initial.commitd7cae122008-07-26 21:49:38176 // Even if both files aren't openable (and thus, in some sense, "equal"),
177 // any unusable file yields a result of "false".
178 if (!file1.is_open() || !file2.is_open())
179 return false;
180
181 const int BUFFER_SIZE = 2056;
182 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
183 do {
184 file1.read(buffer1, BUFFER_SIZE);
185 file2.read(buffer2, BUFFER_SIZE);
186
187 if ((file1.eof() && !file2.eof()) ||
188 (!file1.eof() && file2.eof()) ||
189 (file1.gcount() != file2.gcount()) ||
190 (memcmp(buffer1, buffer2, file1.gcount()))) {
191 file1.close();
192 file2.close();
193 return false;
194 }
195 } while (!file1.eof() && !file2.eof());
196
197 file1.close();
198 file2.close();
199 return true;
200}
201
202bool ReadFileToString(const std::wstring& path, std::string* contents) {
[email protected]836f1342008-10-01 17:40:13203 FILE* file = OpenFile(path, "rb");
204 if (!file) {
initial.commitd7cae122008-07-26 21:49:38205 return false;
[email protected]836f1342008-10-01 17:40:13206 }
initial.commitd7cae122008-07-26 21:49:38207
208 char buf[1 << 16];
209 size_t len;
210 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
211 contents->append(buf, len);
212 }
[email protected]836f1342008-10-01 17:40:13213 CloseFile(file);
initial.commitd7cae122008-07-26 21:49:38214
215 return true;
216}
217
[email protected]eac0709a2008-11-04 21:00:46218bool GetFileSize(const FilePath& file_path, int64* file_size) {
[email protected]f5e3da4d2008-09-26 01:04:08219 FileInfo info;
220 if (!GetFileInfo(file_path, &info))
221 return false;
222 *file_size = info.size;
223 return true;
224}
225
[email protected]836f1342008-10-01 17:40:13226bool CloseFile(FILE* file) {
[email protected]a1a19502008-10-21 17:14:45227 if (file == NULL)
228 return true;
[email protected]836f1342008-10-01 17:40:13229 return fclose(file) == 0;
230}
231
[email protected]640517f2008-10-30 23:54:04232// Deprecated functions ----------------------------------------------------
233
234bool AbsolutePath(std::wstring* path_str) {
[email protected]4a2952f2008-10-31 02:03:07235 FilePath path(FilePath::FromWStringHack(*path_str));
[email protected]640517f2008-10-30 23:54:04236 if (!AbsolutePath(&path))
237 return false;
238 *path_str = path.ToWStringHack();
239 return true;
240}
[email protected]b9e04f02008-11-27 04:03:57241void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
242 if (!path) {
243 NOTREACHED();
244 return; // Don't crash in this function in release builds.
245 }
246
247 if (!EndsWithSeparator(path))
248 path->push_back(FilePath::kSeparators[0]);
249 path->append(new_ending);
[email protected]640517f2008-10-30 23:54:04250}
251bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
252 bool recursive) {
253 return CopyDirectory(FilePath::FromWStringHack(from_path),
254 FilePath::FromWStringHack(to_path),
255 recursive);
256}
[email protected]640517f2008-10-30 23:54:04257bool ContentsEqual(const std::wstring& filename1,
258 const std::wstring& filename2) {
259 return ContentsEqual(FilePath::FromWStringHack(filename1),
260 FilePath::FromWStringHack(filename2));
261}
[email protected]b9e04f02008-11-27 04:03:57262bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
263 return CopyFile(FilePath::FromWStringHack(from_path),
264 FilePath::FromWStringHack(to_path));
265}
[email protected]640517f2008-10-30 23:54:04266bool CreateDirectory(const std::wstring& full_path) {
267 return CreateDirectory(FilePath::FromWStringHack(full_path));
268}
[email protected]392264c2008-11-11 00:01:38269bool CreateTemporaryFileName(std::wstring* temp_file) {
270 FilePath temp_file_path;
271 if (!CreateTemporaryFileName(&temp_file_path))
272 return false;
273 *temp_file = temp_file_path.ToWStringHack();
274 return true;
275}
[email protected]b9e04f02008-11-27 04:03:57276bool Delete(const std::wstring& path, bool recursive) {
277 return Delete(FilePath::FromWStringHack(path), recursive);
278}
279bool DirectoryExists(const std::wstring& path) {
280 return DirectoryExists(FilePath::FromWStringHack(path));
281}
282bool EndsWithSeparator(std::wstring* path) {
283 return EndsWithSeparator(FilePath::FromWStringHack(*path));
284}
285bool EndsWithSeparator(const std::wstring& path) {
286 return EndsWithSeparator(FilePath::FromWStringHack(path));
287}
[email protected]640517f2008-10-30 23:54:04288bool GetCurrentDirectory(std::wstring* path_str) {
289 FilePath path;
290 if (!GetCurrentDirectory(&path))
291 return false;
292 *path_str = path.ToWStringHack();
293 return true;
294}
[email protected]eac0709a2008-11-04 21:00:46295bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
296 return GetFileInfo(FilePath::FromWStringHack(file_path), results);
297}
298bool GetFileSize(const std::wstring& file_path, int64* file_size) {
299 return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
300}
[email protected]640517f2008-10-30 23:54:04301bool GetTempDir(std::wstring* path_str) {
302 FilePath path;
303 if (!GetTempDir(&path))
304 return false;
305 *path_str = path.ToWStringHack();
306 return true;
307}
[email protected]b9e04f02008-11-27 04:03:57308bool Move(const std::wstring& from_path, const std::wstring& to_path) {
309 return Move(FilePath::FromWStringHack(from_path),
310 FilePath::FromWStringHack(to_path));
311}
[email protected]a9cd2a652008-11-17 21:01:19312FILE* OpenFile(const std::wstring& filename, const char* mode) {
313 return OpenFile(FilePath::FromWStringHack(filename), mode);
314}
[email protected]b9e04f02008-11-27 04:03:57315bool PathExists(const std::wstring& path) {
316 return PathExists(FilePath::FromWStringHack(path));
317}
[email protected]a9cd2a652008-11-17 21:01:19318bool SetCurrentDirectory(const std::wstring& directory) {
319 return SetCurrentDirectory(FilePath::FromWStringHack(directory));
320}
[email protected]b9e04f02008-11-27 04:03:57321void TrimFilename(std::wstring* path) {
322 if (EndsWithSeparator(path)) {
323 TrimTrailingSeparator(path);
324 } else {
325 *path = FilePath::FromWStringHack(*path).DirName().ToWStringHack();
326 }
327}
328void UpOneDirectory(std::wstring* dir) {
329 FilePath path = FilePath::FromWStringHack(*dir);
330 FilePath directory = path.DirName();
331 // If there is no separator, we will get back kCurrentDirectory.
332 // In this case don't change |dir|.
333 if (directory.value() != FilePath::kCurrentDirectory)
334 *dir = directory.ToWStringHack();
335}
336void UpOneDirectoryOrEmpty(std::wstring* dir) {
337 FilePath path = FilePath::FromWStringHack(*dir);
338 FilePath directory = path.DirName();
339 // If there is no separator, we will get back kCurrentDirectory.
340 // In this case, clear dir.
341 if (directory == path || directory.value() == FilePath::kCurrentDirectory)
342 dir->clear();
343 else
344 *dir = directory.ToWStringHack();
345}
initial.commitd7cae122008-07-26 21:49:38346} // namespace
license.botbf09a502008-08-24 00:55:55347