From 4a4b685a0420b0ac7c026cd2370c23d54f469976 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 30 Oct 2020 15:02:02 -0400 Subject: common: Enable warnings as errors Cleans up common so that we can enable warnings as errors. --- src/common/file_util.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 16c3713e0..18fbfa25b 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, } bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { - const auto callback = [recursion](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { - std::string new_path = directory + DIR_SEP_CHR + virtual_name; + const auto callback = [recursion](u64*, const std::string& directory, + const std::string& virtual_name) { + const std::string new_path = directory + DIR_SEP_CHR + virtual_name; if (IsDirectory(new_path)) { - if (recursion == 0) + if (recursion == 0) { return false; + } return DeleteDirRecursively(new_path, recursion - 1); } return Delete(new_path); @@ -492,7 +493,8 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) return true; } -void CopyDir(const std::string& source_path, const std::string& dest_path) { +void CopyDir([[maybe_unused]] const std::string& source_path, + [[maybe_unused]] const std::string& dest_path) { #ifndef _WIN32 if (source_path == dest_path) { return; @@ -553,7 +555,7 @@ std::optional GetCurrentDir() { std::string strDir = dir; #endif free(dir); - return std::move(strDir); + return strDir; } bool SetCurrentDir(const std::string& directory) { @@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s void SplitFilename83(const std::string& filename, std::array& short_name, std::array& extension) { - const std::string forbidden_characters = ".\"/\\[]:;=, "; + static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, "; // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces. short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}}; extension = {{' ', ' ', ' ', '\0'}}; - std::string::size_type point = filename.rfind('.'); - if (point == filename.size() - 1) + auto point = filename.rfind('.'); + if (point == filename.size() - 1) { point = filename.rfind('.', point); + } // Get short name. int j = 0; for (char letter : filename.substr(0, point)) { - if (forbidden_characters.find(letter, 0) != std::string::npos) + if (forbidden_characters.find(letter, 0) != std::string::npos) { continue; + } if (j == 8) { // TODO(Link Mauve): also do that for filenames containing a space. // TODO(Link Mauve): handle multiple files having the same short name. @@ -794,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array& short_nam short_name[7] = '1'; break; } - short_name[j++] = toupper(letter); + short_name[j++] = static_cast(std::toupper(letter)); } // Get extension. if (point != std::string::npos) { j = 0; - for (char letter : filename.substr(point + 1, 3)) - extension[j++] = toupper(letter); + for (char letter : filename.substr(point + 1, 3)) { + extension[j++] = static_cast(std::toupper(letter)); + } } } -- cgit v1.2.3 From 0e54aa17e64a5c5b62d60a70414742be29eccff9 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 8 Dec 2020 18:36:49 -0500 Subject: file_util: Migrate Exists() and IsDirectory() over to std::filesystem Greatly simplifies our file-handling code for these functions. --- src/common/file_util.cpp | 61 +++++++----------------------------------------- 1 file changed, 8 insertions(+), 53 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 18fbfa25b..67fcef3e4 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -75,62 +76,16 @@ // The code still needs a ton of cleanup. // REMEMBER: strdup considered harmful! namespace Common::FS { +namespace fs = std::filesystem; -// Remove any ending forward slashes from directory paths -// Modifies argument. -static void StripTailDirSlashes(std::string& fname) { - if (fname.length() <= 1) { - return; - } - - std::size_t i = fname.length(); - while (i > 0 && fname[i - 1] == DIR_SEP_CHR) { - --i; - } - fname.resize(i); +bool Exists(const fs::path& path) { + std::error_code ec; + return fs::exists(path, ec); } -bool Exists(const std::string& filename) { - struct stat file_info; - - std::string copy(filename); - StripTailDirSlashes(copy); - -#ifdef _WIN32 - // Windows needs a slash to identify a driver root - if (copy.size() != 0 && copy.back() == ':') - copy += DIR_SEP_CHR; - - int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); -#else - int result = stat(copy.c_str(), &file_info); -#endif - - return (result == 0); -} - -bool IsDirectory(const std::string& filename) { - struct stat file_info; - - std::string copy(filename); - StripTailDirSlashes(copy); - -#ifdef _WIN32 - // Windows needs a slash to identify a driver root - if (copy.size() != 0 && copy.back() == ':') - copy += DIR_SEP_CHR; - - int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); -#else - int result = stat(copy.c_str(), &file_info); -#endif - - if (result < 0) { - LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); - return false; - } - - return S_ISDIR(file_info.st_mode); +bool IsDirectory(const fs::path& path) { + std::error_code ec; + return fs::is_directory(path, ec); } bool Delete(const std::string& filename) { -- cgit v1.2.3 From 20aad9e01af958b6c8bf7d18faebd498541651f5 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 8 Dec 2020 18:51:04 -0500 Subject: file_util: Migrate remaining file handling functions over to std::filesystem Converts creation and deletion functions over to std::filesystem, simplifying our file-handling code. Notably with this, CopyDir will now function on Windows. --- src/common/file_util.cpp | 378 ++++++++++------------------------------------- 1 file changed, 81 insertions(+), 297 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 67fcef3e4..d5f6ea2ee 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -68,13 +68,6 @@ #include #include -#ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) -#endif - -// This namespace has various generic functions related to files and paths. -// The code still needs a ton of cleanup. -// REMEMBER: strdup considered harmful! namespace Common::FS { namespace fs = std::filesystem; @@ -88,224 +81,89 @@ bool IsDirectory(const fs::path& path) { return fs::is_directory(path, ec); } -bool Delete(const std::string& filename) { - LOG_TRACE(Common_Filesystem, "file {}", filename); +bool Delete(const fs::path& path) { + LOG_TRACE(Common_Filesystem, "file {}", path.string()); // Return true because we care about the file no // being there, not the actual delete. - if (!Exists(filename)) { - LOG_DEBUG(Common_Filesystem, "{} does not exist", filename); + if (!Exists(path)) { + LOG_DEBUG(Common_Filesystem, "{} does not exist", path.string()); return true; } - // We can't delete a directory - if (IsDirectory(filename)) { - LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); - return false; - } + std::error_code ec; + return fs::remove(path, ec); +} -#ifdef _WIN32 - if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { - LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); - return false; - } -#else - if (unlink(filename.c_str()) == -1) { - LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); +bool CreateDir(const fs::path& path) { + LOG_TRACE(Common_Filesystem, "directory {}", path.string()); + + std::error_code ec; + const bool success = fs::create_directory(path, ec); + + if (!success) { + LOG_ERROR(Common_Filesystem, "Unable to create directory: {}", ec.message()); return false; } -#endif return true; } -bool CreateDir(const std::string& path) { - LOG_TRACE(Common_Filesystem, "directory {}", path); -#ifdef _WIN32 - if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) - return true; - DWORD error = GetLastError(); - if (error == ERROR_ALREADY_EXISTS) { - LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); - return true; - } - LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); - return false; -#else - if (mkdir(path.c_str(), 0755) == 0) - return true; +bool CreateFullPath(const fs::path& path) { + LOG_TRACE(Common_Filesystem, "path {}", path.string()); - int err = errno; + std::error_code ec; + const bool success = fs::create_directories(path, ec); - if (err == EEXIST) { - LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); - return true; + if (!success) { + LOG_ERROR(Common_Filesystem, "Unable to create full path: {}", ec.message()); + return false; } - LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); - return false; -#endif + return true; } -bool CreateFullPath(const std::string& fullPath) { - int panicCounter = 100; - LOG_TRACE(Common_Filesystem, "path {}", fullPath); - - if (Exists(fullPath)) { - LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); - return true; - } +bool Rename(const fs::path& src, const fs::path& dst) { + LOG_TRACE(Common_Filesystem, "{} --> {}", src.string(), dst.string()); - std::size_t position = 0; - while (true) { - // Find next sub path - position = fullPath.find(DIR_SEP_CHR, position); - - // we're done, yay! - if (position == fullPath.npos) - return true; - - // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") - std::string const subPath(fullPath.substr(0, position + 1)); - if (!IsDirectory(subPath) && !CreateDir(subPath)) { - LOG_ERROR(Common, "CreateFullPath: directory creation failed"); - return false; - } - - // A safety check - panicCounter--; - if (panicCounter <= 0) { - LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); - return false; - } - position++; - } -} - -bool DeleteDir(const std::string& filename) { - LOG_TRACE(Common_Filesystem, "directory {}", filename); + std::error_code ec; + fs::rename(src, dst, ec); - // check if a directory - if (!IsDirectory(filename)) { - LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); + if (ec) { + LOG_ERROR(Common_Filesystem, "Unable to rename file from {} to {}: {}", src.string(), + dst.string(), ec.message()); return false; } -#ifdef _WIN32 - if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str())) - return true; -#else - if (rmdir(filename.c_str()) == 0) - return true; -#endif - LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); - - return false; -} - -bool Rename(const std::string& srcFilename, const std::string& destFilename) { - LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); -#ifdef _WIN32 - if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), - Common::UTF8ToUTF16W(destFilename).c_str()) == 0) - return true; -#else - if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) - return true; -#endif - LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, - GetLastErrorMsg()); - return false; + return true; } -bool Copy(const std::string& srcFilename, const std::string& destFilename) { - LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); -#ifdef _WIN32 - if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), - Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) - return true; +bool Copy(const fs::path& src, const fs::path& dst) { + LOG_TRACE(Common_Filesystem, "{} --> {}", src.string(), dst.string()); - LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, - GetLastErrorMsg()); - return false; -#else - using CFilePointer = std::unique_ptr; - - // Open input file - CFilePointer input{fopen(srcFilename.c_str(), "rb"), std::fclose}; - if (!input) { - LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); - return false; - } + std::error_code ec; + const bool success = fs::copy_file(src, dst, fs::copy_options::overwrite_existing, ec); - // open output file - CFilePointer output{fopen(destFilename.c_str(), "wb"), std::fclose}; - if (!output) { - LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + if (!success) { + LOG_ERROR(Common_Filesystem, "Unable to copy file {} to {}: {}", src.string(), dst.string(), + ec.message()); return false; } - // copy loop - std::array buffer; - while (!feof(input.get())) { - // read input - std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); - if (rnum != buffer.size()) { - if (ferror(input.get()) != 0) { - LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", - srcFilename, destFilename, GetLastErrorMsg()); - return false; - } - } - - // write output - std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); - if (wnum != rnum) { - LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); - return false; - } - } - return true; -#endif } -u64 GetSize(const std::string& filename) { - if (!Exists(filename)) { - LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); - return 0; - } +u64 GetSize(const fs::path& path) { + std::error_code ec; + const auto size = fs::file_size(path, ec); - if (IsDirectory(filename)) { - LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); + if (ec) { + LOG_ERROR(Common_Filesystem, "Unable to retrieve file size ({}): {}", path.string(), + ec.message()); return 0; } - struct stat buf; -#ifdef _WIN32 - if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0) -#else - if (stat(filename.c_str(), &buf) == 0) -#endif - { - LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); - return buf.st_size; - } - - LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); - return 0; -} - -u64 GetSize(const int fd) { - struct stat buf; - if (fstat(fd, &buf) != 0) { - LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); - return 0; - } - return buf.st_size; + return size; } u64 GetSize(FILE* f) { @@ -393,132 +251,58 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, return true; } -u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, - unsigned int recursion) { - const auto callback = [recursion, &parent_entry](u64* num_entries_out, - const std::string& directory, - const std::string& virtual_name) -> bool { - FSTEntry entry; - entry.virtualName = virtual_name; - entry.physicalName = directory + DIR_SEP + virtual_name; - - if (IsDirectory(entry.physicalName)) { - entry.isDirectory = true; - // is a directory, lets go inside if we didn't recurse to often - if (recursion > 0) { - entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1); - *num_entries_out += entry.size; - } else { - entry.size = 0; - } - } else { // is a file - entry.isDirectory = false; - entry.size = GetSize(entry.physicalName); - } - (*num_entries_out)++; - - // Push into the tree - parent_entry.children.push_back(std::move(entry)); - return true; - }; - - u64 num_entries; - return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0; -} - -bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { - const auto callback = [recursion](u64*, const std::string& directory, - const std::string& virtual_name) { - const std::string new_path = directory + DIR_SEP_CHR + virtual_name; - - if (IsDirectory(new_path)) { - if (recursion == 0) { - return false; - } - return DeleteDirRecursively(new_path, recursion - 1); - } - return Delete(new_path); - }; +bool DeleteDirRecursively(const fs::path& path) { + std::error_code ec; + fs::remove_all(path, ec); - if (!ForeachDirectoryEntry(nullptr, directory, callback)) + if (ec) { + LOG_ERROR(Common_Filesystem, "Unable to completely delete directory {}: {}", path.string(), + ec.message()); return false; + } - // Delete the outermost directory - DeleteDir(directory); return true; } -void CopyDir([[maybe_unused]] const std::string& source_path, - [[maybe_unused]] const std::string& dest_path) { -#ifndef _WIN32 - if (source_path == dest_path) { - return; - } - if (!Exists(source_path)) { - return; - } - if (!Exists(dest_path)) { - CreateFullPath(dest_path); - } +void CopyDir(const fs::path& src, const fs::path& dst) { + constexpr auto copy_flags = fs::copy_options::skip_existing | fs::copy_options::recursive; + + std::error_code ec; + fs::copy(src, dst, copy_flags, ec); - DIR* dirp = opendir(source_path.c_str()); - if (!dirp) { + if (ec) { + LOG_ERROR(Common_Filesystem, "Error copying directory {} to {}: {}", src.string(), + dst.string(), ec.message()); return; } - while (struct dirent* result = readdir(dirp)) { - const std::string virtualName(result->d_name); - // check for "." and ".." - if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || - ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) { - continue; - } - - std::string source, dest; - source = source_path + virtualName; - dest = dest_path + virtualName; - if (IsDirectory(source)) { - source += '/'; - dest += '/'; - if (!Exists(dest)) { - CreateFullPath(dest); - } - CopyDir(source, dest); - } else if (!Exists(dest)) { - Copy(source, dest); - } - } - closedir(dirp); -#endif + LOG_TRACE(Common_Filesystem, "Successfully copied directory."); } -std::optional GetCurrentDir() { -// Get the current working directory (getcwd uses malloc) -#ifdef _WIN32 - wchar_t* dir = _wgetcwd(nullptr, 0); - if (!dir) { -#else - char* dir = getcwd(nullptr, 0); - if (!dir) { -#endif - LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); +std::optional GetCurrentDir() { + std::error_code ec; + auto path = fs::current_path(ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Unable to retrieve current working directory: {}", + ec.message()); return std::nullopt; } -#ifdef _WIN32 - std::string strDir = Common::UTF16ToUTF8(dir); -#else - std::string strDir = dir; -#endif - free(dir); - return strDir; + + return {std::move(path)}; } -bool SetCurrentDir(const std::string& directory) { -#ifdef _WIN32 - return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; -#else - return chdir(directory.c_str()) == 0; -#endif +bool SetCurrentDir(const fs::path& path) { + std::error_code ec; + fs::current_path(path, ec); + + if (ec) { + LOG_ERROR(Common_Filesystem, "Unable to set {} as working directory: {}", path.string(), + ec.message()); + return false; + } + + return true; } #if defined(__APPLE__) -- cgit v1.2.3 From 52f13f2339f8c7259d2aa646346b29997b85c0ec Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 9 Dec 2020 05:21:08 -0300 Subject: common/file_util: Succeed on CreateDir when the directory exists --- src/common/file_util.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index d5f6ea2ee..c4d738bb6 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -98,6 +98,11 @@ bool Delete(const fs::path& path) { bool CreateDir(const fs::path& path) { LOG_TRACE(Common_Filesystem, "directory {}", path.string()); + if (Exists(path)) { + LOG_DEBUG(Common_Filesystem, "path exists {}", path.string()); + return true; + } + std::error_code ec; const bool success = fs::create_directory(path, ec); -- cgit v1.2.3 From 532983437616e15539b448594a360c138995e282 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 9 Dec 2020 05:22:16 -0300 Subject: common/file_util: Fix and deprecate CreateFullPath, add CreateDirs Fix CreateFullPath to have its intended previous behavior (whatever that was), and deprecate it in favor of the new CreateDirs function. Unlike CreateDir, CreateDirs is marked as [[nodiscard]] to avoid new code ignoring its result value. --- src/common/file_util.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index c4d738bb6..7752c0421 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -114,20 +114,41 @@ bool CreateDir(const fs::path& path) { return true; } -bool CreateFullPath(const fs::path& path) { +bool CreateDirs(const fs::path& path) { LOG_TRACE(Common_Filesystem, "path {}", path.string()); + if (Exists(path)) { + LOG_DEBUG(Common_Filesystem, "path exists {}", path.string()); + return true; + } + std::error_code ec; const bool success = fs::create_directories(path, ec); if (!success) { - LOG_ERROR(Common_Filesystem, "Unable to create full path: {}", ec.message()); + LOG_ERROR(Common_Filesystem, "Unable to create directories: {}", ec.message()); return false; } return true; } +bool CreateFullPath(const fs::path& path) { + LOG_TRACE(Common_Filesystem, "path {}", path); + + // Removes trailing slashes and turns any '\' into '/' + const auto new_path = SanitizePath(path.string(), DirectorySeparator::ForwardSlash); + + if (new_path.rfind('.') == std::string::npos) { + // The path is a directory + return CreateDirs(new_path); + } else { + // The path is a file + // Creates directory preceding the last '/' + return CreateDirs(new_path.substr(0, new_path.rfind('/'))); + } +} + bool Rename(const fs::path& src, const fs::path& dst) { LOG_TRACE(Common_Filesystem, "{} --> {}", src.string(), dst.string()); -- cgit v1.2.3 From bab9cae71f5cd5a0078853e64bdf1d1f8af1fc30 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 9 Dec 2020 18:52:36 -0300 Subject: common/file_util: Let std::filesystem cast from UTF16 to std::string Fix invalid encoding paths when iterating over a directory on Windows. --- src/common/file_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 7752c0421..a286b9341 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -239,7 +239,7 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, } // windows loop do { - const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName)); + const std::string virtual_name = std::filesystem::path(ffd.cFileName).string(); #else DIR* dirp = opendir(directory.c_str()); if (!dirp) -- cgit v1.2.3 From ec8548b414d39bf389ca55154e383ca5574344e7 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 9 Dec 2020 19:25:00 -0500 Subject: common/file_util: Simplify the behavior of CreateFullPath --- src/common/file_util.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index a286b9341..8e061ff6c 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -136,16 +136,10 @@ bool CreateDirs(const fs::path& path) { bool CreateFullPath(const fs::path& path) { LOG_TRACE(Common_Filesystem, "path {}", path); - // Removes trailing slashes and turns any '\' into '/' - const auto new_path = SanitizePath(path.string(), DirectorySeparator::ForwardSlash); - - if (new_path.rfind('.') == std::string::npos) { - // The path is a directory - return CreateDirs(new_path); + if (path.has_extension()) { + return CreateDirs(path.parent_path()); } else { - // The path is a file - // Creates directory preceding the last '/' - return CreateDirs(new_path.substr(0, new_path.rfind('/'))); + return CreateDirs(path); } } -- cgit v1.2.3 From ac3ec5ed132a8d8b63c4e95205521addb90fdd0f Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 11 Dec 2020 20:21:21 -0500 Subject: Revert "Merge pull request #5181 from Morph1984/5174-review" This reverts commit cdb36aef9ec9d30bdef1953f9ed46776ae2f12af, reversing changes made to 5e9b77129f2cf8c039a8d98033cae4ac0f93f515. --- src/common/file_util.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 8e061ff6c..a286b9341 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -136,10 +136,16 @@ bool CreateDirs(const fs::path& path) { bool CreateFullPath(const fs::path& path) { LOG_TRACE(Common_Filesystem, "path {}", path); - if (path.has_extension()) { - return CreateDirs(path.parent_path()); + // Removes trailing slashes and turns any '\' into '/' + const auto new_path = SanitizePath(path.string(), DirectorySeparator::ForwardSlash); + + if (new_path.rfind('.') == std::string::npos) { + // The path is a directory + return CreateDirs(new_path); } else { - return CreateDirs(path); + // The path is a file + // Creates directory preceding the last '/' + return CreateDirs(new_path.substr(0, new_path.rfind('/'))); } } -- cgit v1.2.3 From 0195038c07f88bb62fd492559bbc264a71c3ac7a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 11 Dec 2020 20:21:46 -0500 Subject: Revert "Merge pull request #5179 from ReinUsesLisp/fs-path" This reverts commit 4e94d0d53af2cdb7b03ef9de23cc29f3565df97a, reversing changes made to 6d6115475b4edccdf1bb4e96ecc3d3b1be319e76. --- src/common/file_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index a286b9341..7752c0421 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -239,7 +239,7 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, } // windows loop do { - const std::string virtual_name = std::filesystem::path(ffd.cFileName).string(); + const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName)); #else DIR* dirp = opendir(directory.c_str()); if (!dirp) -- cgit v1.2.3 From 8941cdb7d2355ba0f3084b306165923520f9db65 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 11 Dec 2020 20:22:01 -0500 Subject: Revert "Merge pull request #5174 from ReinUsesLisp/fs-fix" This reverts commit 5fe55b16a11d9ec607fb8a3fdddc77a4393cd96a, reversing changes made to e94dd7e2c4fc3f7ca2c15c01bdc301be2b8a4c1b. --- src/common/file_util.cpp | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 7752c0421..d5f6ea2ee 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -98,11 +98,6 @@ bool Delete(const fs::path& path) { bool CreateDir(const fs::path& path) { LOG_TRACE(Common_Filesystem, "directory {}", path.string()); - if (Exists(path)) { - LOG_DEBUG(Common_Filesystem, "path exists {}", path.string()); - return true; - } - std::error_code ec; const bool success = fs::create_directory(path, ec); @@ -114,41 +109,20 @@ bool CreateDir(const fs::path& path) { return true; } -bool CreateDirs(const fs::path& path) { +bool CreateFullPath(const fs::path& path) { LOG_TRACE(Common_Filesystem, "path {}", path.string()); - if (Exists(path)) { - LOG_DEBUG(Common_Filesystem, "path exists {}", path.string()); - return true; - } - std::error_code ec; const bool success = fs::create_directories(path, ec); if (!success) { - LOG_ERROR(Common_Filesystem, "Unable to create directories: {}", ec.message()); + LOG_ERROR(Common_Filesystem, "Unable to create full path: {}", ec.message()); return false; } return true; } -bool CreateFullPath(const fs::path& path) { - LOG_TRACE(Common_Filesystem, "path {}", path); - - // Removes trailing slashes and turns any '\' into '/' - const auto new_path = SanitizePath(path.string(), DirectorySeparator::ForwardSlash); - - if (new_path.rfind('.') == std::string::npos) { - // The path is a directory - return CreateDirs(new_path); - } else { - // The path is a file - // Creates directory preceding the last '/' - return CreateDirs(new_path.substr(0, new_path.rfind('/'))); - } -} - bool Rename(const fs::path& src, const fs::path& dst) { LOG_TRACE(Common_Filesystem, "{} --> {}", src.string(), dst.string()); -- cgit v1.2.3 From 4de079b256ce320c7e261a2c35b703e477e46fa5 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 11 Dec 2020 20:22:18 -0500 Subject: Revert "Merge pull request #5173 from lioncash/common-fs" This reverts commit ce5fcb6bb2c358b0251a2ce87945bda52789a76d, reversing changes made to 6f41763061082d5fa2ab039c554427152243cb46. --- src/common/file_util.cpp | 439 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 350 insertions(+), 89 deletions(-) (limited to 'src/common/file_util.cpp') diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index d5f6ea2ee..18fbfa25b 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include -#include #include #include #include @@ -68,102 +67,290 @@ #include #include +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif + +// This namespace has various generic functions related to files and paths. +// The code still needs a ton of cleanup. +// REMEMBER: strdup considered harmful! namespace Common::FS { -namespace fs = std::filesystem; -bool Exists(const fs::path& path) { - std::error_code ec; - return fs::exists(path, ec); +// Remove any ending forward slashes from directory paths +// Modifies argument. +static void StripTailDirSlashes(std::string& fname) { + if (fname.length() <= 1) { + return; + } + + std::size_t i = fname.length(); + while (i > 0 && fname[i - 1] == DIR_SEP_CHR) { + --i; + } + fname.resize(i); } -bool IsDirectory(const fs::path& path) { - std::error_code ec; - return fs::is_directory(path, ec); +bool Exists(const std::string& filename) { + struct stat file_info; + + std::string copy(filename); + StripTailDirSlashes(copy); + +#ifdef _WIN32 + // Windows needs a slash to identify a driver root + if (copy.size() != 0 && copy.back() == ':') + copy += DIR_SEP_CHR; + + int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); +#else + int result = stat(copy.c_str(), &file_info); +#endif + + return (result == 0); } -bool Delete(const fs::path& path) { - LOG_TRACE(Common_Filesystem, "file {}", path.string()); +bool IsDirectory(const std::string& filename) { + struct stat file_info; - // Return true because we care about the file no - // being there, not the actual delete. - if (!Exists(path)) { - LOG_DEBUG(Common_Filesystem, "{} does not exist", path.string()); - return true; + std::string copy(filename); + StripTailDirSlashes(copy); + +#ifdef _WIN32 + // Windows needs a slash to identify a driver root + if (copy.size() != 0 && copy.back() == ':') + copy += DIR_SEP_CHR; + + int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); +#else + int result = stat(copy.c_str(), &file_info); +#endif + + if (result < 0) { + LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); + return false; } - std::error_code ec; - return fs::remove(path, ec); + return S_ISDIR(file_info.st_mode); } -bool CreateDir(const fs::path& path) { - LOG_TRACE(Common_Filesystem, "directory {}", path.string()); +bool Delete(const std::string& filename) { + LOG_TRACE(Common_Filesystem, "file {}", filename); - std::error_code ec; - const bool success = fs::create_directory(path, ec); + // Return true because we care about the file no + // being there, not the actual delete. + if (!Exists(filename)) { + LOG_DEBUG(Common_Filesystem, "{} does not exist", filename); + return true; + } + + // We can't delete a directory + if (IsDirectory(filename)) { + LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); + return false; + } - if (!success) { - LOG_ERROR(Common_Filesystem, "Unable to create directory: {}", ec.message()); +#ifdef _WIN32 + if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { + LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); + return false; + } +#else + if (unlink(filename.c_str()) == -1) { + LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); return false; } +#endif return true; } -bool CreateFullPath(const fs::path& path) { - LOG_TRACE(Common_Filesystem, "path {}", path.string()); +bool CreateDir(const std::string& path) { + LOG_TRACE(Common_Filesystem, "directory {}", path); +#ifdef _WIN32 + if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) + return true; + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) { + LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); + return true; + } + LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); + return false; +#else + if (mkdir(path.c_str(), 0755) == 0) + return true; - std::error_code ec; - const bool success = fs::create_directories(path, ec); + int err = errno; - if (!success) { - LOG_ERROR(Common_Filesystem, "Unable to create full path: {}", ec.message()); - return false; + if (err == EEXIST) { + LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); + return true; } - return true; + LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); + return false; +#endif } -bool Rename(const fs::path& src, const fs::path& dst) { - LOG_TRACE(Common_Filesystem, "{} --> {}", src.string(), dst.string()); +bool CreateFullPath(const std::string& fullPath) { + int panicCounter = 100; + LOG_TRACE(Common_Filesystem, "path {}", fullPath); + + if (Exists(fullPath)) { + LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); + return true; + } + + std::size_t position = 0; + while (true) { + // Find next sub path + position = fullPath.find(DIR_SEP_CHR, position); - std::error_code ec; - fs::rename(src, dst, ec); + // we're done, yay! + if (position == fullPath.npos) + return true; - if (ec) { - LOG_ERROR(Common_Filesystem, "Unable to rename file from {} to {}: {}", src.string(), - dst.string(), ec.message()); + // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") + std::string const subPath(fullPath.substr(0, position + 1)); + if (!IsDirectory(subPath) && !CreateDir(subPath)) { + LOG_ERROR(Common, "CreateFullPath: directory creation failed"); + return false; + } + + // A safety check + panicCounter--; + if (panicCounter <= 0) { + LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); + return false; + } + position++; + } +} + +bool DeleteDir(const std::string& filename) { + LOG_TRACE(Common_Filesystem, "directory {}", filename); + + // check if a directory + if (!IsDirectory(filename)) { + LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); return false; } - return true; +#ifdef _WIN32 + if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str())) + return true; +#else + if (rmdir(filename.c_str()) == 0) + return true; +#endif + LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); + + return false; +} + +bool Rename(const std::string& srcFilename, const std::string& destFilename) { + LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); +#ifdef _WIN32 + if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), + Common::UTF8ToUTF16W(destFilename).c_str()) == 0) + return true; +#else + if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) + return true; +#endif + LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, + GetLastErrorMsg()); + return false; } -bool Copy(const fs::path& src, const fs::path& dst) { - LOG_TRACE(Common_Filesystem, "{} --> {}", src.string(), dst.string()); +bool Copy(const std::string& srcFilename, const std::string& destFilename) { + LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); +#ifdef _WIN32 + if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), + Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) + return true; - std::error_code ec; - const bool success = fs::copy_file(src, dst, fs::copy_options::overwrite_existing, ec); + LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, + GetLastErrorMsg()); + return false; +#else + using CFilePointer = std::unique_ptr; - if (!success) { - LOG_ERROR(Common_Filesystem, "Unable to copy file {} to {}: {}", src.string(), dst.string(), - ec.message()); + // Open input file + CFilePointer input{fopen(srcFilename.c_str(), "rb"), std::fclose}; + if (!input) { + LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); return false; } + // open output file + CFilePointer output{fopen(destFilename.c_str(), "wb"), std::fclose}; + if (!output) { + LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); + return false; + } + + // copy loop + std::array buffer; + while (!feof(input.get())) { + // read input + std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); + if (rnum != buffer.size()) { + if (ferror(input.get()) != 0) { + LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", + srcFilename, destFilename, GetLastErrorMsg()); + return false; + } + } + + // write output + std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); + if (wnum != rnum) { + LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); + return false; + } + } + return true; +#endif } -u64 GetSize(const fs::path& path) { - std::error_code ec; - const auto size = fs::file_size(path, ec); +u64 GetSize(const std::string& filename) { + if (!Exists(filename)) { + LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); + return 0; + } - if (ec) { - LOG_ERROR(Common_Filesystem, "Unable to retrieve file size ({}): {}", path.string(), - ec.message()); + if (IsDirectory(filename)) { + LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); return 0; } - return size; + struct stat buf; +#ifdef _WIN32 + if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0) +#else + if (stat(filename.c_str(), &buf) == 0) +#endif + { + LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); + return buf.st_size; + } + + LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); + return 0; +} + +u64 GetSize(const int fd) { + struct stat buf; + if (fstat(fd, &buf) != 0) { + LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); + return 0; + } + return buf.st_size; } u64 GetSize(FILE* f) { @@ -251,58 +438,132 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, return true; } -bool DeleteDirRecursively(const fs::path& path) { - std::error_code ec; - fs::remove_all(path, ec); +u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, + unsigned int recursion) { + const auto callback = [recursion, &parent_entry](u64* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { + FSTEntry entry; + entry.virtualName = virtual_name; + entry.physicalName = directory + DIR_SEP + virtual_name; + + if (IsDirectory(entry.physicalName)) { + entry.isDirectory = true; + // is a directory, lets go inside if we didn't recurse to often + if (recursion > 0) { + entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1); + *num_entries_out += entry.size; + } else { + entry.size = 0; + } + } else { // is a file + entry.isDirectory = false; + entry.size = GetSize(entry.physicalName); + } + (*num_entries_out)++; - if (ec) { - LOG_ERROR(Common_Filesystem, "Unable to completely delete directory {}: {}", path.string(), - ec.message()); - return false; - } + // Push into the tree + parent_entry.children.push_back(std::move(entry)); + return true; + }; - return true; + u64 num_entries; + return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0; } -void CopyDir(const fs::path& src, const fs::path& dst) { - constexpr auto copy_flags = fs::copy_options::skip_existing | fs::copy_options::recursive; +bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { + const auto callback = [recursion](u64*, const std::string& directory, + const std::string& virtual_name) { + const std::string new_path = directory + DIR_SEP_CHR + virtual_name; - std::error_code ec; - fs::copy(src, dst, copy_flags, ec); + if (IsDirectory(new_path)) { + if (recursion == 0) { + return false; + } + return DeleteDirRecursively(new_path, recursion - 1); + } + return Delete(new_path); + }; - if (ec) { - LOG_ERROR(Common_Filesystem, "Error copying directory {} to {}: {}", src.string(), - dst.string(), ec.message()); - return; - } + if (!ForeachDirectoryEntry(nullptr, directory, callback)) + return false; - LOG_TRACE(Common_Filesystem, "Successfully copied directory."); + // Delete the outermost directory + DeleteDir(directory); + return true; } -std::optional GetCurrentDir() { - std::error_code ec; - auto path = fs::current_path(ec); +void CopyDir([[maybe_unused]] const std::string& source_path, + [[maybe_unused]] const std::string& dest_path) { +#ifndef _WIN32 + if (source_path == dest_path) { + return; + } + if (!Exists(source_path)) { + return; + } + if (!Exists(dest_path)) { + CreateFullPath(dest_path); + } - if (ec) { - LOG_ERROR(Common_Filesystem, "Unable to retrieve current working directory: {}", - ec.message()); - return std::nullopt; + DIR* dirp = opendir(source_path.c_str()); + if (!dirp) { + return; } - return {std::move(path)}; -} + while (struct dirent* result = readdir(dirp)) { + const std::string virtualName(result->d_name); + // check for "." and ".." + if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || + ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) { + continue; + } -bool SetCurrentDir(const fs::path& path) { - std::error_code ec; - fs::current_path(path, ec); + std::string source, dest; + source = source_path + virtualName; + dest = dest_path + virtualName; + if (IsDirectory(source)) { + source += '/'; + dest += '/'; + if (!Exists(dest)) { + CreateFullPath(dest); + } + CopyDir(source, dest); + } else if (!Exists(dest)) { + Copy(source, dest); + } + } + closedir(dirp); +#endif +} - if (ec) { - LOG_ERROR(Common_Filesystem, "Unable to set {} as working directory: {}", path.string(), - ec.message()); - return false; +std::optional GetCurrentDir() { +// Get the current working directory (getcwd uses malloc) +#ifdef _WIN32 + wchar_t* dir = _wgetcwd(nullptr, 0); + if (!dir) { +#else + char* dir = getcwd(nullptr, 0); + if (!dir) { +#endif + LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); + return std::nullopt; } +#ifdef _WIN32 + std::string strDir = Common::UTF16ToUTF8(dir); +#else + std::string strDir = dir; +#endif + free(dir); + return strDir; +} - return true; +bool SetCurrentDir(const std::string& directory) { +#ifdef _WIN32 + return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; +#else + return chdir(directory.c_str()) == 0; +#endif } #if defined(__APPLE__) -- cgit v1.2.3