file_util.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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
20namespace {
21
22const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
23
24}  // namespace
25
26namespace file_util {
27
28bool 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
36bool 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
50FilePath::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
59void 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
79void 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
106bool 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
140bool 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
180bool 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
196FILE* CreateAndOpenTemporaryFile(FilePath* path) {
197  FilePath directory;
198  if (!GetTempDir(&directory))
199    return false;
200
201  return CreateAndOpenTemporaryFileInDir(directory, path);
202}
203
204bool 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
212bool CloseFile(FILE* file) {
213  if (file == NULL)
214    return true;
215  return fclose(file) == 0;
216}
217
218bool 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
236bool 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
266MemoryMappedFile::~MemoryMappedFile() {
267  CloseHandles();
268}
269
270bool 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
284bool 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
296bool 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
309bool MemoryMappedFile::IsValid() {
310  return data_ != NULL;
311}
312
313// Deprecated functions ----------------------------------------------------
314
315bool ReadFileToString(const std::wstring& path, std::string* contents) {
316  return ReadFileToString(FilePath::FromWStringHack(path), contents);
317}
318
319bool 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}
326void 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}
336bool 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}
342bool Delete(const std::wstring& path, bool recursive) {
343  return Delete(FilePath::FromWStringHack(path), recursive);
344}
345bool EndsWithSeparator(std::wstring* path) {
346  return EndsWithSeparator(FilePath::FromWStringHack(*path));
347}
348bool EndsWithSeparator(const std::wstring& path) {
349  return EndsWithSeparator(FilePath::FromWStringHack(path));
350}
351bool GetCurrentDirectory(std::wstring* path_str) {
352  FilePath path;
353  if (!GetCurrentDirectory(&path))
354    return false;
355  *path_str = path.ToWStringHack();
356  return true;
357}
358std::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}
367bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
368  return GetFileInfo(FilePath::FromWStringHack(file_path), results);
369}
370std::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}
376bool GetFileSize(const std::wstring& file_path, int64* file_size) {
377  return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
378}
379bool GetTempDir(std::wstring* path_str) {
380  FilePath path;
381  if (!GetTempDir(&path))
382    return false;
383  *path_str = path.ToWStringHack();
384  return true;
385}
386FILE* OpenFile(const std::wstring& filename, const char* mode) {
387  return OpenFile(FilePath::FromWStringHack(filename), mode);
388}
389int ReadFile(const std::wstring& filename, char* data, int size) {
390  return ReadFile(FilePath::FromWStringHack(filename), data, size);
391}
392void 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}
400void 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}
410int 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
419bool 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
424bool FileEnumerator::IsDot(const FilePath& path) {
425  return FILE_PATH_LITERAL(".") == path.BaseName().value();
426}
427
428bool FileEnumerator::IsDotDot(const FilePath& path) {
429  return FILE_PATH_LITERAL("..") == path.BaseName().value();
430}
431
432}  // namespace
433