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