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