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