Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2010 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 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
     80   // We open the file in binary format even if they are text files because
     81   // we are just comparing that bytes are exactly same in both files and not
     82   // doing anything smart with text formatting.
     83   std::ifstream file1(filename1.value().c_str(),
     84                       std::ios::in | std::ios::binary);
     85   std::ifstream file2(filename2.value().c_str(),
     86                       std::ios::in | std::ios::binary);
     87 
     88   // Even if both files aren't openable (and thus, in some sense, "equal"),
     89   // any unusable file yields a result of "false".
     90   if (!file1.is_open() || !file2.is_open())
     91     return false;
     92 
     93   const int BUFFER_SIZE = 2056;
     94   char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
     95   do {
     96     file1.read(buffer1, BUFFER_SIZE);
     97     file2.read(buffer2, BUFFER_SIZE);
     98 
     99     if ((file1.eof() != file2.eof()) ||
    100         (file1.gcount() != file2.gcount()) ||
    101         (memcmp(buffer1, buffer2, file1.gcount()))) {
    102       file1.close();
    103       file2.close();
    104       return false;
    105     }
    106   } while (!file1.eof() || !file2.eof());
    107 
    108   file1.close();
    109   file2.close();
    110   return true;
    111 }
    112 
    113 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
    114   std::ifstream file1(filename1.value().c_str(), std::ios::in);
    115   std::ifstream file2(filename2.value().c_str(), std::ios::in);
    116 
    117   // Even if both files aren't openable (and thus, in some sense, "equal"),
    118   // any unusable file yields a result of "false".
    119   if (!file1.is_open() || !file2.is_open())
    120     return false;
    121 
    122   do {
    123     std::string line1, line2;
    124     getline(file1, line1);
    125     getline(file2, line2);
    126 
    127     // Check for mismatched EOF states, or any error state.
    128     if ((file1.eof() != file2.eof()) ||
    129         file1.bad() || file2.bad()) {
    130       return false;
    131     }
    132 
    133     // Trim all '\r' and '\n' characters from the end of the line.
    134     std::string::size_type end1 = line1.find_last_not_of("\r\n");
    135     if (end1 == std::string::npos)
    136       line1.clear();
    137     else if (end1 + 1 < line1.length())
    138       line1.erase(end1 + 1);
    139 
    140     std::string::size_type end2 = line2.find_last_not_of("\r\n");
    141     if (end2 == std::string::npos)
    142       line2.clear();
    143     else if (end2 + 1 < line2.length())
    144       line2.erase(end2 + 1);
    145 
    146     if (line1 != line2)
    147       return false;
    148   } while (!file1.eof() || !file2.eof());
    149 
    150   return true;
    151 }
    152 
    153 bool ReadFileToString(const FilePath& path, std::string* contents) {
    154   FILE* file = OpenFile(path, "rb");
    155   if (!file) {
    156     return false;
    157   }
    158 
    159   char buf[1 << 16];
    160   size_t len;
    161   while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
    162     if (contents)
    163       contents->append(buf, len);
    164   }
    165   CloseFile(file);
    166 
    167   return true;
    168 }
    169 
    170 bool IsDirectoryEmpty(const FilePath& dir_path) {
    171   FileEnumerator files(dir_path, false,
    172       static_cast<FileEnumerator::FILE_TYPE>(
    173           FileEnumerator::FILES | FileEnumerator::DIRECTORIES));
    174   if (files.Next().value().empty())
    175     return true;
    176   return false;
    177 }
    178 
    179 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
    180   FilePath directory;
    181   if (!GetTempDir(&directory))
    182     return NULL;
    183 
    184   return CreateAndOpenTemporaryFileInDir(directory, path);
    185 }
    186 
    187 bool GetFileSize(const FilePath& file_path, int64* file_size) {
    188   base::PlatformFileInfo info;
    189   if (!GetFileInfo(file_path, &info))
    190     return false;
    191   *file_size = info.size;
    192   return true;
    193 }
    194 
    195 bool IsDot(const FilePath& path) {
    196   return FILE_PATH_LITERAL(".") == path.BaseName().value();
    197 }
    198 
    199 bool IsDotDot(const FilePath& path) {
    200   return FILE_PATH_LITERAL("..") == path.BaseName().value();
    201 }
    202 
    203 bool TouchFile(const FilePath& path,
    204                const base::Time& last_accessed,
    205                const base::Time& last_modified) {
    206   base::PlatformFile file =
    207       base::CreatePlatformFile(path,
    208                                base::PLATFORM_FILE_OPEN |
    209                                base::PLATFORM_FILE_WRITE_ATTRIBUTES,
    210                                NULL, NULL);
    211   if (file != base::kInvalidPlatformFileValue) {
    212     bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
    213     base::ClosePlatformFile(file);
    214     return result;
    215   }
    216 
    217   return false;
    218 }
    219 
    220 bool SetLastModifiedTime(const FilePath& path,
    221                          const base::Time& last_modified) {
    222   return TouchFile(path, last_modified, last_modified);
    223 }
    224 
    225 bool CloseFile(FILE* file) {
    226   if (file == NULL)
    227     return true;
    228   return fclose(file) == 0;
    229 }
    230 
    231 bool TruncateFile(FILE* file) {
    232   if (file == NULL)
    233     return false;
    234   long current_offset = ftell(file);
    235   if (current_offset == -1)
    236     return false;
    237 #if defined(OS_WIN)
    238   int fd = _fileno(file);
    239   if (_chsize(fd, current_offset) != 0)
    240     return false;
    241 #else
    242   int fd = fileno(file);
    243   if (ftruncate(fd, current_offset) != 0)
    244     return false;
    245 #endif
    246   return true;
    247 }
    248 
    249 bool ContainsPath(const FilePath &parent, const FilePath& child) {
    250   FilePath abs_parent = FilePath(parent);
    251   FilePath abs_child = FilePath(child);
    252 
    253   if (!file_util::AbsolutePath(&abs_parent) ||
    254       !file_util::AbsolutePath(&abs_child))
    255     return false;
    256 
    257 #if defined(OS_WIN)
    258   // file_util::AbsolutePath() does not flatten case on Windows, so we must do
    259   // a case-insensitive compare.
    260   if (!StartsWith(abs_child.value(), abs_parent.value(), false))
    261 #else
    262   if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
    263 #endif
    264     return false;
    265 
    266   // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
    267   // to check kSeparators[0].
    268   if (abs_child.value().length() <= abs_parent.value().length() ||
    269       abs_child.value()[abs_parent.value().length()] !=
    270           FilePath::kSeparators[0])
    271     return false;
    272 
    273   return true;
    274 }
    275 
    276 int64 ComputeDirectorySize(const FilePath& root_path) {
    277   int64 running_size = 0;
    278   FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
    279   for (FilePath current = file_iter.Next(); !current.empty();
    280        current = file_iter.Next()) {
    281     FileEnumerator::FindInfo info;
    282     file_iter.GetFindInfo(&info);
    283 #if defined(OS_WIN)
    284     LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
    285     running_size += li.QuadPart;
    286 #else
    287     running_size += info.stat.st_size;
    288 #endif
    289   }
    290   return running_size;
    291 }
    292 
    293 int64 ComputeFilesSize(const FilePath& directory,
    294                        const FilePath::StringType& pattern) {
    295   int64 running_size = 0;
    296   FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
    297   for (FilePath current = file_iter.Next(); !current.empty();
    298        current = file_iter.Next()) {
    299     FileEnumerator::FindInfo info;
    300     file_iter.GetFindInfo(&info);
    301 #if defined(OS_WIN)
    302     LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
    303     running_size += li.QuadPart;
    304 #else
    305     running_size += info.stat.st_size;
    306 #endif
    307   }
    308   return running_size;
    309 }
    310 
    311 ///////////////////////////////////////////////
    312 // MemoryMappedFile
    313 
    314 MemoryMappedFile::~MemoryMappedFile() {
    315   CloseHandles();
    316 }
    317 
    318 bool MemoryMappedFile::Initialize(const FilePath& file_name) {
    319   if (IsValid())
    320     return false;
    321 
    322   if (!MapFileToMemory(file_name)) {
    323     CloseHandles();
    324     return false;
    325   }
    326 
    327   return true;
    328 }
    329 
    330 bool MemoryMappedFile::Initialize(base::PlatformFile file) {
    331   if (IsValid())
    332     return false;
    333 
    334   file_ = file;
    335 
    336   if (!MapFileToMemoryInternal()) {
    337     CloseHandles();
    338     return false;
    339   }
    340 
    341   return true;
    342 }
    343 
    344 bool MemoryMappedFile::IsValid() const {
    345   return data_ != NULL;
    346 }
    347 
    348 bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
    349   file_ = base::CreatePlatformFile(
    350       file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
    351       NULL, NULL);
    352 
    353   if (file_ == base::kInvalidPlatformFileValue) {
    354     LOG(ERROR) << "Couldn't open " << file_name.value();
    355     return false;
    356   }
    357 
    358   return MapFileToMemoryInternal();
    359 }
    360 
    361 // Deprecated functions ----------------------------------------------------
    362 
    363 #if defined(OS_WIN)
    364 void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
    365   if (!path) {
    366     NOTREACHED();
    367     return;  // Don't crash in this function in release builds.
    368   }
    369 
    370   if (!EndsWithSeparator(FilePath(*path)))
    371     path->push_back(FilePath::kSeparators[0]);
    372   path->append(new_ending);
    373 }
    374 
    375 bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
    376                    bool recursive) {
    377   return CopyDirectory(FilePath::FromWStringHack(from_path),
    378                        FilePath::FromWStringHack(to_path),
    379                        recursive);
    380 }
    381 bool Delete(const std::wstring& path, bool recursive) {
    382   return Delete(FilePath::FromWStringHack(path), recursive);
    383 }
    384 std::wstring GetFileExtensionFromPath(const std::wstring& path) {
    385   FilePath::StringType extension =
    386       GetFileExtensionFromPath(FilePath::FromWStringHack(path));
    387   return extension;
    388 }
    389 FILE* OpenFile(const std::wstring& filename, const char* mode) {
    390   return OpenFile(FilePath::FromWStringHack(filename), mode);
    391 }
    392 int ReadFile(const std::wstring& filename, char* data, int size) {
    393   return ReadFile(FilePath::FromWStringHack(filename), data, size);
    394 }
    395 int WriteFile(const std::wstring& filename, const char* data, int size) {
    396   return WriteFile(FilePath::FromWStringHack(filename), data, size);
    397 }
    398 #endif  // OS_WIN
    399 
    400 ///////////////////////////////////////////////
    401 // FileEnumerator
    402 //
    403 // Note: the main logic is in file_util_<platform>.cc
    404 
    405 bool FileEnumerator::ShouldSkip(const FilePath& path) {
    406   FilePath::StringType basename = path.BaseName().value();
    407   return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
    408 }
    409 
    410 }  // namespace
    411