Home | History | Annotate | Download | only in base
      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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    196 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
    197   FilePath directory;
    198   if (!GetTempDir(&directory))
    199     return false;
    200 
    201   return CreateAndOpenTemporaryFileInDir(directory, path);
    202 }
    203 
    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 
    212 bool CloseFile(FILE* file) {
    213   if (file == NULL)
    214     return true;
    215   return fclose(file) == 0;
    216 }
    217 
    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 
    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 
    266 MemoryMappedFile::~MemoryMappedFile() {
    267   CloseHandles();
    268 }
    269 
    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 
    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 
    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 
    309 bool MemoryMappedFile::IsValid() {
    310   return data_ != NULL;
    311 }
    312 
    313 // Deprecated functions ----------------------------------------------------
    314 
    315 bool ReadFileToString(const std::wstring& path, std::string* contents) {
    316   return ReadFileToString(FilePath::FromWStringHack(path), contents);
    317 }
    318 
    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 }
    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 }
    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 }
    342 bool Delete(const std::wstring& path, bool recursive) {
    343   return Delete(FilePath::FromWStringHack(path), recursive);
    344 }
    345 bool EndsWithSeparator(std::wstring* path) {
    346   return EndsWithSeparator(FilePath::FromWStringHack(*path));
    347 }
    348 bool EndsWithSeparator(const std::wstring& path) {
    349   return EndsWithSeparator(FilePath::FromWStringHack(path));
    350 }
    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 }
    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 }
    367 bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
    368   return GetFileInfo(FilePath::FromWStringHack(file_path), results);
    369 }
    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 }
    376 bool GetFileSize(const std::wstring& file_path, int64* file_size) {
    377   return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
    378 }
    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 }
    386 FILE* OpenFile(const std::wstring& filename, const char* mode) {
    387   return OpenFile(FilePath::FromWStringHack(filename), mode);
    388 }
    389 int ReadFile(const std::wstring& filename, char* data, int size) {
    390   return ReadFile(FilePath::FromWStringHack(filename), data, size);
    391 }
    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 }
    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 }
    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 
    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 
    424 bool FileEnumerator::IsDot(const FilePath& path) {
    425   return FILE_PATH_LITERAL(".") == path.BaseName().value();
    426 }
    427 
    428 bool FileEnumerator::IsDotDot(const FilePath& path) {
    429   return FILE_PATH_LITERAL("..") == path.BaseName().value();
    430 }
    431 
    432 }  // namespace
    433