|
1 // Copyright (c) 2012 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/files/file_enumerator.h" |
|
15 #include "base/files/file_path.h" |
|
16 #include "base/logging.h" |
|
17 #include "base/strings/string_piece.h" |
|
18 #include "base/strings/string_util.h" |
|
19 #include "base/strings/stringprintf.h" |
|
20 #include "base/strings/utf_string_conversions.h" |
|
21 |
|
22 namespace base { |
|
23 |
|
24 namespace { |
|
25 |
|
26 const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.'); |
|
27 |
|
28 // The maximum number of 'uniquified' files we will try to create. |
|
29 // This is used when the filename we're trying to download is already in use, |
|
30 // so we create a new unique filename by appending " (nnn)" before the |
|
31 // extension, where 1 <= nnn <= kMaxUniqueFiles. |
|
32 // Also used by code that cleans up said files. |
|
33 static const int kMaxUniqueFiles = 100; |
|
34 |
|
35 } // namespace |
|
36 |
|
37 bool g_bug108724_debug = false; |
|
38 |
|
39 int64 ComputeDirectorySize(const FilePath& root_path) { |
|
40 int64 running_size = 0; |
|
41 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); |
|
42 while (!file_iter.Next().empty()) |
|
43 running_size += file_iter.GetInfo().GetSize(); |
|
44 return running_size; |
|
45 } |
|
46 |
|
47 bool Move(const FilePath& from_path, const FilePath& to_path) { |
|
48 if (from_path.ReferencesParent() || to_path.ReferencesParent()) |
|
49 return false; |
|
50 return internal::MoveUnsafe(from_path, to_path); |
|
51 } |
|
52 |
|
53 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { |
|
54 if (from_path.ReferencesParent() || to_path.ReferencesParent()) |
|
55 return false; |
|
56 return internal::CopyFileUnsafe(from_path, to_path); |
|
57 } |
|
58 |
|
59 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { |
|
60 // We open the file in binary format even if they are text files because |
|
61 // we are just comparing that bytes are exactly same in both files and not |
|
62 // doing anything smart with text formatting. |
|
63 std::ifstream file1(filename1.value().c_str(), |
|
64 std::ios::in | std::ios::binary); |
|
65 std::ifstream file2(filename2.value().c_str(), |
|
66 std::ios::in | std::ios::binary); |
|
67 |
|
68 // Even if both files aren't openable (and thus, in some sense, "equal"), |
|
69 // any unusable file yields a result of "false". |
|
70 if (!file1.is_open() || !file2.is_open()) |
|
71 return false; |
|
72 |
|
73 const int BUFFER_SIZE = 2056; |
|
74 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; |
|
75 do { |
|
76 file1.read(buffer1, BUFFER_SIZE); |
|
77 file2.read(buffer2, BUFFER_SIZE); |
|
78 |
|
79 if ((file1.eof() != file2.eof()) || |
|
80 (file1.gcount() != file2.gcount()) || |
|
81 (memcmp(buffer1, buffer2, file1.gcount()))) { |
|
82 file1.close(); |
|
83 file2.close(); |
|
84 return false; |
|
85 } |
|
86 } while (!file1.eof() || !file2.eof()); |
|
87 |
|
88 file1.close(); |
|
89 file2.close(); |
|
90 return true; |
|
91 } |
|
92 |
|
93 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { |
|
94 std::ifstream file1(filename1.value().c_str(), std::ios::in); |
|
95 std::ifstream file2(filename2.value().c_str(), std::ios::in); |
|
96 |
|
97 // Even if both files aren't openable (and thus, in some sense, "equal"), |
|
98 // any unusable file yields a result of "false". |
|
99 if (!file1.is_open() || !file2.is_open()) |
|
100 return false; |
|
101 |
|
102 do { |
|
103 std::string line1, line2; |
|
104 getline(file1, line1); |
|
105 getline(file2, line2); |
|
106 |
|
107 // Check for mismatched EOF states, or any error state. |
|
108 if ((file1.eof() != file2.eof()) || |
|
109 file1.bad() || file2.bad()) { |
|
110 return false; |
|
111 } |
|
112 |
|
113 // Trim all '\r' and '\n' characters from the end of the line. |
|
114 std::string::size_type end1 = line1.find_last_not_of("\r\n"); |
|
115 if (end1 == std::string::npos) |
|
116 line1.clear(); |
|
117 else if (end1 + 1 < line1.length()) |
|
118 line1.erase(end1 + 1); |
|
119 |
|
120 std::string::size_type end2 = line2.find_last_not_of("\r\n"); |
|
121 if (end2 == std::string::npos) |
|
122 line2.clear(); |
|
123 else if (end2 + 1 < line2.length()) |
|
124 line2.erase(end2 + 1); |
|
125 |
|
126 if (line1 != line2) |
|
127 return false; |
|
128 } while (!file1.eof() || !file2.eof()); |
|
129 |
|
130 return true; |
|
131 } |
|
132 |
|
133 bool ReadFileToString(const FilePath& path, std::string* contents) { |
|
134 if (path.ReferencesParent()) |
|
135 return false; |
|
136 FILE* file = file_util::OpenFile(path, "rb"); |
|
137 if (!file) { |
|
138 return false; |
|
139 } |
|
140 |
|
141 char buf[1 << 16]; |
|
142 size_t len; |
|
143 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { |
|
144 if (contents) |
|
145 contents->append(buf, len); |
|
146 } |
|
147 file_util::CloseFile(file); |
|
148 |
|
149 return true; |
|
150 } |
|
151 |
|
152 } // namespace base |
|
153 |
|
154 // ----------------------------------------------------------------------------- |
|
155 |
|
156 namespace file_util { |
|
157 |
|
158 using base::FileEnumerator; |
|
159 using base::FilePath; |
|
160 using base::kExtensionSeparator; |
|
161 using base::kMaxUniqueFiles; |
|
162 |
|
163 bool IsDirectoryEmpty(const FilePath& dir_path) { |
|
164 FileEnumerator files(dir_path, false, |
|
165 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
|
166 if (files.Next().empty()) |
|
167 return true; |
|
168 return false; |
|
169 } |
|
170 |
|
171 FILE* CreateAndOpenTemporaryFile(FilePath* path) { |
|
172 FilePath directory; |
|
173 if (!GetTempDir(&directory)) |
|
174 return NULL; |
|
175 |
|
176 return CreateAndOpenTemporaryFileInDir(directory, path); |
|
177 } |
|
178 |
|
179 bool CreateDirectory(const base::FilePath& full_path) { |
|
180 return CreateDirectoryAndGetError(full_path, NULL); |
|
181 } |
|
182 |
|
183 bool GetFileSize(const FilePath& file_path, int64* file_size) { |
|
184 base::PlatformFileInfo info; |
|
185 if (!GetFileInfo(file_path, &info)) |
|
186 return false; |
|
187 *file_size = info.size; |
|
188 return true; |
|
189 } |
|
190 |
|
191 bool TouchFile(const FilePath& path, |
|
192 const base::Time& last_accessed, |
|
193 const base::Time& last_modified) { |
|
194 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES; |
|
195 |
|
196 #if defined(OS_WIN) |
|
197 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory. |
|
198 if (DirectoryExists(path)) |
|
199 flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS; |
|
200 #endif // OS_WIN |
|
201 |
|
202 const base::PlatformFile file = |
|
203 base::CreatePlatformFile(path, flags, NULL, NULL); |
|
204 if (file != base::kInvalidPlatformFileValue) { |
|
205 bool result = base::TouchPlatformFile(file, last_accessed, last_modified); |
|
206 base::ClosePlatformFile(file); |
|
207 return result; |
|
208 } |
|
209 |
|
210 return false; |
|
211 } |
|
212 |
|
213 bool SetLastModifiedTime(const FilePath& path, |
|
214 const base::Time& last_modified) { |
|
215 return TouchFile(path, last_modified, last_modified); |
|
216 } |
|
217 |
|
218 bool CloseFile(FILE* file) { |
|
219 if (file == NULL) |
|
220 return true; |
|
221 return fclose(file) == 0; |
|
222 } |
|
223 |
|
224 bool TruncateFile(FILE* file) { |
|
225 if (file == NULL) |
|
226 return false; |
|
227 long current_offset = ftell(file); |
|
228 if (current_offset == -1) |
|
229 return false; |
|
230 #if defined(OS_WIN) |
|
231 int fd = _fileno(file); |
|
232 if (_chsize(fd, current_offset) != 0) |
|
233 return false; |
|
234 #else |
|
235 int fd = fileno(file); |
|
236 if (ftruncate(fd, current_offset) != 0) |
|
237 return false; |
|
238 #endif |
|
239 return true; |
|
240 } |
|
241 |
|
242 int GetUniquePathNumber( |
|
243 const FilePath& path, |
|
244 const FilePath::StringType& suffix) { |
|
245 bool have_suffix = !suffix.empty(); |
|
246 if (!PathExists(path) && |
|
247 (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { |
|
248 return 0; |
|
249 } |
|
250 |
|
251 FilePath new_path; |
|
252 for (int count = 1; count <= kMaxUniqueFiles; ++count) { |
|
253 new_path = |
|
254 path.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", count)); |
|
255 if (!PathExists(new_path) && |
|
256 (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { |
|
257 return count; |
|
258 } |
|
259 } |
|
260 |
|
261 return -1; |
|
262 } |
|
263 |
|
264 } // namespace file_util |