1 : // Copyright (c) 2006-2009 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.
4 :
5 : #include "base/file_util.h"
6 :
7 : #if defined(OS_WIN)
8 : #include <io.h>
9 : #endif
10 : #include <stdio.h>
11 : #if defined(ANDROID) || defined(OS_POSIX)
12 : #include <unistd.h>
13 : #endif
14 :
15 : #include <fstream>
16 :
17 : #include "base/file_path.h"
18 : #include "base/logging.h"
19 : #include "base/string_util.h"
20 :
21 : #include "base/string_piece.h"
22 : #include "base/sys_string_conversions.h"
23 :
24 : namespace {
25 :
26 : const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
27 :
28 : } // namespace
29 :
30 : namespace file_util {
31 :
32 0 : void PathComponents(const FilePath& path,
33 : std::vector<FilePath::StringType>* components) {
34 0 : DCHECK(components);
35 0 : if (!components)
36 0 : return;
37 :
38 0 : FilePath::StringType path_str = path.value();
39 0 : FilePath::StringType::size_type start = 0;
40 : FilePath::StringType::size_type end =
41 0 : path_str.find_first_of(FilePath::kSeparators);
42 :
43 : // If the path starts with a separator, add it to components.
44 0 : if (end == start) {
45 0 : components->push_back(FilePath::StringType(path_str, 0, 1));
46 0 : start = end + 1;
47 0 : end = path_str.find_first_of(FilePath::kSeparators, start);
48 : }
49 0 : while (end != FilePath::StringType::npos) {
50 : FilePath::StringType component =
51 0 : FilePath::StringType(path_str, start, end - start);
52 0 : components->push_back(component);
53 0 : start = end + 1;
54 0 : end = path_str.find_first_of(FilePath::kSeparators, start);
55 : }
56 :
57 0 : components->push_back(FilePath::StringType(path_str, start));
58 : }
59 :
60 0 : bool EndsWithSeparator(const FilePath& path) {
61 0 : FilePath::StringType value = path.value();
62 0 : if (value.empty())
63 0 : return false;
64 :
65 0 : return FilePath::IsSeparator(value[value.size() - 1]);
66 : }
67 :
68 0 : bool EnsureEndsWithSeparator(FilePath* path) {
69 0 : if (!DirectoryExists(*path))
70 0 : return false;
71 :
72 0 : if (EndsWithSeparator(*path))
73 0 : return true;
74 :
75 : FilePath::StringType& path_str =
76 0 : const_cast<FilePath::StringType&>(path->value());
77 0 : path_str.append(&FilePath::kSeparators[0], 1);
78 :
79 0 : return true;
80 : }
81 :
82 0 : void TrimTrailingSeparator(std::wstring* dir) {
83 0 : while (dir->length() > 1 && EndsWithSeparator(dir))
84 0 : dir->resize(dir->length() - 1);
85 0 : }
86 :
87 0 : FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
88 0 : FilePath::StringType file_name = path.BaseName().value();
89 : const FilePath::StringType::size_type last_dot =
90 0 : file_name.rfind(kExtensionSeparator);
91 : return FilePath::StringType(last_dot == FilePath::StringType::npos ?
92 : FILE_PATH_LITERAL("") :
93 0 : file_name, last_dot+1);
94 : }
95 :
96 0 : void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
97 : FilePath::StringType& value =
98 0 : const_cast<FilePath::StringType&>(path->value());
99 :
100 : const FilePath::StringType::size_type last_dot =
101 0 : value.rfind(kExtensionSeparator);
102 : const FilePath::StringType::size_type last_separator =
103 0 : value.find_last_of(FilePath::StringType(FilePath::kSeparators));
104 :
105 0 : 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 0 : value.append(suffix);
110 0 : return;
111 : }
112 :
113 0 : value.insert(last_dot, suffix);
114 : }
115 :
116 0 : void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
117 0 : FilePath::StringType clean_extension;
118 : // If the new extension is "" or ".", then we will just remove the current
119 : // extension.
120 0 : if (!extension.empty() &&
121 0 : extension != FilePath::StringType(&kExtensionSeparator, 1)) {
122 0 : if (extension[0] != kExtensionSeparator)
123 0 : clean_extension.append(&kExtensionSeparator, 1);
124 0 : clean_extension.append(extension);
125 : }
126 :
127 : FilePath::StringType& value =
128 0 : const_cast<FilePath::StringType&>(path->value());
129 : const FilePath::StringType::size_type last_dot =
130 0 : value.rfind(kExtensionSeparator);
131 : const FilePath::StringType::size_type last_separator =
132 0 : value.find_last_of(FilePath::StringType(FilePath::kSeparators));
133 :
134 : // Erase the current extension, if any.
135 0 : if ((last_dot > last_separator ||
136 : last_separator == FilePath::StringType::npos) &&
137 : last_dot != FilePath::StringType::npos)
138 0 : value.erase(last_dot);
139 :
140 0 : value.append(clean_extension);
141 0 : }
142 :
143 0 : bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
144 : // We open the file in binary format even if they are text files because
145 : // we are just comparing that bytes are exactly same in both files and not
146 : // doing anything smart with text formatting.
147 0 : std::ifstream file1, file2;
148 :
149 0 : filename1.OpenInputStream(file1);
150 0 : filename2.OpenInputStream(file2);
151 :
152 : // Even if both files aren't openable (and thus, in some sense, "equal"),
153 : // any unusable file yields a result of "false".
154 0 : if (!file1.is_open() || !file2.is_open())
155 0 : return false;
156 :
157 0 : const int BUFFER_SIZE = 2056;
158 : char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
159 0 : do {
160 0 : file1.read(buffer1, BUFFER_SIZE);
161 0 : file2.read(buffer2, BUFFER_SIZE);
162 :
163 0 : if ((file1.eof() && !file2.eof()) ||
164 0 : (!file1.eof() && file2.eof()) ||
165 0 : (file1.gcount() != file2.gcount()) ||
166 0 : (memcmp(buffer1, buffer2, file1.gcount()))) {
167 0 : file1.close();
168 0 : file2.close();
169 0 : return false;
170 : }
171 0 : } while (!file1.eof() && !file2.eof());
172 :
173 0 : file1.close();
174 0 : file2.close();
175 0 : return true;
176 : }
177 :
178 0 : bool ReadFileToString(const FilePath& path, std::string* contents) {
179 0 : FILE* file = OpenFile(path, "rb");
180 0 : if (!file) {
181 0 : return false;
182 : }
183 :
184 : char buf[1 << 16];
185 : size_t len;
186 0 : while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
187 0 : contents->append(buf, len);
188 : }
189 0 : CloseFile(file);
190 :
191 0 : return true;
192 : }
193 :
194 0 : FILE* CreateAndOpenTemporaryFile(FilePath* path) {
195 0 : FilePath directory;
196 0 : if (!GetTempDir(&directory))
197 0 : return false;
198 :
199 0 : return CreateAndOpenTemporaryFileInDir(directory, path);
200 : }
201 :
202 0 : bool GetFileSize(const FilePath& file_path, int64* file_size) {
203 : FileInfo info;
204 0 : if (!GetFileInfo(file_path, &info))
205 0 : return false;
206 0 : *file_size = info.size;
207 0 : return true;
208 : }
209 :
210 0 : bool CloseFile(FILE* file) {
211 0 : if (file == NULL)
212 0 : return true;
213 0 : return fclose(file) == 0;
214 : }
215 :
216 0 : bool TruncateFile(FILE* file) {
217 0 : if (file == NULL)
218 0 : return false;
219 0 : long current_offset = ftell(file);
220 0 : if (current_offset == -1)
221 0 : return false;
222 : #if defined(OS_WIN)
223 : int fd = _fileno(file);
224 : if (_chsize(fd, current_offset) != 0)
225 : return false;
226 : #else
227 0 : int fd = fileno(file);
228 0 : if (ftruncate(fd, current_offset) != 0)
229 0 : return false;
230 : #endif
231 0 : return true;
232 : }
233 :
234 0 : bool ContainsPath(const FilePath &parent, const FilePath& child) {
235 0 : FilePath abs_parent = FilePath(parent);
236 0 : FilePath abs_child = FilePath(child);
237 :
238 0 : if (!file_util::AbsolutePath(&abs_parent) ||
239 0 : !file_util::AbsolutePath(&abs_child))
240 0 : return false;
241 :
242 : #if defined(OS_WIN)
243 : // file_util::AbsolutePath() does not flatten case on Windows, so we must do
244 : // a case-insensitive compare.
245 : if (!StartsWith(abs_child.value(), abs_parent.value(), false))
246 : #else
247 0 : if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
248 : #endif
249 0 : return false;
250 :
251 : // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
252 : // to check kSeparators[0].
253 0 : if (abs_child.value().length() <= abs_parent.value().length() ||
254 0 : abs_child.value()[abs_parent.value().length()] !=
255 0 : FilePath::kSeparators[0])
256 0 : return false;
257 :
258 0 : return true;
259 : }
260 :
261 : ///////////////////////////////////////////////
262 : // MemoryMappedFile
263 :
264 0 : MemoryMappedFile::~MemoryMappedFile() {
265 0 : CloseHandles();
266 0 : }
267 :
268 0 : bool MemoryMappedFile::Initialize(const FilePath& file_name) {
269 0 : if (IsValid())
270 0 : return false;
271 :
272 0 : if (!MapFileToMemory(file_name)) {
273 0 : CloseHandles();
274 0 : return false;
275 : }
276 :
277 0 : return true;
278 : }
279 :
280 0 : bool MemoryMappedFile::IsValid() {
281 0 : return data_ != NULL;
282 : }
283 :
284 : // Deprecated functions ----------------------------------------------------
285 :
286 0 : bool ReadFileToString(const std::wstring& path, std::string* contents) {
287 0 : return ReadFileToString(FilePath::FromWStringHack(path), contents);
288 : }
289 :
290 0 : bool AbsolutePath(std::wstring* path_str) {
291 0 : FilePath path(FilePath::FromWStringHack(*path_str));
292 0 : if (!AbsolutePath(&path))
293 0 : return false;
294 0 : *path_str = path.ToWStringHack();
295 0 : return true;
296 : }
297 0 : void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
298 0 : if (!path) {
299 0 : NOTREACHED();
300 0 : return; // Don't crash in this function in release builds.
301 : }
302 :
303 0 : if (!EndsWithSeparator(path))
304 0 : path->push_back(FilePath::kSeparators[0]);
305 0 : path->append(new_ending);
306 : }
307 0 : bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
308 : bool recursive) {
309 0 : return CopyDirectory(FilePath::FromWStringHack(from_path),
310 0 : FilePath::FromWStringHack(to_path),
311 0 : recursive);
312 : }
313 0 : bool ContentsEqual(const std::wstring& filename1,
314 : const std::wstring& filename2) {
315 0 : return ContentsEqual(FilePath::FromWStringHack(filename1),
316 0 : FilePath::FromWStringHack(filename2));
317 : }
318 0 : bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
319 0 : return CopyFile(FilePath::FromWStringHack(from_path),
320 0 : FilePath::FromWStringHack(to_path));
321 : }
322 0 : bool CreateDirectory(const std::wstring& full_path) {
323 0 : return CreateDirectory(FilePath::FromWStringHack(full_path));
324 : }
325 0 : bool CreateNewTempDirectory(const std::wstring& prefix,
326 : std::wstring* new_temp_path) {
327 : #if defined(OS_WIN)
328 : FilePath::StringType dir_prefix(prefix);
329 : #elif defined(OS_POSIX)
330 0 : FilePath::StringType dir_prefix = WideToUTF8(prefix);
331 : #endif
332 0 : FilePath temp_path;
333 0 : if (!CreateNewTempDirectory(dir_prefix, &temp_path))
334 0 : return false;
335 0 : *new_temp_path = temp_path.ToWStringHack();
336 0 : return true;
337 : }
338 0 : bool CreateTemporaryFileName(std::wstring* temp_file) {
339 0 : FilePath temp_file_path;
340 0 : if (!CreateTemporaryFileName(&temp_file_path))
341 0 : return false;
342 0 : *temp_file = temp_file_path.ToWStringHack();
343 0 : return true;
344 : }
345 0 : bool Delete(const std::wstring& path, bool recursive) {
346 0 : return Delete(FilePath::FromWStringHack(path), recursive);
347 : }
348 0 : bool DirectoryExists(const std::wstring& path) {
349 0 : return DirectoryExists(FilePath::FromWStringHack(path));
350 : }
351 0 : bool EndsWithSeparator(std::wstring* path) {
352 0 : return EndsWithSeparator(FilePath::FromWStringHack(*path));
353 : }
354 0 : bool EndsWithSeparator(const std::wstring& path) {
355 0 : return EndsWithSeparator(FilePath::FromWStringHack(path));
356 : }
357 0 : bool GetCurrentDirectory(std::wstring* path_str) {
358 0 : FilePath path;
359 0 : if (!GetCurrentDirectory(&path))
360 0 : return false;
361 0 : *path_str = path.ToWStringHack();
362 0 : return true;
363 : }
364 0 : std::wstring GetFileExtensionFromPath(const std::wstring& path) {
365 : FilePath::StringType extension =
366 0 : GetFileExtensionFromPath(FilePath::FromWStringHack(path));
367 : #if defined(OS_WIN)
368 : return extension;
369 : #elif defined(OS_POSIX)
370 0 : return UTF8ToWide(extension);
371 : #endif
372 : }
373 0 : bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
374 0 : return GetFileInfo(FilePath::FromWStringHack(file_path), results);
375 : }
376 0 : std::wstring GetFilenameFromPath(const std::wstring& path) {
377 0 : if (path.empty() || EndsWithSeparator(path))
378 0 : return std::wstring();
379 :
380 0 : return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
381 : }
382 0 : bool GetFileSize(const std::wstring& file_path, int64* file_size) {
383 0 : return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
384 : }
385 0 : bool GetTempDir(std::wstring* path_str) {
386 0 : FilePath path;
387 0 : if (!GetTempDir(&path))
388 0 : return false;
389 0 : *path_str = path.ToWStringHack();
390 0 : return true;
391 : }
392 0 : bool Move(const std::wstring& from_path, const std::wstring& to_path) {
393 0 : return Move(FilePath::FromWStringHack(from_path),
394 0 : FilePath::FromWStringHack(to_path));
395 : }
396 0 : FILE* OpenFile(const std::wstring& filename, const char* mode) {
397 0 : return OpenFile(FilePath::FromWStringHack(filename), mode);
398 : }
399 0 : bool PathExists(const std::wstring& path) {
400 0 : return PathExists(FilePath::FromWStringHack(path));
401 : }
402 0 : bool PathIsWritable(const std::wstring& path) {
403 0 : return PathIsWritable(FilePath::FromWStringHack(path));
404 : }
405 0 : int ReadFile(const std::wstring& filename, char* data, int size) {
406 0 : return ReadFile(FilePath::FromWStringHack(filename), data, size);
407 : }
408 0 : bool SetCurrentDirectory(const std::wstring& directory) {
409 0 : return SetCurrentDirectory(FilePath::FromWStringHack(directory));
410 : }
411 0 : void UpOneDirectory(std::wstring* dir) {
412 0 : FilePath path = FilePath::FromWStringHack(*dir);
413 0 : FilePath directory = path.DirName();
414 : // If there is no separator, we will get back kCurrentDirectory.
415 : // In this case don't change |dir|.
416 0 : if (directory.value() != FilePath::kCurrentDirectory)
417 0 : *dir = directory.ToWStringHack();
418 0 : }
419 0 : void UpOneDirectoryOrEmpty(std::wstring* dir) {
420 0 : FilePath path = FilePath::FromWStringHack(*dir);
421 0 : FilePath directory = path.DirName();
422 : // If there is no separator, we will get back kCurrentDirectory.
423 : // In this case, clear dir.
424 0 : if (directory == path || directory.value() == FilePath::kCurrentDirectory)
425 0 : dir->clear();
426 : else
427 0 : *dir = directory.ToWStringHack();
428 0 : }
429 0 : int WriteFile(const std::wstring& filename, const char* data, int size) {
430 0 : return WriteFile(FilePath::FromWStringHack(filename), data, size);
431 : }
432 : } // namespace
|