// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_util.h"
#if defined(OS_WIN)
#include <io.h>
#endif
#include <stdio.h>
#include <fstream>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
namespace {
const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
} // namespace
namespace file_util {
bool EndsWithSeparator(const FilePath& path) {
FilePath::StringType value = path.value();
if (value.empty())
return false;
return FilePath::IsSeparator(value[value.size() - 1]);
}
bool EnsureEndsWithSeparator(FilePath* path) {
if (!DirectoryExists(*path))
return false;
if (EndsWithSeparator(*path))
return true;
FilePath::StringType& path_str =
const_cast<FilePath::StringType&>(path->value());
path_str.append(&FilePath::kSeparators[0], 1);
return true;
}
FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
FilePath::StringType file_name = path.BaseName().value();
const FilePath::StringType::size_type last_dot =
file_name.rfind(kExtensionSeparator);
return FilePath::StringType(last_dot == FilePath::StringType::npos ?
FILE_PATH_LITERAL("") :
file_name, last_dot+1);
}
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
FilePath::StringType& value =
const_cast<FilePath::StringType&>(path->value());
const FilePath::StringType::size_type last_dot =
value.rfind(kExtensionSeparator);
const FilePath::StringType::size_type last_separator =
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
if (last_dot == FilePath::StringType::npos ||
(last_separator != std::wstring::npos && last_dot < last_separator)) {
// The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
// We should just append the suffix to the entire path.
value.append(suffix);
return;
}
value.insert(last_dot, suffix);
}
bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
// We open the file in binary format even if they are text files because
// we are just comparing that bytes are exactly same in both files and not
// doing anything smart with text formatting.
std::ifstream file1(filename1.value().c_str(),
std::ios::in | std::ios::binary);
std::ifstream file2(filename2.value().c_str(),
std::ios::in | std::ios::binary);
// Even if both files aren't openable (and thus, in some sense, "equal"),
// any unusable file yields a result of "false".
if (!file1.is_open() || !file2.is_open())
return false;
const int BUFFER_SIZE = 2056;
char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
do {
file1.read(buffer1, BUFFER_SIZE);
file2.read(buffer2, BUFFER_SIZE);
if ((file1.eof() != file2.eof()) ||
(file1.gcount() != file2.gcount()) ||
(memcmp(buffer1, buffer2, file1.gcount()))) {
file1.close();
file2.close();
return false;
}
} while (!file1.eof() || !file2.eof());
file1.close();
file2.close();
return true;
}
bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
std::ifstream file1(filename1.value().c_str(), std::ios::in);
std::ifstream file2(filename2.value().c_str(), std::ios::in);
// Even if both files aren't openable (and thus, in some sense, "equal"),
// any unusable file yields a result of "false".
if (!file1.is_open() || !file2.is_open())
return false;
do {
std::string line1, line2;
getline(file1, line1);
getline(file2, line2);
// Check for mismatched EOF states, or any error state.
if ((file1.eof() != file2.eof()) ||
file1.bad() || file2.bad()) {
return false;
}
// Trim all '\r' and '\n' characters from the end of the line.
std::string::size_type end1 = line1.find_last_not_of("\r\n");
if (end1 == std::string::npos)
line1.clear();
else if (end1 + 1 < line1.length())
line1.erase(end1 + 1);
std::string::size_type end2 = line2.find_last_not_of("\r\n");
if (end2 == std::string::npos)
line2.clear();
else if (end2 + 1 < line2.length())
line2.erase(end2 + 1);
if (line1 != line2)
return false;
} while (!file1.eof() || !file2.eof());
return true;
}
bool ReadFileToString(const FilePath& path, std::string* contents) {
FILE* file = OpenFile(path, "rb");
if (!file) {
return false;
}
char buf[1 << 16];
size_t len;
while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
if (contents)
contents->append(buf, len);
}
CloseFile(file);
return true;
}
bool IsDirectoryEmpty(const FilePath& dir_path) {
FileEnumerator files(dir_path, false,
static_cast<FileEnumerator::FILE_TYPE>(
FileEnumerator::FILES | FileEnumerator::DIRECTORIES));
if (files.Next().value().empty())
return true;
return false;
}
FILE* CreateAndOpenTemporaryFile(FilePath* path) {
FilePath directory;
if (!GetTempDir(&directory))
return NULL;
return CreateAndOpenTemporaryFileInDir(directory, path);
}
bool GetFileSize(const FilePath& file_path, int64* file_size) {
base::PlatformFileInfo info;
if (!GetFileInfo(file_path, &info))
return false;
*file_size = info.size;
return true;
}
bool IsDot(const FilePath& path) {
return FILE_PATH_LITERAL(".") == path.BaseName().value();
}
bool IsDotDot(const FilePath& path) {
return FILE_PATH_LITERAL("..") == path.BaseName().value();
}
bool TouchFile(const FilePath& path,
const base::Time& last_accessed,
const base::Time& last_modified) {
base::PlatformFile file =
base::CreatePlatformFile(path,
base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_WRITE_ATTRIBUTES,
NULL, NULL);
if (file != base::kInvalidPlatformFileValue) {
bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
base::ClosePlatformFile(file);
return result;
}
return false;
}
bool SetLastModifiedTime(const FilePath& path,
const base::Time& last_modified) {
return TouchFile(path, last_modified, last_modified);
}
bool CloseFile(FILE* file) {
if (file == NULL)
return true;
return fclose(file) == 0;
}
bool TruncateFile(FILE* file) {
if (file == NULL)
return false;
long current_offset = ftell(file);
if (current_offset == -1)
return false;
#if defined(OS_WIN)
int fd = _fileno(file);
if (_chsize(fd, current_offset) != 0)
return false;
#else
int fd = fileno(file);
if (ftruncate(fd, current_offset) != 0)
return false;
#endif
return true;
}
bool ContainsPath(const FilePath &parent, const FilePath& child) {
FilePath abs_parent = FilePath(parent);
FilePath abs_child = FilePath(child);
if (!file_util::AbsolutePath(&abs_parent) ||
!file_util::AbsolutePath(&abs_child))
return false;
#if defined(OS_WIN)
// file_util::AbsolutePath() does not flatten case on Windows, so we must do
// a case-insensitive compare.
if (!StartsWith(abs_child.value(), abs_parent.value(), false))
#else
if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
#endif
return false;
// file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
// to check kSeparators[0].
if (abs_child.value().length() <= abs_parent.value().length() ||
abs_child.value()[abs_parent.value().length()] !=
FilePath::kSeparators[0])
return false;
return true;
}
int64 ComputeDirectorySize(const FilePath& root_path) {
int64 running_size = 0;
FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
for (FilePath current = file_iter.Next(); !current.empty();
current = file_iter.Next()) {
FileEnumerator::FindInfo info;
file_iter.GetFindInfo(&info);
#if defined(OS_WIN)
LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
running_size += li.QuadPart;
#else
running_size += info.stat.st_size;
#endif
}
return running_size;
}
int64 ComputeFilesSize(const FilePath& directory,
const FilePath::StringType& pattern) {
int64 running_size = 0;
FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
for (FilePath current = file_iter.Next(); !current.empty();
current = file_iter.Next()) {
FileEnumerator::FindInfo info;
file_iter.GetFindInfo(&info);
#if defined(OS_WIN)
LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
running_size += li.QuadPart;
#else
running_size += info.stat.st_size;
#endif
}
return running_size;
}
///////////////////////////////////////////////
// MemoryMappedFile
MemoryMappedFile::~MemoryMappedFile() {
CloseHandles();
}
bool MemoryMappedFile::Initialize(const FilePath& file_name) {
if (IsValid())
return false;
if (!MapFileToMemory(file_name)) {
CloseHandles();
return false;
}
return true;
}
bool MemoryMappedFile::Initialize(base::PlatformFile file) {
if (IsValid())
return false;
file_ = file;
if (!MapFileToMemoryInternal()) {
CloseHandles();
return false;
}
return true;
}
bool MemoryMappedFile::IsValid() const {
return data_ != NULL;
}
bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
file_ = base::CreatePlatformFile(
file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
NULL, NULL);
if (file_ == base::kInvalidPlatformFileValue) {
LOG(ERROR) << "Couldn't open " << file_name.value();
return false;
}
return MapFileToMemoryInternal();
}
// Deprecated functions ----------------------------------------------------
#if defined(OS_WIN)
void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
if (!path) {
NOTREACHED();
return; // Don't crash in this function in release builds.
}
if (!EndsWithSeparator(FilePath(*path)))
path->push_back(FilePath::kSeparators[0]);
path->append(new_ending);
}
bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
bool recursive) {
return CopyDirectory(FilePath::FromWStringHack(from_path),
FilePath::FromWStringHack(to_path),
recursive);
}
bool Delete(const std::wstring& path, bool recursive) {
return Delete(FilePath::FromWStringHack(path), recursive);
}
std::wstring GetFileExtensionFromPath(const std::wstring& path) {
FilePath::StringType extension =
GetFileExtensionFromPath(FilePath::FromWStringHack(path));
return extension;
}
FILE* OpenFile(const std::wstring& filename, const char* mode) {
return OpenFile(FilePath::FromWStringHack(filename), mode);
}
int ReadFile(const std::wstring& filename, char* data, int size) {
return ReadFile(FilePath::FromWStringHack(filename), data, size);
}
int WriteFile(const std::wstring& filename, const char* data, int size) {
return WriteFile(FilePath::FromWStringHack(filename), data, size);
}
#endif // OS_WIN
///////////////////////////////////////////////
// FileEnumerator
//
// Note: the main logic is in file_util_<platform>.cc
bool FileEnumerator::ShouldSkip(const FilePath& path) {
FilePath::StringType basename = path.BaseName().value();
return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
}
} // namespace