• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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