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