#include "File.h" #include "FileFilter.h" #include #include #include #include #if defined(_WIN32) #define PLATFORM_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #else #define PLATFORM_POSIX #include #include #include #include #include #include #endif #if defined(PLATFORM_WINDOWS) const char File::pathSeparator = '\\'; #else const char File::pathSeparator = '/'; #endif namespace { std::vector splitPath(const std::string& path, char sep) { std::vector parts; std::string token; for (char c : path) { if (c == sep) { parts.push_back(token); token.clear(); } else { token += c; } } parts.push_back(token); return parts; } } File::File(const std::string& pathname) : m_path(pathname) {} File::File(const File& parent, const std::string& child) : m_path(parent.getPath() + pathSeparator + child) {} File::File(const std::string& parent, const std::string& child) : m_path(parent + pathSeparator + child) {} bool File::exists() const { #if defined(PLATFORM_WINDOWS) DWORD attr = GetFileAttributesA(m_path.c_str()); return attr != INVALID_FILE_ATTRIBUTES; #else struct stat st; return ::stat(m_path.c_str(), &st) == 0; #endif } bool File::isFile() const { #if defined(PLATFORM_WINDOWS) DWORD attr = GetFileAttributesA(m_path.c_str()); if (attr == INVALID_FILE_ATTRIBUTES) return false; return !(attr & FILE_ATTRIBUTE_DIRECTORY); #else struct stat st; if (::stat(m_path.c_str(), &st) != 0) return false; return S_ISREG(st.st_mode); #endif } bool File::isDirectory() const { #if defined(PLATFORM_WINDOWS) DWORD attr = GetFileAttributesA(m_path.c_str()); if (attr == INVALID_FILE_ATTRIBUTES) return false; return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; #else struct stat st; if (::stat(m_path.c_str(), &st) != 0) return false; return S_ISDIR(st.st_mode); #endif } bool File::_delete() { #if defined(PLATFORM_WINDOWS) if (isDirectory()) return RemoveDirectoryA(m_path.c_str()) != 0; return DeleteFileA(m_path.c_str()) != 0; #else return ::remove(m_path.c_str()) == 0; #endif } bool File::mkdir() const { #if defined(PLATFORM_WINDOWS) return CreateDirectoryA(m_path.c_str(), nullptr) != 0; #else return ::mkdir(m_path.c_str(), 0755) == 0; #endif } bool File::mkdirs() const { if (exists()) return isDirectory(); std::vector parts = splitPath(m_path, pathSeparator); std::string current; for (size_t i = 0; i < parts.size(); ++i) { if (i == 0) { current = parts[i]; // may be "" for a POSIX absolute path } else { current += pathSeparator; current += parts[i]; } if (current.empty() || current == ".") continue; #if defined(PLATFORM_WINDOWS) // Skip drive roots like "C:" if (current.size() == 2 && current[1] == ':') continue; DWORD attr = GetFileAttributesA(current.c_str()); if (attr == INVALID_FILE_ATTRIBUTES) { if (!CreateDirectoryA(current.c_str(), nullptr)) return false; } else if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { return false; // A file is blocking the path } #else struct stat st; if (::stat(current.c_str(), &st) != 0) { if (::mkdir(current.c_str(), 0755) != 0 && errno != EEXIST) return false; } else if (!S_ISDIR(st.st_mode)) { return false; // A file is blocking the path } #endif } assert(exists()); return true; } bool File::renameTo(const File& dest) { #if defined(PLATFORM_WINDOWS) return MoveFileA(m_path.c_str(), dest.getPath().c_str()) != 0; #else return ::rename(m_path.c_str(), dest.getPath().c_str()) == 0; #endif } int64_t File::length() const { #if defined(PLATFORM_WINDOWS) WIN32_FILE_ATTRIBUTE_DATA info; if (!GetFileAttributesExA(m_path.c_str(), GetFileExInfoStandard, &info)) return 0; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return 0; LARGE_INTEGER li; li.HighPart = static_cast(info.nFileSizeHigh); li.LowPart = info.nFileSizeLow; return static_cast(li.QuadPart); #else struct stat st; if (::stat(m_path.c_str(), &st) != 0) return 0; if (S_ISDIR(st.st_mode)) return 0; return static_cast(st.st_size); #endif } int64_t File::lastModified() const { #if defined(PLATFORM_WINDOWS) WIN32_FILE_ATTRIBUTE_DATA info; if (!GetFileAttributesExA(m_path.c_str(), GetFileExInfoStandard, &info)) return 0; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return 0; LARGE_INTEGER li; li.HighPart = static_cast(info.ftLastWriteTime.dwHighDateTime); li.LowPart = info.ftLastWriteTime.dwLowDateTime; const int64_t EPOCH_DIFF = 116444736000000000LL; return (li.QuadPart - EPOCH_DIFF) / 10000; #else struct stat st; if (::stat(m_path.c_str(), &st) != 0) return 0; if (S_ISDIR(st.st_mode)) return 0; return static_cast(st.st_mtime) * 1000; #endif } std::string File::getPath() const { return m_path; } std::string File::getName() const { size_t sep = m_path.find_last_of(pathSeparator); if (sep == std::string::npos) return m_path; return m_path.substr(sep + 1); } std::string File::getParentPath() const { size_t sep = m_path.find_last_of(pathSeparator); if (sep == std::string::npos) return ""; if (sep == 0) return std::string(1, pathSeparator); return m_path.substr(0, sep); } std::unique_ptr File::listFiles() const { auto output = std::unique_ptr(new FileList()); if (!isDirectory()) return output; #if defined(PLATFORM_WINDOWS) std::string pattern = m_path + pathSeparator + "*"; WIN32_FIND_DATAA wfd; HANDLE hFind = FindFirstFileA(pattern.c_str(), &wfd); if (hFind != INVALID_HANDLE_VALUE) { do { std::string name = wfd.cFileName; if (name == "." || name == "..") continue; output->push_back(std::unique_ptr(new File(*this, name))); } while (FindNextFileA(hFind, &wfd)); FindClose(hFind); } #else DIR* dir = ::opendir(m_path.c_str()); if (dir) { struct dirent* entry; while ((entry = ::readdir(dir)) != nullptr) { std::string name = entry->d_name; if (name == "." || name == "..") continue; output->push_back(std::unique_ptr(new File(*this, name))); } ::closedir(dir); } #endif return output; } std::unique_ptr File::listFiles(FileFilter* filter) const { if (!isDirectory()) return nullptr; auto output = std::unique_ptr(new FileList()); #if defined(PLATFORM_WINDOWS) std::string pattern = m_path + pathSeparator + "*"; WIN32_FIND_DATAA wfd; HANDLE hFind = FindFirstFileA(pattern.c_str(), &wfd); if (hFind != INVALID_HANDLE_VALUE) { do { std::string name = wfd.cFileName; if (name == "." || name == "..") continue; auto candidate = std::unique_ptr(new File(*this, name)); if (filter && filter->accept(candidate.get())) output->push_back(std::move(candidate)); // unique_ptr destructs candidate automatically if not accepted } while (FindNextFileA(hFind, &wfd)); FindClose(hFind); } #else DIR* dir = ::opendir(m_path.c_str()); if (dir) { struct dirent* entry; while ((entry = ::readdir(dir)) != nullptr) { std::string name = entry->d_name; if (name == "." || name == "..") continue; auto candidate = std::unique_ptr(new File(*this, name)); if (filter && filter->accept(candidate.get())) output->push_back(std::move(candidate)); // unique_ptr destructs candidate automatically if not accepted } ::closedir(dir); } #endif return output; } bool File::eq_test(const File& x, const File& y) { #if defined(PLATFORM_WINDOWS) // Windows paths are case-insensitive if (x.m_path.size() != y.m_path.size()) return false; for (size_t i = 0; i < x.m_path.size(); ++i) { if (::tolower((unsigned char)x.m_path[i]) != ::tolower((unsigned char)y.m_path[i])) return false; } return true; #else return x.m_path == y.m_path; #endif } int File::hash_fnct(const File& k) { // djb2 — simple, well-distributed for path strings unsigned long hash = 5381; for (unsigned char c : k.m_path) hash = ((hash << 5) + hash) + c; // hash * 33 + c return static_cast(hash); }