blob: f6beb576b5d2d21db17e20ac3274d31fa2acdc78 [file] [log] [blame]
[email protected]b2e97292008-09-02 18:20:341// Copyright (c) 2006-2008 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#include <fcntl.h>
8#include <fnmatch.h>
9#include <fts.h>
10#include <libgen.h>
11#include <sys/errno.h>
12#include <sys/stat.h>
13#include <time.h>
14
15#include <fstream>
16
17#include "base/basictypes.h"
18#include "base/logging.h"
19#include "base/string_util.h"
20
21namespace file_util {
22
23std::wstring GetDirectoryFromPath(const std::wstring& path) {
24 if (EndsWithSeparator(path)) {
25 std::wstring dir = path;
26 TrimTrailingSeparator(&dir);
27 return dir;
28 } else {
29 char full_path[PATH_MAX];
30 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
31 return UTF8ToWide(dirname(full_path));
32 }
33}
34
35bool AbsolutePath(std::wstring* path) {
36 char full_path[PATH_MAX];
37 if (realpath(WideToUTF8(*path).c_str(), full_path) == NULL)
38 return false;
39 *path = UTF8ToWide(full_path);
40 return true;
41}
42
43// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
44// which works both with and without the recursive flag. I'm not sure we need
45// that functionality. If not, remove from file_util_win.cc, otherwise add it
46// here.
47bool Delete(const std::wstring& path, bool recursive) {
48 const char* utf8_path = WideToUTF8(path).c_str();
49 struct stat64 file_info;
50 int test = stat64(utf8_path, &file_info);
51 if (test != 0) {
52 // The Windows version defines this condition as success.
53 bool ret = (errno == ENOENT || errno == ENOTDIR);
54 return ret;
55 }
56 if (!S_ISDIR(file_info.st_mode))
57 return (unlink(utf8_path) == 0);
58 if (!recursive)
59 return (rmdir(utf8_path) == 0);
60
61 bool success = true;
62 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT;
63 char top_dir[PATH_MAX];
64 base::strlcpy(top_dir, utf8_path, sizeof(top_dir));
65 char* dir_list[2] = { top_dir, NULL };
66 FTS* fts = fts_open(dir_list, ftsflags, NULL);
67 if (fts) {
68 FTSENT* fts_ent = fts_read(fts);
69 while (success && fts_ent != NULL) {
70 switch (fts_ent->fts_info) {
71 case FTS_DNR:
72 case FTS_ERR:
73 // log error
74 success = false;
75 continue;
76 break;
77 case FTS_DP:
78 rmdir(fts_ent->fts_accpath);
79 break;
80 case FTS_D:
81 break;
82 case FTS_NSOK:
83 case FTS_F:
84 case FTS_SL:
85 case FTS_SLNONE:
86 unlink(fts_ent->fts_accpath);
87 break;
88 default:
89 DCHECK(false);
90 break;
91 }
92 fts_ent = fts_read(fts);
93 }
94 fts_close(fts);
95 }
96 return success;
97}
98
99bool Move(const std::wstring& from_path, const std::wstring& to_path) {
100 return (rename(WideToUTF8(from_path).c_str(),
101 WideToUTF8(to_path).c_str()) == 0);
102}
103
104bool CopyTree(const std::wstring& from_path, const std::wstring& to_path) {
105 // TODO(erikkay): implement
106 return false;
107}
108
109bool PathExists(const std::wstring& path) {
110 struct stat64 file_info;
111 return (stat64(WideToUTF8(path).c_str(), &file_info) == 0);
112}
113
[email protected]806b9c62008-09-11 16:09:11114bool DirectoryExists(const std::wstring& path) {
115 struct stat64 file_info;
116 if (stat64(WideToUTF8(path).c_str(), &file_info) == 0)
117 return S_ISDIR(file_info.st_mode);
118 return false;
119}
120
[email protected]b2e97292008-09-02 18:20:34121// TODO(erikkay): implement
122#if 0
123bool GetFileCreationLocalTimeFromHandle(int fd,
124 LPSYSTEMTIME creation_time) {
125 if (!file_handle)
126 return false;
127
128 FILETIME utc_filetime;
129 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
130 return false;
131
132 FILETIME local_filetime;
133 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
134 return false;
135
136 return !!FileTimeToSystemTime(&local_filetime, creation_time);
137}
138
139bool GetFileCreationLocalTime(const std::string& filename,
140 LPSYSTEMTIME creation_time) {
141 ScopedHandle file_handle(
142 CreateFile(filename.c_str(), GENERIC_READ,
143 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
144 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
145 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
146}
147#endif
148
149bool CreateTemporaryFileName(std::wstring* temp_file) {
150 std::wstring tmpdir;
151 if (!GetTempDir(&tmpdir))
152 return false;
153 tmpdir.append(L"com.google.chrome.XXXXXX");
154 // this should be OK since mktemp just replaces characters in place
155 char* buffer = const_cast<char*>(WideToUTF8(tmpdir).c_str());
156 *temp_file = UTF8ToWide(mktemp(buffer));
157 int fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666);
158 if (fd < 0)
159 return false;
160 close(fd);
161 return true;
162}
163
164bool CreateNewTempDirectory(const std::wstring& prefix,
165 std::wstring* new_temp_path) {
166 std::wstring tmpdir;
167 if (!GetTempDir(&tmpdir))
168 return false;
169 tmpdir.append(L"/com.google.chrome.XXXXXX");
170 // this should be OK since mkdtemp just replaces characters in place
171 char* buffer = const_cast<char*>(WideToUTF8(tmpdir).c_str());
172 char* dtemp = mkdtemp(buffer);
173 if (!dtemp)
174 return false;
175 *new_temp_path = UTF8ToWide(dtemp);
176 return true;
177}
178
179bool CreateDirectory(const std::wstring& full_path) {
180 std::vector<std::wstring> components;
181 PathComponents(full_path, &components);
182 std::wstring path;
183 std::vector<std::wstring>::iterator i = components.begin();
184 for (; i != components.end(); ++i) {
185 if (path.length() == 0)
186 path = *i;
187 else
188 AppendToPath(&path, *i);
[email protected]806b9c62008-09-11 16:09:11189 if (!DirectoryExists(path)) {
[email protected]b2e97292008-09-02 18:20:34190 if (mkdir(WideToUTF8(path).c_str(), 0777) != 0)
191 return false;
192 }
193 }
194 return true;
195}
196
197bool GetFileSize(const std::wstring& file_path, int64* file_size) {
198 struct stat64 file_info;
199 if (stat64(WideToUTF8(file_path).c_str(), &file_info) != 0)
200 return false;
201 *file_size = file_info.st_size;
202 return true;
203}
204
205int ReadFile(const std::wstring& filename, char* data, int size) {
206 int fd = open(WideToUTF8(filename).c_str(), O_RDONLY);
207 if (fd < 0)
208 return -1;
209
210 int ret_value = read(fd, data, size);
211 close(fd);
212 return ret_value;
213}
214
215int WriteFile(const std::wstring& filename, const char* data, int size) {
216 int fd = open(WideToUTF8(filename).c_str(), O_WRONLY | O_CREAT | O_TRUNC,
217 0666);
218 if (fd < 0)
219 return -1;
220
221 int ret_value = write(fd, data, size);
222 close(fd);
223 return ret_value;
224}
225
226// Gets the current working directory for the process.
227bool GetCurrentDirectory(std::wstring* dir) {
228 char system_buffer[PATH_MAX] = "";
229 getcwd(system_buffer, sizeof(system_buffer));
230 *dir = UTF8ToWide(system_buffer);
231 return true;
232}
233
234// Sets the current working directory for the process.
235bool SetCurrentDirectory(const std::wstring& current_directory) {
236 int ret = chdir(WideToUTF8(current_directory).c_str());
237 return (ret == 0);
238}
239
240FileEnumerator::FileEnumerator(const std::wstring& root_path,
241 bool recursive,
242 FileEnumerator::FILE_TYPE file_type)
243 : recursive_(recursive),
244 file_type_(file_type),
245 is_in_find_op_(false),
246 fts_(NULL) {
247 pending_paths_.push(root_path);
248}
249
250FileEnumerator::FileEnumerator(const std::wstring& root_path,
251 bool recursive,
252 FileEnumerator::FILE_TYPE file_type,
253 const std::wstring& pattern)
254 : recursive_(recursive),
255 file_type_(file_type),
256 pattern_(root_path),
257 is_in_find_op_(false),
258 fts_(NULL) {
259 // The Windows version of this code only matches against items in the top-most
260 // directory, and we're comparing fnmatch against full paths, so this is the
261 // easiest way to get the right pattern.
262 AppendToPath(&pattern_, pattern);
263 pending_paths_.push(root_path);
264}
265
266FileEnumerator::~FileEnumerator() {
267 if (fts_)
268 fts_close(fts_);
269}
270
271// As it stands, this method calls itself recursively when the next item of
272// the fts enumeration doesn't match (type, pattern, etc.). In the case of
273// large directories with many files this can be quite deep.
274// TODO(erikkay) - get rid of this recursive pattern
275std::wstring FileEnumerator::Next() {
276 if (!is_in_find_op_) {
277 if (pending_paths_.empty())
278 return std::wstring();
279
280 // The last find FindFirstFile operation is done, prepare a new one.
281 root_path_ = pending_paths_.top();
282 TrimTrailingSeparator(&root_path_);
283 pending_paths_.pop();
284
285 // Start a new find operation.
286 int ftsflags = FTS_LOGICAL;
287 char top_dir[PATH_MAX];
288 base::strlcpy(top_dir, WideToUTF8(root_path_).c_str(), sizeof(top_dir));
289 char* dir_list[2] = { top_dir, NULL };
290 fts_ = fts_open(dir_list, ftsflags, NULL);
291 if (!fts_)
292 return Next();
293 is_in_find_op_ = true;
294 }
295
296 FTSENT* fts_ent = fts_read(fts_);
297 if (fts_ent == NULL) {
298 fts_close(fts_);
299 fts_ = NULL;
300 is_in_find_op_ = false;
301 return Next();
302 }
303
304 // Level 0 is the top, which is always skipped.
305 if (fts_ent->fts_level == 0)
306 return Next();
307
308 // Patterns are only matched on the items in the top-most directory.
309 // (see Windows implementation)
310 if (fts_ent->fts_level == 1 && pattern_.length() > 0) {
311 const char* utf8_pattern = WideToUTF8(pattern_).c_str();
312 if (fnmatch(utf8_pattern, fts_ent->fts_path, 0) != 0) {
313 if (fts_ent->fts_info == FTS_D)
314 fts_set(fts_, fts_ent, FTS_SKIP);
315 return Next();
316 }
317 }
318
319 std::wstring cur_file(UTF8ToWide(fts_ent->fts_path));
320 if (fts_ent->fts_info == FTS_D) {
321 // If not recursive, then prune children.
322 if (!recursive_)
323 fts_set(fts_, fts_ent, FTS_SKIP);
324 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
325 } else if (fts_ent->fts_info == FTS_F) {
326 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
327 }
328 // TODO(erikkay) - verify that the other fts_info types aren't interesting
329 return Next();
330}
331
332
333} // namespace file_util