/* * Copyright (C) Nemirtingas * This file is part of System. * * System is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * System is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with System; if not, see * . */ #include "Filesystem.h" #include "Encoding.hpp" #include "System_internals.h" #if defined(SYSTEM_OS_WINDOWS) #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #define NOMINMAX #include #ifdef CreateDirectory #undef CreateDirectory #endif #ifdef DeleteFile #undef DeleteFile #endif #elif defined(SYSTEM_OS_LINUX) || defined(SYSTEM_OS_APPLE) #include #include // get iface broadcast #include // stats on a file (is directory, size, mtime) #include // to open directories #include // dlopen (like dll for linux) #include #include // PATH_MAX #include #else #error "unknown arch" #endif #include #include #include #include namespace System { namespace Filesystem { static void _CleanSlashes(std::string& str); std::string Filename(std::string const& path) { size_t pos = path.find_last_of("/\\"); if (pos != std::string::npos) return path.substr(pos+1); return path; } std::string Dirname(std::string const& path) { std::string r(path); _CleanSlashes(r); size_t pos = r.find_last_of("/\\"); if (pos == std::string::npos || (pos == 0 && r.length() == 1)) return std::string(); if (pos == 0) ++pos; return r.substr(0, pos); } std::string Join(StringView r, StringView l) { std::string result(r.to_string()); result += Separator; result += l.to_string(); _CleanSlashes(result); return result; } std::string CanonicalPath(std::string const& path) { if (IsAbsolute(path)) return CleanPath(path); return CleanPath(Join(GetCwd(),path)); } size_t FileSize(std::string const& path) { std::ifstream in_file(path, std::ios::in | std::ios::binary | std::ios::ate); if (in_file) { return in_file.tellg(); } return 0; } std::chrono::system_clock::time_point FileATime(std::string const& path) { struct stat file_stat = {}; if (stat(path.c_str(), &file_stat) != 0) return std::chrono::system_clock::time_point{}; return std::chrono::system_clock::from_time_t(file_stat.st_atime); } std::chrono::system_clock::time_point FileMTime(std::string const& path) { struct stat file_stat = {}; if (stat(path.c_str(), &file_stat) != 0) return std::chrono::system_clock::time_point{}; return std::chrono::system_clock::from_time_t(file_stat.st_mtime); } std::chrono::system_clock::time_point FileCTime(std::string const& path) { struct stat file_stat = {}; if (stat(path.c_str(), &file_stat) != 0) return std::chrono::system_clock::time_point{}; return std::chrono::system_clock::from_time_t(file_stat.st_ctime); } #ifdef SYSTEM_OS_WINDOWS static void _CleanSlashes(std::string& str) { size_t pos; std::replace(str.begin(), str.end(), '/', '\\'); while ((pos = str.find("\\\\")) != std::string::npos) str.replace(pos, 2, "\\"); pos = 0; while ((pos = str.find("\\.", pos)) != std::string::npos) { if (str[pos + 2] == '\\' || (pos + 2) >= str.length()) { str.replace(pos, 3, "\\"); } else { ++pos; } } } std::string GetCwd() { DWORD size = GetCurrentDirectoryW(0, nullptr); if (size == 0) return "."; std::wstring wdirectory; ++size; wdirectory.resize(size); wdirectory.resize(GetCurrentDirectoryW(size, &wdirectory[0])); wdirectory += L'\\'; return System::Encoding::WCharToUtf8(wdirectory); } bool IsAbsolute(std::string const& path) { return path.length() >= 2 && (((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) && path[1] == ':'); } std::string CleanPath(std::string const& path) { std::string cleaned_path(path); size_t pos; size_t size; _CleanSlashes(cleaned_path); pos = 0; while ((pos = cleaned_path.find("\\..", pos)) != std::string::npos ) { if (cleaned_path[pos + 3] == '\\' || (pos+3) >= cleaned_path.length()) { if (pos == 0) size = 3; else { size_t parent_pos = cleaned_path.rfind("\\", pos - 1); if (parent_pos == std::string::npos) { size = pos + 3; pos = 0; } else { size = 3 + pos - parent_pos; pos = parent_pos; } } cleaned_path.replace(pos, size, ""); } else { ++pos; } } return cleaned_path; } bool IsDir(std::string const& path) { std::wstring wpath(System::Encoding::Utf8ToWChar(path)); DWORD attrs = GetFileAttributesW(wpath.c_str()); return attrs != INVALID_FILE_ATTRIBUTES && attrs & FILE_ATTRIBUTE_DIRECTORY; } bool IsFile(std::string const& path) { std::wstring wpath(System::Encoding::Utf8ToWChar(path)); DWORD attrs = GetFileAttributesW(wpath.c_str()); return attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY); } bool Exists(std::string const& path) { std::wstring wpath(System::Encoding::Utf8ToWChar(path)); DWORD attrs = GetFileAttributesW(wpath.c_str()); return attrs != INVALID_FILE_ATTRIBUTES; } bool CreateDirectory(std::string const& directory, bool recursive) { size_t pos = 0; struct _stat sb; std::wstring sub_dir; std::wstring wdirectory(System::Encoding::Utf8ToWChar(directory)); if (wdirectory.empty()) return false; if (recursive) { pos = 3; do { pos = wdirectory.find_first_of(L"\\/", pos + 1); sub_dir = std::move(wdirectory.substr(0, pos)); if (_wstat(sub_dir.c_str(), &sb) == 0) { if (!(sb.st_mode & _S_IFDIR)) {// A subpath in the target is not a directory return false; } // Folder Exists } else if (CreateDirectoryW(wdirectory.substr(0, pos).c_str(), NULL) == FALSE && GetLastError() != ERROR_ALREADY_EXISTS) {// Failed to create directory return false; } } while (pos != std::string::npos); return true; } return (CreateDirectoryW(wdirectory.c_str(), NULL) != FALSE || GetLastError() == ERROR_ALREADY_EXISTS); } bool DeleteFile(std::string const& path) { std::wstring wpath(System::Encoding::Utf8ToWChar(path)); return DeleteFileW(wpath.c_str()) == TRUE || GetLastError() == ERROR_FILE_NOT_FOUND; } static std::vector ListFiles(std::wstring const& path, bool files_only, bool recursive) { std::vector files; WIN32_FIND_DATAW hfind_data; HANDLE hfind = INVALID_HANDLE_VALUE; std::wstring search_path = path; if (*path.rbegin() != L'\\') search_path += L'\\'; search_path += L'*'; // Start iterating over the files in the path directory. hfind = FindFirstFileW(search_path.c_str(), &hfind_data); if (hfind != INVALID_HANDLE_VALUE) { search_path.pop_back(); do // Managed to locate and create an handle to that folder. { if (wcscmp(L".", hfind_data.cFileName) == 0 || wcscmp(L"..", hfind_data.cFileName) == 0) continue; if (hfind_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (recursive) { std::wstring dir_name = hfind_data.cFileName; std::vector sub_files = std::move(ListFiles(search_path + dir_name, files_only, true)); std::transform(sub_files.begin(), sub_files.end(), std::back_inserter(files), [&dir_name](std::wstring& Filename) { return dir_name + L'\\' + Filename; }); } if (!files_only) { files.emplace_back(hfind_data.cFileName); } } else { files.emplace_back(hfind_data.cFileName); } } while (FindNextFileW(hfind, &hfind_data) == TRUE); FindClose(hfind); } return files; } std::vector ListFiles(std::string const& path, bool files_only, bool recursive) { std::vector files; std::wstring wpath(System::Encoding::Utf8ToWChar(path)); std::vector wfiles(std::move(ListFiles(wpath, files_only, recursive))); files.reserve(wfiles.size()); std::transform(wfiles.begin(), wfiles.end(), std::back_inserter(files), [](std::wstring const& wFilename) { return System::Encoding::WCharToUtf8(wFilename); }); return files; } #else static void _CleanSlashes(std::string& str) { size_t pos; std::replace(str.begin(), str.end(), '\\', '/'); while ((pos = str.find("//")) != std::string::npos) str.replace(pos, 2, "/"); pos = 0; while ((pos = str.find("/.", pos)) != std::string::npos) { if (str[pos + 2] == '/' || (pos + 2) >= str.length()) { str.replace(pos, 3, "/"); } else { ++pos; } } } std::string GetCwd() { char buff[4096]; std::string tmp(getcwd(buff, 4096) == nullptr ? "." : buff); tmp += '/'; return tmp; } bool IsAbsolute(std::string const& path) { return path[0] == '/'; } std::string CleanPath(std::string const& path) { std::string cleaned_path(path); size_t pos; size_t size; std::replace(cleaned_path.begin(), cleaned_path.end(), '\\', '/'); _CleanSlashes(cleaned_path); pos = 0; while ((pos = cleaned_path.find("/..", pos)) != std::string::npos) { if (cleaned_path[pos + 3] == '/' || (pos + 3) >= cleaned_path.length()) { if (pos == 0) size = 3; else { size_t parent_pos = cleaned_path.rfind("/", pos - 1); if (parent_pos == std::string::npos) { size = pos + 3; pos = 0; } else { size = 3 + pos - parent_pos; pos = parent_pos; } } cleaned_path.replace(pos, size, ""); } else { ++pos; } } return cleaned_path; } bool IsDir(std::string const& path) { struct stat sb; if (stat(path.c_str(), &sb) == 0) { return S_ISDIR(sb.st_mode); } return false; } bool IsFile(std::string const& path) { struct stat sb; if (stat(path.c_str(), &sb) == 0) { return S_ISREG(sb.st_mode); } return false; } bool Exists(std::string const& path) { struct stat sb; return stat(path.c_str(), &sb) == 0; } bool CreateDirectory(std::string const& directory, bool recursive) { size_t pos = 0; struct stat sb; std::string sub_dir; do { pos = directory.find("/", pos + 1); sub_dir = std::move(directory.substr(0, pos)); if (stat(sub_dir.c_str(), &sb) == 0) { if (!S_ISDIR(sb.st_mode)) {// A subpath in the target is not a directory return false; } // Folder Exists } else if (mkdir(sub_dir.c_str(), 0755) < 0 && errno != EEXIST) {// Failed to create directory (no permission?) return false; } } while (pos != std::string::npos); return true; } bool DeleteFile(std::string const& path) { return unlink(path.c_str()) == 0; } std::vector ListFiles(std::string const& path, bool files_only, bool recursive) { std::vector files; std::string search_path = path; if (*path.rbegin() != Separator) search_path += Separator; DIR* dir = opendir(search_path.c_str()); struct dirent* entry; if (dir == nullptr) return files; while ((entry = readdir(dir)) != nullptr) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; if (entry->d_type == DT_DIR) { if (recursive) { std::string dir_name = entry->d_name; std::vector sub_files = std::move(ListFiles(search_path + dir_name, true)); std::transform(sub_files.begin(), sub_files.end(), std::back_inserter(files), [&dir_name](std::string& Filename) { return dir_name + Separator + Filename; }); } if (!files_only) { files.emplace_back(entry->d_name); } } else if (entry->d_type == DT_REG) { files.emplace_back(entry->d_name); } } closedir(dir); return files; } #endif } }