23 #include <sys/types.h> 36 string DirName(
const string& path) {
38 static const char kPathSeparators[] =
"\\/";
40 static const char kPathSeparators[] =
"/";
42 static const char*
const kEnd = kPathSeparators +
sizeof(kPathSeparators) - 1;
44 string::size_type slash_pos = path.find_last_of(kPathSeparators);
45 if (slash_pos == string::npos)
47 while (slash_pos > 0 &&
48 std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd)
50 return path.substr(0, slash_pos);
53 int MakeDir(
const string& path) {
55 return _mkdir(path.c_str());
57 return mkdir(path.c_str(), 0777);
62 TimeStamp TimeStampFromFileTime(
const FILETIME& filetime) {
69 return (
TimeStamp)mtime - 12622770400LL * (1000000000LL / 100);
72 TimeStamp StatSingleFile(
const string& path,
string* err) {
73 WIN32_FILE_ATTRIBUTE_DATA attrs;
74 if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) {
75 DWORD win_err = GetLastError();
76 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
78 *err =
"GetFileAttributesEx(" + path +
"): " + GetLastErrorString();
81 return TimeStampFromFileTime(attrs.ftLastWriteTime);
84 bool IsWindows7OrLater() {
85 OSVERSIONINFOEX version_info =
86 {
sizeof(OSVERSIONINFOEX), 6, 1, 0, 0, {0}, 0, 0, 0, 0, 0};
87 DWORDLONG comparison = 0;
88 VER_SET_CONDITION(comparison, VER_MAJORVERSION, VER_GREATER_EQUAL);
89 VER_SET_CONDITION(comparison, VER_MINORVERSION, VER_GREATER_EQUAL);
90 return VerifyVersionInfo(
91 &version_info, VER_MAJORVERSION | VER_MINORVERSION, comparison);
94 bool StatAllFilesInDir(
const string& dir, map<string, TimeStamp>* stamps,
97 static bool can_use_basic_info = IsWindows7OrLater();
99 const FINDEX_INFO_LEVELS kFindExInfoBasic =
100 static_cast<FINDEX_INFO_LEVELS
>(1);
101 FINDEX_INFO_LEVELS level =
102 can_use_basic_info ? kFindExInfoBasic : FindExInfoStandard;
103 WIN32_FIND_DATAA ffd;
104 HANDLE find_handle = FindFirstFileExA((dir +
"\\*").c_str(), level, &ffd,
105 FindExSearchNameMatch, NULL, 0);
107 if (find_handle == INVALID_HANDLE_VALUE) {
108 DWORD win_err = GetLastError();
109 if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND)
111 *err =
"FindFirstFileExA(" + dir +
"): " + GetLastErrorString();
115 string lowername = ffd.cFileName;
116 if (lowername ==
"..") {
121 transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
122 stamps->insert(make_pair(lowername,
123 TimeStampFromFileTime(ffd.ftLastWriteTime)));
124 }
while (FindNextFileA(find_handle, &ffd));
125 FindClose(find_handle);
135 string dir = DirName(path);
141 Error(
"%s", err.c_str());
161 if (!path.empty() && path[0] !=
'\\' && path.size() > MAX_PATH) {
162 ostringstream err_stream;
163 err_stream <<
"Stat(" << path <<
"): Filename longer than " << MAX_PATH
165 *err = err_stream.str();
169 return StatSingleFile(path, err);
171 string dir = DirName(path);
172 string base(path.substr(dir.size() ? dir.size() + 1 : 0));
179 transform(dir.begin(), dir.end(), dir.begin(), ::tolower);
180 transform(base.begin(), base.end(), base.begin(), ::tolower);
182 Cache::iterator ci = cache_.find(dir);
183 if (ci == cache_.end()) {
184 ci = cache_.insert(make_pair(dir, DirCache())).first;
185 if (!StatAllFilesInDir(dir.empty() ?
"." : dir, &ci->second, err)) {
190 DirCache::iterator di = ci->second.find(base);
191 return di != ci->second.end() ? di->second : 0;
194 if (stat(path.c_str(), &st) < 0) {
195 if (errno == ENOENT || errno == ENOTDIR)
197 *err =
"stat(" + path +
"): " + strerror(errno);
203 if (st.st_mtime == 0)
205 #if defined(__APPLE__) && !defined(_POSIX_C_SOURCE) 206 return ((
int64_t)st.st_mtimespec.tv_sec * 1000000000LL +
207 st.st_mtimespec.tv_nsec);
208 #elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \ 209 defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)) || defined(__FreeBSD__)) 215 return (
int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec;
217 return (
int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n;
219 return (
int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;
225 FILE* fp = fopen(path.c_str(),
"w");
227 Error(
"WriteFile(%s): Unable to create file. %s",
228 path.c_str(), strerror(errno));
232 if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length()) {
233 Error(
"WriteFile(%s): Unable to write to the file. %s",
234 path.c_str(), strerror(errno));
239 if (fclose(fp) == EOF) {
240 Error(
"WriteFile(%s): Unable to close the file. %s",
241 path.c_str(), strerror(errno));
250 if (errno == EEXIST) {
253 Error(
"mkdir(%s): %s", path.c_str(), strerror(errno));
262 switch (::
ReadFile(path, contents, err)) {
270 if (
remove(path.c_str()) < 0) {
275 Error(
"remove(%s): %s", path.c_str(), strerror(errno));
virtual Status ReadFile(const string &path, string *contents, string *err)
Read and store in given string.
bool MakeDirs(const string &path)
Create all the parent directories for path; like mkdir -p basename path.
virtual bool WriteFile(const string &path, const string &contents)
Create a file, with the specified name and contents Returns true on success, false on failure...
signed long long int64_t
A 64-bit integer type.
virtual bool MakeDir(const string &path)
Create a directory, returning false on failure.
virtual TimeStamp Stat(const string &path, string *err) const
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
Status
Result of ReadFile.
virtual TimeStamp Stat(const string &path, string *err) const =0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
#define METRIC_RECORD(name)
The primary interface to metrics.
virtual int RemoveFile(const string &path)
Remove the file named path.
void AllowStatCache(bool allow)
Whether stat information can be cached. Only has an effect on Windows.
unsigned long long uint64_t
virtual bool MakeDir(const string &path)=0
Create a directory, returning false on failure.
void Error(const char *msg,...)
Log an error message.