// Copyright (c) 2006-2009 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);
}
void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
FilePath::StringType clean_extension;
// If the new extension is "" or ".", then we will just remove the current
// extension.
if (!extension.empty() &&
extension != FilePath::StringType(&kExtensionSeparator, 1)) {
if (extension[0] != kExtensionSeparator)
clean_extension.append(&kExtensionSeparator, 1);
clean_extension.append(extension);
}
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));
// Erase the current extension, if any.
if ((last_dot > last_separator ||
last_separator == FilePath::StringType::npos) &&
last_dot != FilePath::StringType::npos)
value.erase(last_dot);
value.append(clean_extension);
}
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) {
contents->append(buf, len);
}
CloseFile(file);
return true;
}
FILE* CreateAndOpenTemporaryFile(FilePath* path) {
FilePath directory;
if (!GetTempDir(&directory))
return false;
return CreateAndOpenTemporaryFileInDir(directory, path);
}
bool GetFileSize(const FilePath& file_path, int64* file_size) {
FileInfo info;
if (!GetFileInfo(file_path, &info))
return false;
*file_size = info.size;
return true;
}
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;
}
///////////////////////////////////////////////
// MemoryMappedFile
MemoryMappedFile::~MemoryMappedFile() {
CloseHandles();
}
bool MemoryMappedFile::Initialize(base::PlatformFile file) {
if (IsValid())
return false;
file_ = file;
if (!MapFileToMemoryInternal()) {
CloseHandles();
return false;
}
return true;
}
bool MemoryMappedFile::Initialize(const FilePath& file_name) {
if (IsValid())
return false;
if (!MapFileToMemory(file_name)) {
CloseHandles();
return false;
}
return true;
}
bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
file_ = base::CreatePlatformFile(file_name,
base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
NULL);
if (file_ == base::kInvalidPlatformFileValue) {
LOG(ERROR) << "Couldn't open " << file_name.value();
return false;
}
return MapFileToMemoryInternal();
}
bool MemoryMappedFile::IsValid() {
return data_ != NULL;
}
// Deprecated functions ----------------------------------------------------
bool ReadFileToString(const std::wstring& path, std::string* contents) {
return ReadFileToString(FilePath::FromWStringHack(path), contents);
}
bool AbsolutePath(std::wstring* path_str) {
FilePath path(FilePath::FromWStringHack(*path_str));
if (!AbsolutePath(&path))
return false;
*path_str = path.ToWStringHack();
return true;
}
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(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);
}
bool EndsWithSeparator(std::wstring* path) {
return EndsWithSeparator(FilePath::FromWStringHack(*path));
}
bool EndsWithSeparator(const std::wstring& path) {
return EndsWithSeparator(FilePath::FromWStringHack(path));
}
bool GetCurrentDirectory(std::wstring* path_str) {
FilePath path;
if (!GetCurrentDirectory(&path))
return false;
*path_str = path.ToWStringHack();
return true;
}
std::wstring GetFileExtensionFromPath(const std::wstring& path) {
FilePath::StringType extension =
GetFileExtensionFromPath(FilePath::FromWStringHack(path));
#if defined(OS_WIN)
return extension;
#elif defined(OS_POSIX)
return UTF8ToWide(extension);
#endif
}
bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
return GetFileInfo(FilePath::FromWStringHack(file_path), results);
}
std::wstring GetFilenameFromPath(const std::wstring& path) {
if (path.empty() || EndsWithSeparator(path))
return std::wstring();
return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
}
bool GetFileSize(const std::wstring& file_path, int64* file_size) {
return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
}
bool GetTempDir(std::wstring* path_str) {
FilePath path;
if (!GetTempDir(&path))
return false;
*path_str = path.ToWStringHack();
return true;
}
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);
}
void UpOneDirectory(std::wstring* dir) {
FilePath path = FilePath::FromWStringHack(*dir);
FilePath directory = path.DirName();
// If there is no separator, we will get back kCurrentDirectory.
// In this case don't change |dir|.
if (directory.value() != FilePath::kCurrentDirectory)
*dir = directory.ToWStringHack();
}
void UpOneDirectoryOrEmpty(std::wstring* dir) {
FilePath path = FilePath::FromWStringHack(*dir);
FilePath directory = path.DirName();
// If there is no separator, we will get back kCurrentDirectory.
// In this case, clear dir.
if (directory == path || directory.value() == FilePath::kCurrentDirectory)
dir->clear();
else
*dir = directory.ToWStringHack();
}
int WriteFile(const std::wstring& filename, const char* data, int size) {
return WriteFile(FilePath::FromWStringHack(filename), data, size);
}
///////////////////////////////////////////////
// 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_));
}
bool FileEnumerator::IsDot(const FilePath& path) {
return FILE_PATH_LITERAL(".") == path.BaseName().value();
}
bool FileEnumerator::IsDotDot(const FilePath& path) {
return FILE_PATH_LITERAL("..") == path.BaseName().value();
}
} // namespace