From 3745cec3cb4ad00bf1a780befb41ac1f2be9ba55 Mon Sep 17 00:00:00 2001 From: levlam Date: Thu, 13 Sep 2018 20:41:26 +0300 Subject: [PATCH] Pass std::function as callback in walk_path. GitOrigin-RevId: 80da4073e3b1d7d2b2b136efecac8b26a774157b --- tdutils/td/utils/port/path.cpp | 174 ++++++++++++++++++++++++++- tdutils/td/utils/port/path.h | 211 ++------------------------------- tdutils/test/port.cpp | 6 +- 3 files changed, 186 insertions(+), 205 deletions(-) diff --git a/tdutils/td/utils/port/path.cpp b/tdutils/td/utils/port/path.cpp index ec3cf224..b6957e18 100644 --- a/tdutils/td/utils/port/path.cpp +++ b/tdutils/td/utils/port/path.cpp @@ -6,14 +6,21 @@ // #include "td/utils/port/path.h" -#include "td/utils/port/detail/PollableFd.h" +#include "td/utils/port/config.h" -#if TD_WINDOWS +#include "td/utils/format.h" +#include "td/utils/logging.h" +#include "td/utils/port/detail/PollableFd.h" +#include "td/utils/ScopeGuard.h" + +#if TD_PORT_WINDOWS +#include "td/utils/port/wstring_convert.h" #include "td/utils/Random.h" #endif #if TD_PORT_POSIX +#include #include #include @@ -32,7 +39,12 @@ #endif +#if TD_DARWIN +#include +#endif + #include +#include namespace td { @@ -226,6 +238,117 @@ Result mkdtemp(CSlice dir, Slice prefix) { return result; } +namespace detail { +Status walk_path_dir(string &path, FileFd fd, + const std::function &func) TD_WARN_UNUSED_RESULT; + +Status walk_path_dir(string &path, + const std::function &func) TD_WARN_UNUSED_RESULT; + +Status walk_path_file(string &path, + const std::function &func) TD_WARN_UNUSED_RESULT; + +Status walk_path(string &path, const std::function &func) TD_WARN_UNUSED_RESULT; + +Status walk_path_subdir(string &path, DIR *dir, const std::function &func) { + while (true) { + errno = 0; + auto *entry = readdir(dir); + auto readdir_errno = errno; + if (readdir_errno) { + return Status::PosixError(readdir_errno, "readdir"); + } + if (entry == nullptr) { + return Status::OK(); + } + Slice name = Slice(static_cast(entry->d_name)); + if (name == "." || name == "..") { + continue; + } + auto size = path.size(); + if (path.back() != TD_DIR_SLASH) { + path += TD_DIR_SLASH; + } + path.append(name.begin(), name.size()); + SCOPE_EXIT { + path.resize(size); + }; + Status status; +#ifdef DT_DIR + if (entry->d_type == DT_UNKNOWN) { + status = walk_path(path, func); + } else if (entry->d_type == DT_DIR) { + status = walk_path_dir(path, func); + } else if (entry->d_type == DT_REG) { + status = walk_path_file(path, func); + } +#else +#warning "Slow walk_path" + status = walk_path(path, func); +#endif + if (status.is_error()) { + return status; + } + } +} + +Status walk_path_dir(string &path, DIR *subdir, const std::function &func) { + SCOPE_EXIT { + closedir(subdir); + }; + TRY_STATUS(walk_path_subdir(path, subdir, func)); + func(path, true); + return Status::OK(); +} + +Status walk_path_dir(string &path, FileFd fd, const std::function &func) { + auto native_fd = fd.move_as_native_fd(); + auto *subdir = fdopendir(native_fd.fd()); + if (subdir == nullptr) { + return OS_ERROR("fdopendir"); + } + native_fd.release(); + return walk_path_dir(path, subdir, func); +} + +Status walk_path_dir(string &path, const std::function &func) { + auto *subdir = opendir(path.c_str()); + if (subdir == nullptr) { + return OS_ERROR(PSLICE() << tag("opendir", path)); + } + return walk_path_dir(path, subdir, func); +} + +Status walk_path_file(string &path, const std::function &func) { + func(path, false); + return Status::OK(); +} + +Status walk_path(string &path, const std::function &func) { + TRY_RESULT(fd, FileFd::open(path, FileFd::Read)); + auto stat = fd.stat(); + bool is_dir = stat.is_dir_; + bool is_reg = stat.is_reg_; + if (is_dir) { + return walk_path_dir(path, std::move(fd), func); + } + + fd.close(); + if (is_reg) { + return walk_path_file(path, func); + } + + return Status::OK(); +} +} // namespace detail + +Status walk_path(CSlice path, const std::function &func) { + string curr_path; + curr_path.reserve(PATH_MAX + 10); + curr_path = path.c_str(); + return detail::walk_path(curr_path, func); +} + #endif #if TD_PORT_WINDOWS @@ -388,6 +511,53 @@ Result> mkstemp(CSlice dir) { return Status::Error(PSLICE() << "Can't create temporary file \"" << file_pattern << '"'); } +static Status walk_path_dir(const std::wstring &dir_name, + const std::function &func) { + std::wstring name = dir_name + L"\\*"; + + WIN32_FIND_DATA file_data; + auto handle = FindFirstFileExW(name.c_str(), FindExInfoStandard, &file_data, FindExSearchNameMatch, nullptr, 0); + if (handle == INVALID_HANDLE_VALUE) { + return OS_ERROR(PSLICE() << "FindFirstFileEx" << tag("name", from_wstring(name).ok())); + } + + SCOPE_EXIT { + FindClose(handle); + }; + while (true) { + auto full_name = dir_name + L"\\" + file_data.cFileName; + TRY_RESULT(entry_name, from_wstring(full_name)); + if (file_data.cFileName[0] != '.') { + if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + TRY_STATUS(walk_path_dir(full_name, func)); + } else { + func(entry_name, false); + } + } + auto status = FindNextFileW(handle, &file_data); + if (status == 0) { + auto last_error = GetLastError(); + if (last_error == ERROR_NO_MORE_FILES) { + break; + } + return OS_ERROR("FindNextFileW"); + } + } + TRY_RESULT(entry_name, from_wstring(dir_name)); + func(entry_name, true); + return Status::OK(); +} + +Status walk_path(CSlice path, const std::function &func) { + TRY_RESULT(wpath, to_wstring(path)); + Slice path_slice = path; + while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) { + path_slice.remove_suffix(1); + wpath.pop_back(); + } + return walk_path_dir(wpath, func); +} + #endif } // namespace td diff --git a/tdutils/td/utils/port/path.h b/tdutils/td/utils/port/path.h index 777fd5cc..29a478a4 100644 --- a/tdutils/td/utils/port/path.h +++ b/tdutils/td/utils/port/path.h @@ -6,225 +6,40 @@ // #pragma once -#include "td/utils/port/config.h" - #include "td/utils/common.h" -#include "td/utils/format.h" -#include "td/utils/logging.h" #include "td/utils/port/FileFd.h" -#include "td/utils/ScopeGuard.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" -#if TD_PORT_POSIX -#include -#include -#include -#endif - -#if TD_DARWIN -#include -#endif - -#if TD_PORT_WINDOWS -#include "td/utils/port/wstring_convert.h" - -#include -#endif - +#include #include namespace td { Status mkdir(CSlice dir, int32 mode = 0700) TD_WARN_UNUSED_RESULT; + Status mkpath(CSlice path, int32 mode = 0700) TD_WARN_UNUSED_RESULT; + Status rename(CSlice from, CSlice to) TD_WARN_UNUSED_RESULT; + Result realpath(CSlice slice, bool ignore_access_denied = false) TD_WARN_UNUSED_RESULT; + Status chdir(CSlice dir) TD_WARN_UNUSED_RESULT; + Status rmdir(CSlice dir) TD_WARN_UNUSED_RESULT; + Status unlink(CSlice path) TD_WARN_UNUSED_RESULT; + Status rmrf(CSlice path) TD_WARN_UNUSED_RESULT; + Status set_temporary_dir(CSlice dir) TD_WARN_UNUSED_RESULT; + CSlice get_temporary_dir(); + Result> mkstemp(CSlice dir) TD_WARN_UNUSED_RESULT; + Result mkdtemp(CSlice dir, Slice prefix) TD_WARN_UNUSED_RESULT; -template -Status walk_path(CSlice path, Func &&func) TD_WARN_UNUSED_RESULT; - -#if TD_PORT_POSIX - -// TODO move details somewhere else -namespace detail { -template -Status walk_path_dir(string &path, FileFd fd, Func &&func) TD_WARN_UNUSED_RESULT; -template -Status walk_path_dir(string &path, Func &&func) TD_WARN_UNUSED_RESULT; -template -Status walk_path_file(string &path, Func &&func) TD_WARN_UNUSED_RESULT; -template -Status walk_path(string &path, Func &&func) TD_WARN_UNUSED_RESULT; - -template -Status walk_path_subdir(string &path, DIR *dir, Func &&func) { - while (true) { - errno = 0; - auto *entry = readdir(dir); - auto readdir_errno = errno; - if (readdir_errno) { - return Status::PosixError(readdir_errno, "readdir"); - } - if (entry == nullptr) { - return Status::OK(); - } - Slice name = Slice(static_cast(entry->d_name)); - if (name == "." || name == "..") { - continue; - } - auto size = path.size(); - if (path.back() != TD_DIR_SLASH) { - path += TD_DIR_SLASH; - } - path.append(name.begin(), name.size()); - SCOPE_EXIT { - path.resize(size); - }; - Status status; -#ifdef DT_DIR - if (entry->d_type == DT_UNKNOWN) { - status = walk_path(path, std::forward(func)); - } else if (entry->d_type == DT_DIR) { - status = walk_path_dir(path, std::forward(func)); - } else if (entry->d_type == DT_REG) { - status = walk_path_file(path, std::forward(func)); - } -#else -#warning "Slow walk_path" - status = walk_path(path, std::forward(func)); -#endif - if (status.is_error()) { - return status; - } - } -} - -template -Status walk_path_dir(string &path, DIR *subdir, Func &&func) { - SCOPE_EXIT { - closedir(subdir); - }; - TRY_STATUS(walk_path_subdir(path, subdir, std::forward(func))); - func(path, true); - return Status::OK(); -} - -template -Status walk_path_dir(string &path, FileFd fd, Func &&func) { - auto native_fd = fd.move_as_native_fd(); - auto *subdir = fdopendir(native_fd.fd()); - if (subdir == nullptr) { - return OS_ERROR("fdopendir"); - } - native_fd.release(); - return walk_path_dir(path, subdir, std::forward(func)); -} - -template -Status walk_path_dir(string &path, Func &&func) { - auto *subdir = opendir(path.c_str()); - if (subdir == nullptr) { - return OS_ERROR(PSLICE() << tag("opendir", path)); - } - return walk_path_dir(path, subdir, std::forward(func)); -} - -template -Status walk_path_file(string &path, Func &&func) { - func(path, false); - return Status::OK(); -} - -template -Status walk_path(string &path, Func &&func) { - TRY_RESULT(fd, FileFd::open(path, FileFd::Read)); - auto stat = fd.stat(); - bool is_dir = stat.is_dir_; - bool is_reg = stat.is_reg_; - if (is_dir) { - return walk_path_dir(path, std::move(fd), std::forward(func)); - } - - fd.close(); - if (is_reg) { - return walk_path_file(path, func); - } - - return Status::OK(); -} -} // namespace detail - -template -Status walk_path(CSlice path, Func &&func) { - string curr_path; - curr_path.reserve(PATH_MAX + 10); - curr_path = path.c_str(); - return detail::walk_path(curr_path, std::forward(func)); -} - -#endif - -#if TD_PORT_WINDOWS - -namespace detail { -template -Status walk_path_dir(const std::wstring &dir_name, Func &&func) { - std::wstring name = dir_name + L"\\*"; - - WIN32_FIND_DATA file_data; - auto handle = FindFirstFileExW(name.c_str(), FindExInfoStandard, &file_data, FindExSearchNameMatch, nullptr, 0); - if (handle == INVALID_HANDLE_VALUE) { - return OS_ERROR(PSLICE() << "FindFirstFileEx" << tag("name", from_wstring(name).ok())); - } - - SCOPE_EXIT { - FindClose(handle); - }; - while (true) { - auto full_name = dir_name + L"\\" + file_data.cFileName; - TRY_RESULT(entry_name, from_wstring(full_name)); - if (file_data.cFileName[0] != '.') { - if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { - TRY_STATUS(walk_path_dir(full_name, std::forward(func))); - } else { - func(entry_name, false); - } - } - auto status = FindNextFileW(handle, &file_data); - if (status == 0) { - auto last_error = GetLastError(); - if (last_error == ERROR_NO_MORE_FILES) { - break; - } - return OS_ERROR("FindNextFileW"); - } - } - TRY_RESULT(entry_name, from_wstring(dir_name)); - func(entry_name, true); - return Status::OK(); -} -} // namespace detail - -template -Status walk_path(CSlice path, Func &&func) { - TRY_RESULT(wpath, to_wstring(path)); - Slice path_slice = path; - while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) { - path_slice.remove_suffix(1); - wpath.pop_back(); - } - return detail::walk_path_dir(wpath.c_str(), std::forward(func)); -} - -#endif +Status walk_path(CSlice path, const std::function &func) TD_WARN_UNUSED_RESULT; } // namespace td diff --git a/tdutils/test/port.cpp b/tdutils/test/port.cpp index 58b8d669..7d5e08a6 100644 --- a/tdutils/test/port.cpp +++ b/tdutils/test/port.cpp @@ -35,15 +35,11 @@ TEST(Port, files) { const int ITER_COUNT = 1000; for (int i = 0; i < ITER_COUNT; i++) { walk_path(main_dir, - [&, ptr = std::make_unique(2)](CSlice name, bool is_directory) mutable { - ASSERT_EQ(2, *ptr); + [&](CSlice name, bool is_directory) mutable { if (!is_directory) { ASSERT_TRUE(name == fd_path || name == fd2_path); } cnt++; - if (cnt == 7) { - ptr.reset(); - } }) .ensure(); }