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