From 0b5efef569adbca24621a5186c28713a88738598 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Thu, 8 Aug 2024 18:59:14 -0500 Subject: [PATCH] updated build system --- CMakeLists.txt | 8 + config.sh | 2 + .../comm/curl/requests/http_request_base.hpp | 6 +- .../include/file_manager/file_manager.hpp | 2 +- .../src/comm/curl/requests/http_put_file.cpp | 4 +- .../file_manager/file_manager_open_file.cpp | 28 +- .../file_manager_ring_buffer_open_file.cpp | 18 +- .../librepertory/src/utils/file_utils.cpp | 10 +- .../src/encrypting_reader_test.cpp | 2 +- .../src/file_manager_open_file_test.cpp | 8 +- ...ile_manager_ring_buffer_open_file_test.cpp | 20 +- .../repertory_test/src/file_manager_test.cpp | 11 +- .../repertory_test/src/remote_winfsp_test.cpp | 3 +- scripts/env.sh | 10 + support/include/utils/config.hpp | 7 + support/include/utils/encrypting_reader.hpp | 2 +- support/include/utils/file.hpp | 355 ++++++++++++++++-- support/include/utils/path.hpp | 310 ++++++++------- support/src/utils/encrypting_reader.cpp | 36 +- support/src/utils/file.cpp | 148 +++++++- support/src/utils/file_file.cpp | 200 ++++++---- support/src/utils/file_thread_file.cpp | 64 ++++ support/src/utils/path.cpp | 135 +++---- support/src/utils/windows.cpp | 2 +- support/test/include/test.hpp | 3 +- support/test/src/test.cpp | 34 +- support/test/src/utils/common_test.cpp | 2 +- .../test/src/utils/encrypting_reader_test.cpp | 16 +- support/test/src/utils/file_test.cpp | 86 +++-- support/test/src/utils/path_test.cpp | 104 +++-- 30 files changed, 1162 insertions(+), 474 deletions(-) create mode 100644 support/src/utils/file_thread_file.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b436d57..b28f0280 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,13 @@ if(PROJECT_IS_ARM64) add_definitions(-DPROJECT_IS_ARM64) endif() +if(PROJECT_IS_MINGW) + option(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES "Enable path sizes of 32767 characters on Windows" OFF) + if(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + add_definitions(-DPROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + endif() +endif() + include(cmake/settings.cmake) include(cmake/flags.cmake) @@ -127,6 +134,7 @@ endif() -DPROJECT_COPYRIGHT=${PROJECT_COPYRIGHT} -DPROJECT_DESC=${PROJECT_DESC} -DPROJECT_DIST_DIR=${PROJECT_DIST_DIR} + -DPROJECT_ENABLE_WIN32_LONG_PATH_NAMES=${PROJECT_ENABLE_WIN32_LONG_PATH_NAMES} -DPROJECT_ENABLE_BACKWARD_CPP=${PROJECT_ENABLE_BACKWARD_CPP} -DPROJECT_ENABLE_BOOST=${PROJECT_ENABLE_BOOST} -DPROJECT_ENABLE_CPP_HTTPLIB=${PROJECT_ENABLE_CPP_HTTPLIB} diff --git a/config.sh b/config.sh index 76bd46f7..2fa88cca 100755 --- a/config.sh +++ b/config.sh @@ -19,6 +19,8 @@ PROJECT_APP_LIST=(${PROJECT_NAME}) PROJECT_PRIVATE_KEY=${DEVELOPER_PRIVATE_KEY} PROJECT_PUBLIC_KEY=${DEVELOPER_PUBLIC_KEY} +PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF + PROJECT_ENABLE_BACKWARD_CPP=ON PROJECT_ENABLE_BOOST=ON PROJECT_ENABLE_CPP_HTTPLIB=ON diff --git a/repertory/librepertory/include/comm/curl/requests/http_request_base.hpp b/repertory/librepertory/include/comm/curl/requests/http_request_base.hpp index 44a983c3..c5eeb1d5 100644 --- a/repertory/librepertory/include/comm/curl/requests/http_request_base.hpp +++ b/repertory/librepertory/include/comm/curl/requests/http_request_base.hpp @@ -33,7 +33,7 @@ using response_callback = struct read_file_info final { stop_type &stop_requested; - utils::file::file file{}; + std::unique_ptr file{}; std::uint64_t offset{}; }; @@ -42,8 +42,8 @@ inline const auto read_file_data = static_cast( auto *read_info = reinterpret_cast(instream); std::size_t bytes_read{}; auto ret = - read_info->file.read(reinterpret_cast(buffer), - size * nitems, read_info->offset, &bytes_read); + read_info->file->read(reinterpret_cast(buffer), + size * nitems, read_info->offset, &bytes_read); if (ret) { read_info->offset += bytes_read; } diff --git a/repertory/librepertory/include/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index ee5e5779..fac90ff3 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -131,7 +131,7 @@ public: std::atomic last_access_{ std::chrono::system_clock::now()}; bool modified_{false}; - utils::file::file nf_; + std::unique_ptr nf_; mutable std::mutex io_thread_mtx_; std::condition_variable io_thread_notify_; std::deque> io_thread_queue_; diff --git a/repertory/librepertory/src/comm/curl/requests/http_put_file.cpp b/repertory/librepertory/src/comm/curl/requests/http_put_file.cpp index 3c2a5b3c..09f2f8a5 100644 --- a/repertory/librepertory/src/comm/curl/requests/http_put_file.cpp +++ b/repertory/librepertory/src/comm/curl/requests/http_put_file.cpp @@ -49,11 +49,11 @@ auto http_put_file::set_method(CURL *curl, utils::file::file::open_or_create_file(source_path), }); - if (not read_info->file) { + if (not *read_info->file) { return false; } - auto file_size = read_info->file.size(); + auto file_size = read_info->file->size(); curl_easy_setopt(curl, CURLOPT_READDATA, read_info.get()); curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_file_data); diff --git a/repertory/librepertory/src/file_manager/file_manager_open_file.cpp b/repertory/librepertory/src/file_manager/file_manager_open_file.cpp index af9d603d..759de5fc 100644 --- a/repertory/librepertory/src/file_manager/file_manager_open_file.cpp +++ b/repertory/librepertory/src/file_manager/file_manager_open_file.cpp @@ -68,7 +68,7 @@ file_manager::open_file::open_file( if (not fsi.directory) { nf_ = utils::file::file::open_or_create_file(fsi.source_path, provider_.is_direct_only()); - set_api_error(nf_ ? api_error::success : api_error::os_error); + set_api_error(*nf_ ? api_error::success : api_error::os_error); if (get_api_error() == api_error::success) { if (read_state.has_value()) { read_state_ = read_state.value(); @@ -78,16 +78,16 @@ file_manager::open_file::open_file( fsi_.size, chunk_size)), false); - auto file_size = nf_.size(); + auto file_size = nf_->size(); if (provider_.is_direct_only() || file_size == fsi.size) { read_state_.set(0U, read_state_.size(), true); - } else if (not nf_.truncate(fsi.size)) { + } else if (not nf_->truncate(fsi.size)) { set_api_error(api_error::os_error); } } - if (get_api_error() != api_error::success && nf_) { - nf_.close(); + if (get_api_error() != api_error::success && *nf_) { + nf_->close(); } } } @@ -182,7 +182,7 @@ void file_manager::open_file::download_chunk(std::size_t chunk, res = do_io([&]() -> api_error { std::size_t bytes_written{}; - if (not nf_.write(data, data_offset, &bytes_written)) { + if (not nf_->write(data, data_offset, &bytes_written)) { return api_error::os_error; } @@ -242,7 +242,7 @@ auto file_manager::open_file::native_operation( } file_lock.unlock(); - return do_io([&]() -> api_error { return callback(nf_.get_handle()); }); + return do_io([&]() -> api_error { return callback(nf_->get_handle()); }); } auto file_manager::open_file::native_operation( @@ -283,7 +283,7 @@ auto file_manager::open_file::native_operation( const auto original_file_size = get_file_size(); - auto res = do_io([&]() -> api_error { return callback(nf_.get_handle()); }); + auto res = do_io([&]() -> api_error { return callback(nf_->get_handle()); }); if (res != api_error::success) { utils::error::raise_api_path_error(function_name, get_api_path(), utils::get_last_error_code(), @@ -292,7 +292,7 @@ auto file_manager::open_file::native_operation( } { - auto file_size = nf_.size(); + auto file_size = nf_->size(); if (file_size != new_file_size) { utils::error::raise_api_path_error( function_name, get_api_path(), api_error::file_size_mismatch, @@ -361,7 +361,7 @@ auto file_manager::open_file::read(std::size_t read_size, data.resize(read_size); std::size_t bytes_read{}; - return nf_.read(data.data(), read_size, read_offset, &bytes_read) + return nf_->read(data.data(), read_size, read_offset, &bytes_read) ? api_error::success : api_error::os_error; }); @@ -412,8 +412,8 @@ auto file_manager::open_file::resize(std::uint64_t new_file_size) -> api_error { return native_operation( new_file_size, [this, &new_file_size](native_handle) -> api_error { - return nf_.truncate(new_file_size) ? api_error::success - : api_error::os_error; + return nf_->truncate(new_file_size) ? api_error::success + : api_error::os_error; }); } @@ -449,7 +449,7 @@ auto file_manager::open_file::close() -> bool { } } - nf_.close(); + nf_->close(); if (modified_ && (get_api_error() == api_error::success)) { mgr_.queue_upload(*this); @@ -575,7 +575,7 @@ auto file_manager::open_file::write(std::uint64_t write_offset, } auto res = do_io([&]() -> api_error { - if (not nf_.write(data, write_offset, &bytes_written)) { + if (not nf_->write(data, write_offset, &bytes_written)) { return api_error::os_error; } diff --git a/repertory/librepertory/src/file_manager/file_manager_ring_buffer_open_file.cpp b/repertory/librepertory/src/file_manager/file_manager_ring_buffer_open_file.cpp index 17949cf9..e0adcdaf 100644 --- a/repertory/librepertory/src/file_manager/file_manager_ring_buffer_open_file.cpp +++ b/repertory/librepertory/src/file_manager/file_manager_ring_buffer_open_file.cpp @@ -73,13 +73,13 @@ file_manager::ring_buffer_open_file::ring_buffer_open_file( fsi_.source_path = utils::path::combine(buffer_directory, {utils::create_uuid_string()}); nf_ = utils::file::file::open_or_create_file(fsi_.source_path); - if (not nf_) { + if (not *nf_) { throw std::runtime_error("failed to create buffer file|err|" + std::to_string(utils::get_last_error_code())); } - if (not nf_.truncate(ring_state_.size() * chunk_size)) { - nf_.close(); + if (not nf_->truncate(ring_state_.size() * chunk_size)) { + nf_->close(); throw std::runtime_error("failed to resize buffer file|err|" + std::to_string(utils::get_last_error_code())); } @@ -92,7 +92,7 @@ file_manager::ring_buffer_open_file::~ring_buffer_open_file() { close(); - nf_.close(); + nf_->close(); if (not utils::file::retry_delete_file(fsi_.source_path)) { utils::error::raise_api_path_error( function_name, fsi_.api_path, fsi_.source_path, @@ -128,8 +128,8 @@ auto file_manager::file_manager::ring_buffer_open_file::download_chunk( if (res == api_error::success) { res = do_io([&]() -> api_error { std::size_t bytes_written{}; - if (not nf_.write(buffer, (chunk % ring_state_.size()) * chunk_size_, - &bytes_written)) { + if (not nf_->write(buffer, (chunk % ring_state_.size()) * chunk_size_, + &bytes_written)) { return api_error::os_error; } @@ -200,7 +200,7 @@ auto file_manager::ring_buffer_open_file::is_download_complete() const -> bool { auto file_manager::ring_buffer_open_file::native_operation( i_open_file::native_operation_callback callback) -> api_error { - return do_io([&]() -> api_error { return callback(nf_.get_handle()); }); + return do_io([&]() -> api_error { return callback(nf_->get_handle()); }); } void file_manager::ring_buffer_open_file::reverse(std::size_t count) { @@ -270,8 +270,8 @@ auto file_manager::ring_buffer_open_file::read(std::size_t read_size, &to_read]() -> api_error { std::size_t bytes_read{}; auto ret = - nf_.read(buffer, ((chunk % ring_state_.size()) * chunk_size_), - &bytes_read) + nf_->read(buffer, ((chunk % ring_state_.size()) * chunk_size_), + &bytes_read) ? api_error::success : api_error::os_error; if (ret == api_error::success) { diff --git a/repertory/librepertory/src/utils/file_utils.cpp b/repertory/librepertory/src/utils/file_utils.cpp index 40f7055d..35a45001 100644 --- a/repertory/librepertory/src/utils/file_utils.cpp +++ b/repertory/librepertory/src/utils/file_utils.cpp @@ -278,15 +278,15 @@ auto generate_sha256(const std::string &file_path) -> std::string { } auto nf = utils::file::file::open_file(file_path); - if (not nf) { + if (not *nf) { throw std::runtime_error("failed to open file|" + file_path); } { - data_buffer buffer(1048576u); - std::uint64_t read_offset = 0U; - std::size_t bytes_read = 0U; - while (nf.read(buffer, read_offset, &bytes_read)) { + data_buffer buffer(nf->get_read_buffer_size()); + std::uint64_t read_offset{0U}; + std::size_t bytes_read{0U}; + while (nf->read(buffer, read_offset, &bytes_read)) { if (not bytes_read) { break; } diff --git a/repertory/repertory_test/src/encrypting_reader_test.cpp b/repertory/repertory_test/src/encrypting_reader_test.cpp index 52d491ae..ee8b5972 100644 --- a/repertory/repertory_test/src/encrypting_reader_test.cpp +++ b/repertory/repertory_test/src/encrypting_reader_test.cpp @@ -32,7 +32,7 @@ namespace repertory { ASSERT_TRUE(utils::file::retry_delete_file(source_file_name)); const auto token = std::string("moose"); - auto source_file = create_random_file(source_file_name, 1024UL); + auto &source_file = create_random_file(source_file_name, 1024UL); EXPECT_TRUE(source_file != nullptr); if (source_file) { stop_type stop_requested = false; diff --git a/repertory/repertory_test/src/file_manager_open_file_test.cpp b/repertory/repertory_test/src/file_manager_open_file_test.cpp index 8b68bfb7..4bb78a87 100644 --- a/repertory/repertory_test/src/file_manager_open_file_test.cpp +++ b/repertory/repertory_test/src/file_manager_open_file_test.cpp @@ -171,7 +171,7 @@ TEST(open_file, will_change_source_path_if_file_size_is_greater_than_0) { TEST(open_file, will_not_change_source_path_if_file_size_matches_existing_source) { - auto rf = test::create_random_file(test_chunk_size); + auto &rf = test::create_random_file(test_chunk_size); const auto source_path = rf.get_path().string(); rf.close(); @@ -197,7 +197,7 @@ TEST(open_file, TEST(open_file, write_with_incomplete_download) { const auto source_path = test::generate_test_file_name("test"); - auto nf = test::create_random_file(test_chunk_size * 2u); + auto &nf = test::create_random_file(test_chunk_size * 2u); mock_provider mp; mock_upload_manager um; @@ -449,7 +449,7 @@ TEST(open_file, write_new_file_multiple_chunks) { } TEST(open_file, resize_file_to_0_bytes) { - auto rf = test::create_random_file(test_chunk_size * 4u); + auto &rf = test::create_random_file(test_chunk_size * 4u); const auto source_path = rf.get_path().string(); rf.close(); @@ -499,7 +499,7 @@ TEST(open_file, resize_file_to_0_bytes) { } TEST(open_file, resize_file_by_full_chunk) { - auto rf = test::create_random_file(test_chunk_size * 4u); + auto &rf = test::create_random_file(test_chunk_size * 4u); const auto source_path = rf.get_path().string(); rf.close(); diff --git a/repertory/repertory_test/src/file_manager_ring_buffer_open_file_test.cpp b/repertory/repertory_test/src/file_manager_ring_buffer_open_file_test.cpp index cc3df4b7..97e82901 100644 --- a/repertory/repertory_test/src/file_manager_ring_buffer_open_file_test.cpp +++ b/repertory/repertory_test/src/file_manager_ring_buffer_open_file_test.cpp @@ -320,7 +320,7 @@ TEST(ring_buffer_open_file, can_reverse_full_ring) { } TEST(ring_buffer_open_file, read_full_file) { - auto nf = test::create_random_file(test_chunk_size * 32u); + auto &nf = test::create_random_file(test_chunk_size * 32u); const auto download_source_path = nf.get_path().string(); const auto dest_path = test::generate_test_file_name("ring_buffer_open_file"); @@ -352,7 +352,8 @@ TEST(ring_buffer_open_file, read_full_file) { file_manager::ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp, 8u); - auto nf2 = utils::file::file::open_or_create_file(dest_path); + auto ptr = utils::file::file::open_or_create_file(dest_path); + auto &nf2 = *ptr; EXPECT_TRUE(nf2); auto to_read = fsi.size; @@ -378,7 +379,7 @@ TEST(ring_buffer_open_file, read_full_file) { } TEST(ring_buffer_open_file, read_full_file_in_reverse) { - auto nf = test::create_random_file(test_chunk_size * 32u); + auto &nf = test::create_random_file(test_chunk_size * 32u); const auto download_source_path = nf.get_path().string(); const auto dest_path = test::generate_test_file_name("ring_buffer_open_file"); @@ -410,7 +411,8 @@ TEST(ring_buffer_open_file, read_full_file_in_reverse) { file_manager::ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp, 8u); - auto nf2 = utils::file::file::open_or_create_file(dest_path); + auto ptr = utils::file::file::open_or_create_file(dest_path); + auto &nf2 = *ptr; EXPECT_TRUE(nf2); auto to_read = fsi.size; @@ -436,7 +438,7 @@ TEST(ring_buffer_open_file, read_full_file_in_reverse) { } TEST(ring_buffer_open_file, read_full_file_in_partial_chunks) { - auto nf = test::create_random_file(test_chunk_size * 32u); + auto &nf = test::create_random_file(test_chunk_size * 32u); const auto download_source_path = nf.get_path().string(); const auto dest_path = test::generate_test_file_name("test"); @@ -468,7 +470,8 @@ TEST(ring_buffer_open_file, read_full_file_in_partial_chunks) { file_manager::ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp, 8u); - auto nf2 = utils::file::file::open_or_create_file(dest_path); + auto ptr = utils::file::file::open_or_create_file(dest_path); + auto &nf2 = *ptr; EXPECT_TRUE(nf2); // EXPECT_EQ(api_error::success, native_file::create_or_open(dest_path, // nf2)); @@ -495,7 +498,7 @@ TEST(ring_buffer_open_file, read_full_file_in_partial_chunks) { } TEST(ring_buffer_open_file, read_full_file_in_partial_chunks_in_reverse) { - auto nf = test::create_random_file(test_chunk_size * 32u); + auto &nf = test::create_random_file(test_chunk_size * 32u); const auto download_source_path = nf.get_path().string(); const auto dest_path = test::generate_test_file_name("ring_buffer_open_file"); @@ -527,7 +530,8 @@ TEST(ring_buffer_open_file, read_full_file_in_partial_chunks_in_reverse) { file_manager::ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp, 8u); - auto nf2 = utils::file::file::open_or_create_file(dest_path); + auto ptr = utils::file::file::open_or_create_file(dest_path); + auto &nf2 = *ptr; EXPECT_TRUE(nf2); std::uint64_t total_read{0U}; diff --git a/repertory/repertory_test/src/file_manager_test.cpp b/repertory/repertory_test/src/file_manager_test.cpp index 4c6804c3..717d3de3 100644 --- a/repertory/repertory_test/src/file_manager_test.cpp +++ b/repertory/repertory_test/src/file_manager_test.cpp @@ -425,7 +425,7 @@ TEST(file_manager, download_is_stored_after_write_if_partially_downloaded) { false, 1, "key", 2, now + 3u, 3u, 4u, utils::encryption::encrypting_reader::get_data_chunk_size() * 4u, source_path, 10, now + 4u); - auto nf = + auto &nf = test::create_random_file(utils::string::to_uint64(meta[META_SIZE])); EXPECT_CALL(mp, get_filesystem_item) @@ -586,7 +586,7 @@ TEST(file_manager, upload_occurs_after_write_if_fully_downloaded) { false, 1, "key", 2, now + 3u, 3u, 4u, utils::encryption::encrypting_reader::get_data_chunk_size() * 4u, source_path, 10, now + 4u); - auto nf = + auto &nf = test::create_random_file(utils::string::to_uint64(meta[META_SIZE])); EXPECT_CALL(mp, get_filesystem_item) @@ -1596,9 +1596,10 @@ TEST(file_manager, can_remove_file) { file_manager fm(cfg, mp); - auto file = utils::file::file::open_or_create_file("./test_remove.txt"); - EXPECT_TRUE(file); - file.close(); + { + auto file = utils::file::file::open_or_create_file("./test_remove.txt"); + EXPECT_TRUE(*file); + } EXPECT_TRUE(utils::file::is_file("./test_remove.txt")); EXPECT_CALL(mp, get_filesystem_item) diff --git a/repertory/repertory_test/src/remote_winfsp_test.cpp b/repertory/repertory_test/src/remote_winfsp_test.cpp index cf1a5449..bd55cd28 100644 --- a/repertory/repertory_test/src/remote_winfsp_test.cpp +++ b/repertory/repertory_test/src/remote_winfsp_test.cpp @@ -49,7 +49,8 @@ static void can_delete_test(remote_client &client) { auto api_path = utils::string::from_utf8(test_file).substr(mount_location_.size()); - auto nf = utils::file::file::open_or_create_file(test_file); + auto ptr = utils::file::file::open_or_create_file(test_file); + auto &nf = *ptr; EXPECT_TRUE(nf); if (nf) { EXPECT_EQ(STATUS_INVALID_HANDLE, diff --git a/scripts/env.sh b/scripts/env.sh index 75cfb19b..9104bc88 100755 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -40,6 +40,7 @@ done PROJECT_APP_LIST=() PROJECT_CMAKE_OPTS="" +PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF PROJECT_IS_ALPINE=0 PROJECT_IS_ARM64=0 PROJECT_MINGW64_COPY_DEPENDENCIES=() @@ -73,6 +74,10 @@ fi . "${PROJECT_SOURCE_DIR}/config.sh" +if [ "${PROJECT_IS_MINGW}" == "0" ]; then + PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF +fi + if [ "${PROJECT_ENABLE_SFML}" == "ON" ]; then PROJECT_ENABLE_FLAC=ON PROJECT_ENABLE_FONTCONFIG=ON @@ -223,6 +228,7 @@ PROJECT_CMAKE_OPTS="-DPROJECT_BUILD_DIR=${PROJECT_BUILD_DIR} ${PROJECT_CMAKE_OPT PROJECT_CMAKE_OPTS="-DPROJECT_BUILD_SHARED_LIBS=${PROJECT_BUILD_SHARED_LIBS} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_CMAKE_BUILD_TYPE=${PROJECT_CMAKE_BUILD_TYPE} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_DIST_DIR=${PROJECT_DIST_DIR} ${PROJECT_CMAKE_OPTS}" +PROJECT_CMAKE_OPTS="-DPROJECT_ENABLE_WIN32_LONG_PATH_NAMES=${PROJECT_ENABLE_WIN32_LONG_PATH_NAMES} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_EXTERNAL_BUILD_ROOT=${PROJECT_EXTERNAL_BUILD_ROOT} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_GIT_REV=${PROJECT_GIT_REV} ${PROJECT_CMAKE_OPTS}" PROJECT_CMAKE_OPTS="-DPROJECT_IS_ALPINE=${PROJECT_IS_ALPINE} ${PROJECT_CMAKE_OPTS}" @@ -288,6 +294,7 @@ export PROJECT_COMPANY_NAME export PROJECT_COPYRIGHT export PROJECT_DESC export PROJECT_DIST_DIR +export PROJECT_ENABLE_WIN32_LONG_PATH_NAMES export PROJECT_FILE_PART export PROJECT_GIT_REV export PROJECT_IS_ALPINE @@ -346,6 +353,9 @@ echo " Is MINGW on Unix: ${PROJECT_IS_MINGW_UNIX}" echo " Is MINGW: ${PROJECT_IS_MINGW}" echo " Job count: ${NUM_JOBS}" echo " Link type: ${PROJECT_LINK_TYPE}" +if [ "${PROJECT_IS_MINGW}" == "1" ]; then + echo " Long path names: ${PROJECT_ENABLE_WIN32_LONG_PATH_NAMES}" +fi echo " Meson toolchain file: ${PROJECT_TOOLCHAIN_FILE_MESON}" if [ "${PROJECT_IS_MINGW}" == "1" ] && [ "${PROJECT_IS_MINGW_UNIX}" == "1" ]; then echo " MinGW docker build args: ${PROJECT_MINGW64_DOCKER_BUILD_ARGS}" diff --git a/support/include/utils/config.hpp b/support/include/utils/config.hpp index c5e44ed4..cb8c1dab 100644 --- a/support/include/utils/config.hpp +++ b/support/include/utils/config.hpp @@ -307,8 +307,15 @@ using unique_mutex_lock = std::unique_lock; using unique_recur_mutex_lock = std::unique_lock; #if defined(_WIN32) +#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) +inline constexpr const auto max_path_length = std::size_t{32767U}; +#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) +inline constexpr const auto max_path_length = std::size_t{MAX_PATH}; +#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + using native_handle = HANDLE; #else // !defined(_WIN32) +inline constexpr const auto max_path_length = std::size_t{PATH_MAX}; using native_handle = int; #if !defined(INVALID_HANDLE_VALUE) #define INVALID_HANDLE_VALUE (-1) diff --git a/support/include/utils/encrypting_reader.hpp b/support/include/utils/encrypting_reader.hpp index e13f4d74..aaf085da 100644 --- a/support/include/utils/encrypting_reader.hpp +++ b/support/include/utils/encrypting_reader.hpp @@ -65,7 +65,7 @@ private: utils::encryption::hash_256_t key_; stop_type &stop_requested_; size_t error_return_; - utils::file::file source_file_; + std::unique_ptr source_file_; private: std::unordered_map chunk_buffers_; diff --git a/support/include/utils/file.hpp b/support/include/utils/file.hpp index e088407a..5a091a9d 100644 --- a/support/include/utils/file.hpp +++ b/support/include/utils/file.hpp @@ -24,104 +24,362 @@ #include "utils/config.hpp" +#include "utils/path.hpp" +#include + namespace repertory::utils::file { -class file final { +struct i_file { + virtual ~i_file() = default; + + virtual void close() = 0; + + [[nodiscard]] virtual auto exists() const -> bool = 0; + + virtual void flush() const = 0; + + [[nodiscard]] virtual auto get_handle() const -> native_handle = 0; + + [[nodiscard]] virtual auto get_path() const -> std::string = 0; + + [[nodiscard]] virtual auto get_read_buffer_size() const -> std::uint32_t = 0; + + [[nodiscard]] virtual auto is_read_only() const -> bool = 0; + + [[nodiscard]] virtual auto move_to(std::string_view new_path) -> bool = 0; + + [[nodiscard]] virtual auto + read(data_buffer &data, std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool = 0; + + [[nodiscard]] virtual auto + read(unsigned char *data, std::size_t to_read, std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool = 0; + + [[nodiscard]] virtual auto + read_all(data_buffer &data, std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool = 0; + + [[nodiscard]] virtual auto remove() -> bool = 0; + + virtual auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t = 0; + + [[nodiscard]] virtual auto size() const -> std::uint64_t = 0; + + [[nodiscard]] virtual auto truncate() -> bool = 0; + + [[nodiscard]] virtual auto truncate(std::size_t size) -> bool = 0; + + [[nodiscard]] virtual auto + write(const data_buffer &data, std::uint64_t offset, + std::size_t *total_written = nullptr) -> bool = 0; + + [[nodiscard]] virtual auto + write(const unsigned char *data, std::size_t to_write, std::size_t offset, + std::size_t *total_written = nullptr) -> bool = 0; + public: - [[nodiscard]] static auto attach_file(native_handle handle, - bool read_only = false) -> file; + [[nodiscard]] virtual operator bool() const = 0; - [[nodiscard]] static auto open_file(std::filesystem::path path, - bool read_only = false) -> file; +protected: + i_file() noexcept = default; - [[nodiscard]] static auto open_or_create_file(std::filesystem::path path, - bool read_only = false) -> file; + i_file(const i_file &) noexcept = delete; + + i_file(i_file &&) noexcept = delete; + + auto operator=(i_file &&) noexcept -> i_file & = delete; + + auto operator=(const i_file &) noexcept -> i_file & = delete; +}; + +class file final : public i_file { +public: + [[nodiscard]] static auto + attach_file(native_handle handle, + bool read_only = false) -> std::unique_ptr; + + [[nodiscard]] static auto + open_file(std::string_view path, + bool read_only = false) -> std::unique_ptr; + + [[nodiscard]] static auto + open_file(std::wstring_view path, + bool read_only = false) -> std::unique_ptr { + return open_file(utils::string::to_utf8(path), read_only); + } + + [[nodiscard]] static auto + open_or_create_file(std::string_view path, + bool read_only = false) -> std::unique_ptr; + + [[nodiscard]] static auto + open_or_create_file(std::wstring_view path, + bool read_only = false) -> std::unique_ptr { + return open_or_create_file(utils::string::to_utf8(path), read_only); + } public: file() noexcept = default; -protected: - file(file_t file_ptr, std::filesystem::path path) - : file_(std::move(file_ptr)), path_(std::move(path)) {} + file(std::string_view path) + : file_(nullptr), path_(utils::path::absolute(path)) {} + + file(std::wstring_view path) + : file_(nullptr), + path_(utils::path::absolute(utils::string::to_utf8(path))) {} + +private: + file(file_t file_ptr, std::string_view path, bool read_only) + : file_(std::move(file_ptr)), path_(path), read_only_(read_only) {} public: file(const file &) = delete; file(file &&move_file) noexcept : file_(std::move(move_file.file_)), - path_(std::move(move_file.path_)) + path_(std::move(move_file.path_)), + read_only_(move_file.read_only_) #if defined(_WIN32) , - mtx_() + mtx_(std::move(move_file.mtx_)) #endif // defined(_WIN32) { } - ~file() { close(); } + ~file() override { close(); } +private: + file_t file_; + std::string path_; + bool read_only_{false}; +#if defined(_WIN32) + mutable std::unique_ptr mtx_{ + new std::recursive_mutex(), + }; +#endif // defined(_WIN32) + +private: + std::atomic_uint32_t read_buffer_size{65536U}; + +private: + void open(); + +public: + void close() override; + + [[nodiscard]] auto exists() const -> bool override; + + void flush() const override; + + [[nodiscard]] auto get_handle() const -> native_handle override; + + [[nodiscard]] auto get_path() const -> std::string override { return path_; } + + [[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override { + return read_buffer_size; + } + + [[nodiscard]] auto is_read_only() const -> bool override { + return read_only_; + }; + + [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; + + [[nodiscard]] auto read(data_buffer &data, std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool override; + + [[nodiscard]] auto read(unsigned char *data, std::size_t to_read, + std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool override; + + [[nodiscard]] auto + read_all(data_buffer &data, std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool override; + + [[nodiscard]] auto remove() -> bool override; + + auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override { + read_buffer_size = size; + return read_buffer_size; + } + + [[nodiscard]] auto size() const -> std::uint64_t override; + + [[nodiscard]] auto truncate() -> bool override { return truncate(0U); } + + [[nodiscard]] auto truncate(std::size_t size) -> bool override; + + [[nodiscard]] auto + write(const data_buffer &data, std::uint64_t offset, + std::size_t *total_written = nullptr) -> bool override; + + [[nodiscard]] auto + write(const unsigned char *data, std::size_t to_write, std::size_t offset, + std::size_t *total_written = nullptr) -> bool override; + +public: auto operator=(const file &) noexcept -> file & = delete; auto operator=(file &&move_file) noexcept -> file & { if (&move_file != this) { file_ = std::move(move_file.file_); path_ = std::move(move_file.path_); + read_only_ = move_file.read_only_; +#if defined(_WIN32) + mtx_ = std::move(move_file.mtx_); +#endif // defined(_WIN32) } return *this; } -private: - file_t file_{nullptr}; - std::filesystem::path path_; -#if defined(_WIN32) - mutable std::recursive_mutex mtx_{}; -#endif // defined(_WIN32) + [[nodiscard]] operator bool() const override { return file_ != nullptr; } +}; + +class thread_file final : public i_file { +public: + [[nodiscard]] static auto + attach_file(native_handle handle, + bool read_only = false) -> std::unique_ptr; + + [[nodiscard]] static auto + open_file(std::string_view path, + bool read_only = false) -> std::unique_ptr; + + [[nodiscard]] static auto + open_file(std::wstring_view path, + bool read_only = false) -> std::unique_ptr { + return open_file(utils::string::to_utf8(path), read_only); + } + + [[nodiscard]] static auto + open_or_create_file(std::string_view path, + bool read_only = false) -> std::unique_ptr; + + [[nodiscard]] static auto + open_or_create_file(std::wstring_view path, + bool read_only = false) -> std::unique_ptr { + return open_or_create_file(utils::string::to_utf8(path), read_only); + } public: - void close(); + thread_file() noexcept = default; - void flush(); + thread_file(std::string_view path) : file_(new file(path)) {} - [[nodiscard]] auto get_handle() const -> native_handle; + thread_file(std::wstring_view path) + : file_(new file(utils::string::to_utf8(path))) {} - [[nodiscard]] auto get_path() const -> std::filesystem::path { return path_; } +protected: + thread_file(std::unique_ptr file); - [[nodiscard]] auto move_to(std::filesystem::path new_path) -> bool; +public: + thread_file(const thread_file &) = delete; + + thread_file(thread_file &&move_file) noexcept + : file_(std::move(move_file.file_)) {} + + ~thread_file() override { close(); } + +private: + std::unique_ptr file_; + +public: + void close() override; + + [[nodiscard]] auto exists() const -> bool override { return file_->exists(); } + + void flush() const override; + + [[nodiscard]] auto get_handle() const -> native_handle override { + return file_->get_handle(); + } + + [[nodiscard]] auto get_path() const -> std::string override { + return file_->get_path(); + } + + [[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override { + return file_->get_read_buffer_size(); + } + + [[nodiscard]] auto is_read_only() const -> bool override { + return file_->is_read_only(); + }; + + [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; [[nodiscard]] auto read(data_buffer &data, std::uint64_t offset, - std::size_t *total_read = nullptr) -> bool; + std::size_t *total_read = nullptr) -> bool override; [[nodiscard]] auto read(unsigned char *data, std::size_t to_read, std::uint64_t offset, - std::size_t *total_read = nullptr) -> bool; + std::size_t *total_read = nullptr) -> bool override; - [[nodiscard]] auto read_all(data_buffer &data, std::uint64_t offset, - std::size_t *total_read = nullptr) -> bool; + [[nodiscard]] auto + read_all(data_buffer &data, std::uint64_t offset, + std::size_t *total_read = nullptr) -> bool override; - [[nodiscard]] auto remove() -> bool; + [[nodiscard]] auto remove() -> bool override; - [[nodiscard]] auto size() const -> std::uint64_t; + auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override { + return file_->set_read_buffer_size(size); + } - [[nodiscard]] auto truncate() -> bool { return truncate(0U); } + [[nodiscard]] auto size() const -> std::uint64_t override; - [[nodiscard]] auto truncate(std::size_t size) -> bool; + [[nodiscard]] auto truncate() -> bool override { return truncate(0U); } - [[nodiscard]] auto write(const data_buffer &data, std::uint64_t offset, - std::size_t *total_written = nullptr) -> bool; + [[nodiscard]] auto truncate(std::size_t size) -> bool override; - [[nodiscard]] auto write(const unsigned char *data, std::size_t to_write, - std::size_t offset, - std::size_t *total_written = nullptr) -> bool; + [[nodiscard]] auto + write(const data_buffer &data, std::uint64_t offset, + std::size_t *total_written = nullptr) -> bool override; + + [[nodiscard]] auto + write(const unsigned char *data, std::size_t to_write, std::size_t offset, + std::size_t *total_written = nullptr) -> bool override; public: - [[nodiscard]] operator bool() const { return file_ != nullptr; } + [[nodiscard]] operator bool() const override { + return static_cast(*file_); + } + + auto operator=(const file &) noexcept -> thread_file & = delete; + + auto operator=(thread_file &&move_file) noexcept -> thread_file & { + if (&move_file != this) { + file_ = std::move(move_file.file_); + } + + return *this; + } }; +[[nodiscard]] auto create_directories(std::string_view path) -> bool; + +[[nodiscard]] auto create_directories(std::wstring_view path) -> bool; + +[[nodiscard]] auto +directory_exists_in_path(std::string_view path, + std::string_view sub_directory) -> bool; + +[[nodiscard]] auto +directory_exists_in_path(std::wstring_view path, + std::wstring_view sub_directory) -> bool; + [[nodiscard]] auto get_file_size(std::string_view path, std::uint64_t &file_size) -> bool; [[nodiscard]] auto get_file_size(std::wstring_view path, std::uint64_t &file_size) -> bool; +[[nodiscard]] auto file_exists_in_path(std::string_view path, + std::string_view file_name) -> bool; + +[[nodiscard]] auto file_exists_in_path(std::wstring_view path, + std::wstring_view file_name) -> bool; + [[nodiscard]] auto is_directory(std::string_view path) -> bool; [[nodiscard]] auto is_directory(std::wstring_view path) -> bool; @@ -130,6 +388,12 @@ public: [[nodiscard]] auto is_file(std::wstring_view path) -> bool; +[[nodiscard]] auto remove_directory(std::string_view path, + bool recursive = false) -> bool; + +[[nodiscard]] auto remove_directory(std::wstring_view path, + bool recursive = false) -> bool; + #if defined(PROJECT_ENABLE_JSON) #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) [[nodiscard]] auto @@ -161,6 +425,21 @@ read_json_file(std::string_view path, nlohmann::json &data, const nlohmann::json &data) -> bool; #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) #endif // defined(PROJECT_ENABLE_JSON) + +template +inline auto directory_exists_in_path_t( + std::basic_string_view path, + std::basic_string_view sub_directory) + -> bool { + return is_directory(utils::path::combine(path, {sub_directory})); +} + +template +inline auto file_exists_in_path_t( + std::basic_string_view path, + std::basic_string_view file_name) -> bool { + return is_file(utils::path::combine(path, {file_name})); +} } // namespace repertory::utils::file #endif // REPERTORY_INCLUDE_UTILS_FILE_HPP_ diff --git a/support/include/utils/path.hpp b/support/include/utils/path.hpp index 0b806c01..ef4695ce 100644 --- a/support/include/utils/path.hpp +++ b/support/include/utils/path.hpp @@ -30,8 +30,12 @@ inline constexpr const std::string_view backslash{"\\"}; inline constexpr const std::wstring_view backslash_w{L"\\"}; inline constexpr const std::string_view dot{"."}; inline constexpr const std::wstring_view dot_w{L"."}; +inline constexpr const std::string_view dot_backslash{".\\"}; +inline constexpr const std::wstring_view dot_backslash_w{L".\\"}; inline constexpr const std::string_view dot_slash{"./"}; inline constexpr const std::wstring_view dot_slash_w{L"./"}; +inline constexpr const std::string_view long_notation{"\\\\?\\"}; +inline constexpr const std::wstring_view long_notation_w{L"\\\\?\\"}; inline constexpr const std::string_view slash{"/"}; inline constexpr const std::wstring_view slash_w{L"/"}; #if defined(_WIN32) @@ -62,53 +66,6 @@ get_backslash() -> std::basic_string_view { return backslash_w; } -template -[[nodiscard]] inline constexpr auto get_dot() -> std::basic_string_view; - -template <> -[[nodiscard]] inline constexpr auto -get_dot() -> std::basic_string_view { - return dot; -} - -template <> -[[nodiscard]] inline constexpr auto -get_dot() -> std::basic_string_view { - return dot_w; -} - -template -[[nodiscard]] inline constexpr auto -get_dot_slash() -> std::basic_string_view; - -template <> -[[nodiscard]] inline constexpr auto -get_dot_slash() -> std::basic_string_view { - return dot_slash; -} - -template <> -[[nodiscard]] inline constexpr auto -get_dot_slash() -> std::basic_string_view { - return dot_slash_w; -} - -template -[[nodiscard]] inline constexpr auto -get_slash() -> std::basic_string_view; - -template <> -[[nodiscard]] inline constexpr auto -get_slash() -> std::basic_string_view { - return slash; -} - -template <> -[[nodiscard]] inline constexpr auto -get_slash() -> std::basic_string_view { - return slash_w; -} - template [[nodiscard]] inline constexpr auto get_directory_seperator() -> std::basic_string_view; @@ -141,16 +98,98 @@ get_not_directory_seperator() -> std::basic_string_view { return not_directory_seperator_w; } +template +[[nodiscard]] inline constexpr auto get_dot() -> std::basic_string_view; + +template <> +[[nodiscard]] inline constexpr auto +get_dot() -> std::basic_string_view { + return dot; +} + +template <> +[[nodiscard]] inline constexpr auto +get_dot() -> std::basic_string_view { + return dot_w; +} + +template +[[nodiscard]] inline constexpr auto +get_dot_backslash() -> std::basic_string_view; + +template <> +[[nodiscard]] inline constexpr auto +get_dot_backslash() -> std::basic_string_view { + return dot_backslash; +} + +template <> +[[nodiscard]] inline constexpr auto +get_dot_backslash() -> std::basic_string_view { + return dot_backslash_w; +} + +template +[[nodiscard]] inline constexpr auto +get_dot_slash() -> std::basic_string_view; + +template <> +[[nodiscard]] inline constexpr auto +get_dot_slash() -> std::basic_string_view { + return dot_slash; +} + +template <> +[[nodiscard]] inline constexpr auto +get_dot_slash() -> std::basic_string_view { + return dot_slash_w; +} + +template +[[nodiscard]] inline constexpr auto +get_long_notation() -> std::basic_string_view; + +template <> +[[nodiscard]] inline constexpr auto +get_long_notation() -> std::basic_string_view { + return long_notation; +} + +template <> +[[nodiscard]] inline constexpr auto +get_long_notation() -> std::basic_string_view { + return long_notation_w; +} + +template +[[nodiscard]] inline constexpr auto +get_slash() -> std::basic_string_view; + +template <> +[[nodiscard]] inline constexpr auto +get_slash() -> std::basic_string_view { + return slash; +} + +template <> +[[nodiscard]] inline constexpr auto +get_slash() -> std::basic_string_view { + return slash_w; +} + +template +[[nodiscard]] inline auto get_current_path() -> string_t; + [[nodiscard]] auto absolute(std::string_view path) -> std::string; [[nodiscard]] auto absolute(std::wstring_view path) -> std::wstring; [[nodiscard]] inline auto -combine(std::string path, +combine(std::string_view path, const std::vector &paths) -> std::string; [[nodiscard]] inline auto -combine(std::wstring path, +combine(std::wstring_view path, const std::vector &paths) -> std::wstring; [[nodiscard]] auto inline create_api_path(std::string_view path) -> std::string; @@ -158,27 +197,12 @@ combine(std::wstring path, [[nodiscard]] auto inline create_api_path(std::wstring_view path) -> std::wstring; -[[nodiscard]] inline auto -directory_exists_in_path(std::string_view path, - std::string_view sub_directory) -> bool; - -[[nodiscard]] inline auto -directory_exists_in_path(std::wstring_view path, - std::wstring_view sub_directory) -> bool; - -[[nodiscard]] inline auto -file_exists_in_path(std::string_view path, std::string_view file_name) -> bool; - -[[nodiscard]] inline auto -file_exists_in_path(std::wstring_view path, - std::wstring_view file_name) -> bool; - [[nodiscard]] inline auto finalize(std::string_view path) -> std::string; [[nodiscard]] inline auto finalize(std::wstring_view path) -> std::wstring; -[[nodiscard]] auto -find_program_in_path(const std::string &name_without_extension) -> std::string; +[[nodiscard]] auto find_program_in_path( + const std::string_view &name_without_extension) -> std::string; [[nodiscard]] auto find_program_in_path(std::wstring_view name_without_extension) -> std::wstring; @@ -208,10 +232,6 @@ get_parent_api_path(std::wstring_view path) -> std::wstring; [[nodiscard]] auto make_file_uri(std::wstring_view path) -> std::wstring; -[[nodiscard]] auto remove_file_name(std::string_view path) -> std::string; - -[[nodiscard]] auto remove_file_name(std::wstring_view path) -> std::wstring; - [[nodiscard]] auto strip_to_file_name(std::string path) -> std::string; [[nodiscard]] auto strip_to_file_name(std::wstring path) -> std::wstring; @@ -222,31 +242,30 @@ get_parent_api_path(std::wstring_view path) -> std::wstring; template [[nodiscard]] inline auto combine_t( - string_t path, + std::basic_string_view path, const std::vector> &paths) -> string_t { - path = std::accumulate( - paths.begin(), paths.end(), path, [](auto next_path, auto &&path_part) { - if (next_path.empty()) { - return string_t{path_part}; - } + auto dir_sep_t = + string_t{get_directory_seperator()}; + return absolute( + std::accumulate(paths.begin(), paths.end(), + std::basic_string{path}, + [&dir_sep_t](auto next_path, auto &&path_part) { + if (next_path.empty()) { + return string_t{path_part}; + } - return next_path + - string_t{ - get_directory_seperator()} + - string_t{path_part}; - }); - - return absolute(path); + return next_path + dir_sep_t + string_t{path_part}; + })); } -inline auto combine(std::string path, +inline auto combine(std::string_view path, const std::vector &paths) -> std::string { return combine_t(path, paths); } inline auto -combine(std::wstring path, +combine(std::wstring_view path, const std::vector &paths) -> std::wstring { return combine_t(path, paths); } @@ -255,12 +274,20 @@ template [[nodiscard]] inline auto create_api_path_t( std::basic_string_view path) -> string_t { auto backslash_t = get_backslash(); - auto dot_t = get_dot(); + auto dot_backslash_t = get_dot_backslash(); auto dot_slash_t = get_dot_slash(); + auto dot_t = get_dot(); auto slash_t = get_slash(); +#if defined(_WIN32) + auto long_notation_t = get_long_notation(); + if (utils::string::begins_with(path, long_notation_t)) { + path = path.substr(long_notation_t.size()); + } +#endif // defined(_WIN32) + if (path.empty() || path == backslash_t || path == dot_t || - path == dot_slash_t || path == slash_t) { + path == dot_slash_t || path == slash_t || path == dot_backslash_t) { return string_t{slash_t}; } @@ -292,50 +319,52 @@ inline auto create_api_path(std::wstring_view path) -> std::wstring { return create_api_path_t(path); } -template -[[nodiscard]] inline auto directory_exists_in_path_t( - std::basic_string_view path, - std::basic_string_view sub_directory) - -> bool { - return std::filesystem::is_directory( - std::filesystem::path(path).append(sub_directory)); -} - -inline auto directory_exists_in_path(std::string_view path, - std::string_view sub_directory) -> bool { - return directory_exists_in_path_t(path, sub_directory); -} - -inline auto directory_exists_in_path(std::wstring_view path, - std::wstring_view sub_directory) -> bool { - return directory_exists_in_path_t(path, sub_directory); -} - -template -[[nodiscard]] inline auto file_exists_in_path_t( - std::basic_string_view path, - std::basic_string_view file_name) -> bool { - return std::filesystem::is_regular_file( - std::filesystem::path(path).append(file_name)); -} - -inline auto file_exists_in_path(std::string_view path, - std::string_view file_name) -> bool { - return file_exists_in_path_t(path, file_name); -} - -inline auto file_exists_in_path(std::wstring_view path, - std::wstring_view file_name) -> bool { - return file_exists_in_path_t(path, file_name); -} - template [[nodiscard]] inline auto finalize_t( std::basic_string_view path) -> string_t { + string_t dir_sep_t{get_directory_seperator()}; string_t fmt_path{path}; - format_path(fmt_path, - get_directory_seperator(), + if (fmt_path.empty()) { + return fmt_path; + } + + format_path(fmt_path, dir_sep_t, get_not_directory_seperator()); + +#if defined(_WIN32) + auto dot_t = get_dot(); + auto dot_sep_t = string_t{dot_t} + dir_sep_t; + if (fmt_path == dot_t || fmt_path == dot_sep_t) { + return get_current_path(); + } + + if (fmt_path == dir_sep_t) { +#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + return get_current_path().substr(0U, long_notation.size() + 2U); +#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + return get_current_path().substr(0U, 2U); +#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + } + + if (utils::string::begins_with(fmt_path, dir_sep_t)) { +#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + return get_current_path().substr(0U, long_notation.size() + 2U) + + fmt_path; +#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + return get_current_path().substr(0U, 2U) + fmt_path; +#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + } + + if (utils::string::begins_with(fmt_path, dot_sep_t)) { + return get_current_path() + dir_sep_t + fmt_path.substr(2U); + } + +#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + return string_t{get_long_notation()} + + fmt_path; +#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) +#endif // defined(_WIN32) + return fmt_path; } @@ -355,6 +384,13 @@ format_path(string_t &path, -> string_t & { utils::string::replace(path, not_sep, sep); +#if defined(_WIN32) + auto long_notation_t = get_long_notation(); + if (utils::string::begins_with(path, long_notation_t)) { + path = path.substr(long_notation_t.size()); + } +#endif // defined(_WIN32) + string_t double_sep(2U, sep.at(0U)); while (utils::string::contains(path, double_sep)) { utils::string::replace(path, double_sep, sep); @@ -373,6 +409,24 @@ format_path(string_t &path, return path; } +template <> +[[nodiscard]] inline auto get_current_path() -> std::string { +#if defined(_WIN32) + std::string path; + path.resize(repertory::max_path_length + 1); + ::GetCurrentDirectoryA(static_cast(path.size()), path.data()); + path = path.c_str(); + return finalize(path); +#else // !defined(_WIN32) + return finalize(std::filesystem::current_path().string()); +#endif // defined(_WIN32) +} + +template <> +[[nodiscard]] inline auto get_current_path() -> std::wstring { + return utils::string::from_utf8(get_current_path()); +} + template [[nodiscard]] inline auto get_parent_api_path_t( std::basic_string_view path) -> string_t { @@ -383,12 +437,12 @@ template return ret; } - ret = path.substr(0, path.rfind('/') + 1); + ret = path.substr(0, path.rfind(slash_t) + 1); if (ret == slash_t) { return ret; } - return utils::string::right_trim(ret, '/'); + return utils::string::right_trim(ret, slash_t.at(0U)); } inline auto get_parent_api_path(std::string_view path) -> std::string { diff --git a/support/src/utils/encrypting_reader.cpp b/support/src/utils/encrypting_reader.cpp index b780ad27..bf69f239 100644 --- a/support/src/utils/encrypting_reader.cpp +++ b/support/src/utils/encrypting_reader.cpp @@ -178,7 +178,7 @@ encrypting_reader::encrypting_reader( stop_requested_(stop_requested), error_return_(error_return), source_file_(utils::file::file::open_or_create_file(source_path, true)) { - if (not source_file_) { + if (not *source_file_) { throw std::runtime_error("file open failed|src|" + std::string{source_path}); } @@ -190,16 +190,18 @@ encrypting_reader::encrypting_reader( encrypted_file_name_ = utils::collection::to_hex_string(result); if (relative_parent_path.has_value()) { - for (auto &&part : std::filesystem::path(relative_parent_path.value())) { + for (auto &&part : + utils::string::split(relative_parent_path.value(), + utils::path::directory_seperator, false)) { utils::encryption::encrypt_data( - key_, reinterpret_cast(part.string().c_str()), - strnlen(part.string().c_str(), part.string().size()), result); + key_, reinterpret_cast(part.c_str()), + strnlen(part.c_str(), part.size()), result); encrypted_file_path_ += '/' + utils::collection::to_hex_string(result); } encrypted_file_path_ += '/' + encrypted_file_name_; } - auto file_size = source_file_.size(); + auto file_size = source_file_->size(); const auto total_chunks = utils::divide_with_ceiling( file_size, static_cast(data_chunk_size_)); @@ -225,16 +227,15 @@ encrypting_reader::encrypting_reader(std::string_view encrypted_file_path, stop_requested_(stop_requested), error_return_(error_return), source_file_(utils::file::file::open_or_create_file(source_path, true)) { - if (not source_file_) { + if (not *source_file_) { throw std::runtime_error("file open failed|src|" + std::string{source_path}); } encrypted_file_path_ = encrypted_file_path; - encrypted_file_name_ = - std::filesystem::path(encrypted_file_path_).filename().string(); + encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_); - auto file_size = source_file_.size(); + auto file_size = source_file_->size(); const auto total_chunks = utils::divide_with_ceiling( file_size, static_cast(data_chunk_size_)); @@ -262,16 +263,15 @@ encrypting_reader::encrypting_reader( stop_requested_(stop_requested), error_return_(error_return), source_file_(utils::file::file::open_or_create_file(source_path, true)) { - if (not source_file_) { + if (not *source_file_) { throw std::runtime_error("file open failed|src|" + std::string{source_path}); } encrypted_file_path_ = encrypted_file_path; - encrypted_file_name_ = - std::filesystem::path(encrypted_file_path_).filename().string(); + encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_); - auto file_size = source_file_.size(); + auto file_size = source_file_->size(); const auto total_chunks = utils::divide_with_ceiling( file_size, static_cast(data_chunk_size_)); @@ -289,7 +289,7 @@ encrypting_reader::encrypting_reader(const encrypting_reader &reader) stop_requested_(reader.stop_requested_), error_return_(reader.error_return_), source_file_( - utils::file::file::open_file(reader.source_file_.get_path(), true)), + utils::file::file::open_file(reader.source_file_->get_path(), true)), chunk_buffers_(reader.chunk_buffers_), encrypted_file_name_(reader.encrypted_file_name_), encrypted_file_path_(reader.encrypted_file_path_), @@ -298,9 +298,9 @@ encrypting_reader::encrypting_reader(const encrypting_reader &reader) last_data_chunk_size_(reader.last_data_chunk_size_), read_offset_(reader.read_offset_), total_size_(reader.total_size_) { - if (not source_file_) { + if (not *source_file_) { throw std::runtime_error("file open failed|src|" + - source_file_.get_path().string()); + source_file_->get_path()); } } @@ -359,8 +359,8 @@ auto encrypting_reader::reader_function(char *buffer, size_t size, chunk_buffer.resize(file_data.size() + encryption_header_size); std::size_t bytes_read{}; - if ((ret = source_file_.read(file_data, chunk * data_chunk_size_, - &bytes_read))) { + if ((ret = source_file_->read(file_data, chunk * data_chunk_size_, + &bytes_read))) { utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data, chunk_buffer); } diff --git a/support/src/utils/file.cpp b/support/src/utils/file.cpp index d0d23f2c..390bf183 100644 --- a/support/src/utils/file.cpp +++ b/support/src/utils/file.cpp @@ -26,13 +26,129 @@ #include "utils/path.hpp" #include "utils/string.hpp" +namespace { +[[nodiscard]] auto remove_directory_recursively(std::string_view path) -> bool { +#if defined(_WIN32) + WIN32_FIND_DATA fd{}; + auto search = repertory::utils::path::combine(path, {"*.*"}); + auto find = ::FindFirstFileA(search.c_str(), &fd); + if (find != INVALID_HANDLE_VALUE) { + auto res{true}; + do { + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if ((std::string(fd.cFileName) != ".") && + (std::string(fd.cFileName) != "..")) { + res = remove_directory_recursively( + repertory::utils::path::combine(path, {fd.cFileName})); + } + } else { + res = repertory::utils::file::file( + repertory::utils::path::combine(path, {fd.cFileName})) + .remove(); + } + } while (res && (::FindNextFileA(find, &fd) != 0)); + + ::FindClose(find); + } +#else + auto *root = opendir(std::string{path}.c_str()); + if (root != nullptr) { + auto res{true}; + struct dirent *de{}; + while (res && (de = readdir(root))) { + if (de->d_type == DT_DIR) { + if ((strcmp(de->d_name, ".") != 0) && (strcmp(de->d_name, "..") != 0)) { + res = remove_directory_recursively( + repertory::utils::path::combine(path, {de->d_name})); + } + } else { + res = repertory::utils::file::file( + repertory::utils::path::combine(path, {de->d_name})) + .remove(); + } + } + + closedir(root); + } +#endif + + return repertory::utils::file::remove_directory(path, false); +} +} // namespace + namespace repertory::utils::file { +auto create_directories(std::string_view path) -> bool { +#if defined(_WIN32) + return is_directory(path) || + (::SHCreateDirectory( + nullptr, + utils::string::from_utf8(utils::path::absolute(path)).c_str()) == + ERROR_SUCCESS); +#else // !defined(_WIN32) + auto ret{true}; + auto paths = utils::string::split(utils::path::absolute(path), + utils::path::directory_seperator, false); + + std::string current_path; + for (std::size_t idx = 0U; ret && (idx < paths.size()); idx++) { + if (paths.at(idx).empty()) { + current_path = utils::path::directory_seperator; + continue; + } + + current_path = utils::path::combine(current_path, {paths.at(idx)}); + auto status = mkdir(current_path.c_str(), S_IRWXU); + ret = ((status == 0) || (errno == EEXIST)); + } + + return ret; +#endif +} + +auto create_directories(std::wstring_view path) -> bool { + return create_directories(utils::string::to_utf8(path)); +} + +auto directory_exists_in_path(std::string_view path, + std::string_view sub_directory) -> bool { + return directory_exists_in_path_t(path, sub_directory); +} + +auto directory_exists_in_path(std::wstring_view path, + std::wstring_view sub_directory) -> bool { + return directory_exists_in_path_t(path, sub_directory); +} + +auto file_exists_in_path(std::string_view path, + std::string_view file_name) -> bool { + return file_exists_in_path_t(path, file_name); +} + +auto file_exists_in_path(std::wstring_view path, + std::wstring_view file_name) -> bool { + return file_exists_in_path_t(path, file_name); +} + auto get_file_size(std::string_view path, std::uint64_t &file_size) -> bool { auto abs_path = utils::path::absolute(path); + file_size = 0U; +#if defined(_WIN32) + struct _stat64 st {}; + auto res = _stat64(std::string{path}.c_str(), &st); + if (res != 0) { + return false; + } + + file_size = static_cast(st.st_size); +#else // !defined(_WIN32) std::error_code ec{}; file_size = std::filesystem::file_size(abs_path, ec); - return ec.value() == 0; + if (not ec) { + return false; + } +#endif // defined(_WIN32) + return true; } auto get_file_size(std::wstring_view path, std::uint64_t &file_size) -> bool { @@ -70,6 +186,23 @@ auto is_file(std::wstring_view path) -> bool { return is_file(utils::string::to_utf8(path)); } +auto remove_directory(std::string_view path, bool recursive) -> bool { + auto abs_path = utils::path::absolute(path); + if (recursive) { + return remove_directory_recursively(abs_path); + } + +#if defined(_WIN32) + return (not is_directory(abs_path) || ::RemoveDirectoryA(abs_path.c_str())); +#else // !defined(_WIN32) + return not is_directory(abs_path) || (rmdir(abs_path.c_str()) == 0); +#endif // defined(_WIN32) +} + +auto remove_directory(std::wstring_view path, bool recursive) -> bool { + return remove_directory(utils::string::to_utf8(path), recursive); +} + #if defined(PROJECT_ENABLE_JSON) #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) auto read_json_file(std::string_view path, nlohmann::json &data, @@ -84,13 +217,13 @@ auto read_json_file(std::string_view path, nlohmann::json &data) -> bool { try { auto abs_path = utils::path::absolute(path); auto file = file::open_file(abs_path); - if (not file) { + if (not *file) { return false; } try { data_buffer buffer{}; - if (not file.read_all(buffer, 0U)) { + if (not file->read_all(buffer, 0U)) { return false; } @@ -141,7 +274,7 @@ auto write_json_file(std::string_view path, try { auto file = file::open_or_create_file(path); - if (not file.truncate()) { + if (not file->truncate()) { throw std::runtime_error("failed to truncate file"); } @@ -153,13 +286,14 @@ auto write_json_file(std::string_view path, utils::encryption::encrypt_data( *password, reinterpret_cast(str_data.c_str()), str_data.size(), encrypted_data); - return file.write(encrypted_data, 0U); + return file->write(encrypted_data, 0U); } #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) auto json_str = data.dump(); - return file.write(reinterpret_cast(json_str.c_str()), - json_str.size(), 0U); + return file->write( + reinterpret_cast(json_str.c_str()), + json_str.size(), 0U); } catch (const std::exception &e) { utils::error::handle_exception(function_name, e); } catch (...) { diff --git a/support/src/utils/file_file.cpp b/support/src/utils/file_file.cpp index 5bdecdac..3c484213 100644 --- a/support/src/utils/file_file.cpp +++ b/support/src/utils/file_file.cpp @@ -27,7 +27,8 @@ #include "utils/string.hpp" namespace repertory::utils::file { -auto file::attach_file(native_handle handle, bool read_only) -> file { +auto file::attach_file(native_handle handle, + bool read_only) -> std::unique_ptr { static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), }; @@ -36,13 +37,12 @@ auto file::attach_file(native_handle handle, bool read_only) -> file { std::string path; #if defined(_WIN32) - path.resize(MAX_PATH + 1); - + path.resize(repertory::max_path_length + 1); ::GetFinalPathNameByHandleA(handle, path.data(), static_cast(path.size()), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); #else // !defined(_WIN32) - path.resize(PATH_MAX + 1); + path.resize(repertory::max_path_length + 1); #if defined(__APPLE__) fcntl(handle, F_GETPATH, source_path.data()); @@ -63,54 +63,60 @@ auto file::attach_file(native_handle handle, bool read_only) -> file { auto *ptr = fdopen(handle, read_only ? "rb" : "rb+"); #endif // defined(_WIN32) - return file{ + return std::unique_ptr(new file{ file_t{ptr}, utils::path::absolute(path), - }; + read_only, + }); } catch (const std::exception &e) { utils::error::handle_exception(function_name, e); } catch (...) { utils::error::handle_exception(function_name); } - return {}; + return nullptr; } -auto file::open_file(std::filesystem::path path, bool read_only) -> file { +void file::open() { + if (not is_file(path_)) { + throw std::runtime_error("file not found: " + path_); + } + +#if defined(_WIN32) + file_.reset(_fsopen(path_.c_str(), read_only_ ? "rb" : "rb+", _SH_DENYNO)); +#else // !defined(_WIN32) + file_.reset(fopen(path_.c_str(), read_only_ ? "rb" : "rb+")); +#endif // defined(_WIN32) +} + +auto file::open_file(std::string_view path, + bool read_only) -> std::unique_ptr { static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), }; + auto *ptr = new file{ + nullptr, + utils::path::absolute(path), + read_only, + }; + auto new_file = std::unique_ptr(ptr); + try { - path = utils::path::absolute(path.string()); - if (not is_file(path.string())) { - throw std::runtime_error("file not found: " + path.string()); - } - -#if defined(_WIN32) - auto *ptr = - _fsopen(path.string().c_str(), read_only ? "rb" : "rb+", _SH_DENYNO); -#else // !defined(_WIN32) - auto *ptr = fopen(path.string().c_str(), read_only ? "rb" : "rb+"); -#endif // defined(_WIN32) - - return file{ - file_t{ptr}, - path, - }; + ptr->open(); } catch (const std::exception &e) { utils::error::handle_exception(function_name, e); } catch (...) { utils::error::handle_exception(function_name); } - return {}; + return new_file; } -auto file::open_or_create_file(std::filesystem::path path, - bool read_only) -> file { - path = utils::path::absolute(path.string()); - if (not utils::file::is_file(path.string())) { +auto file::open_or_create_file(std::string_view path, + bool read_only) -> std::unique_ptr { + auto abs_path = utils::path::absolute(path); + if (not is_file(abs_path)) { #if defined(_WIN32) int old_mode{}; _umask_s(077, &old_mode); @@ -119,36 +125,44 @@ auto file::open_or_create_file(std::filesystem::path path, #endif // defined(_WIN32) #if defined(_WIN32) - auto *ptr = _fsopen(path.string().c_str(), "ab+", _SH_DENYNO); + auto *ptr = _fsopen(abs_path.c_str(), "ab+", _SH_DENYNO); #else // !defined(_WIN32) - auto *ptr = fopen(path.string().c_str(), "ab+"); + auto *ptr = fopen(abs_path.c_str(), "ab+"); #endif // defined(_WIN32) + if (ptr != nullptr) { + fclose(ptr); + } + #if defined(_WIN32) _umask_s(old_mode, nullptr); #else // !defined(_WIN32) umask(old_mode); #endif // defined(_WIN32) - - if (ptr != nullptr) { - fclose(ptr); - } } - return open_file(path, read_only); + return open_file(abs_path, read_only); } void file::close() { #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) file_.reset(); } -void file::flush() { +auto file::exists() const -> bool { #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; +#endif // defined(_WIN32) + + return is_file(path_); +} + +void file::flush() const { +#if defined(_WIN32) + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) if (file_) { @@ -157,6 +171,10 @@ void file::flush() { } auto file::get_handle() const -> native_handle { +#if defined(_WIN32) + recur_mutex_lock lock{*mtx_}; +#endif // defined(_WIN32) + if (file_) { #if defined(_WIN32) return reinterpret_cast( @@ -169,12 +187,16 @@ auto file::get_handle() const -> native_handle { return INVALID_HANDLE_VALUE; } -auto file::move_to(std::filesystem::path new_path) -> bool { +auto file::move_to(std::string_view path) -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) - new_path = utils::path::absolute(new_path.string()); + auto abs_path = utils::path::absolute(path); auto reopen{false}; if (file_) { @@ -182,19 +204,29 @@ auto file::move_to(std::filesystem::path new_path) -> bool { close(); } + auto success{false}; +#if defined(_WIN32) + success = !!::MoveFileExA(path_.c_str(), abs_path.c_str(), + MOVEFILE_REPLACE_EXISTING); +#else // !// defined(_WIN32) std::error_code ec{}; - std::filesystem::rename(path_, new_path, ec); - if (not ec) { - path_ = new_path; - if (reopen) { - *this = open_file(path_); - } + std::filesystem::rename(path_, abs_path, ec); + success = ec.value() == 0; +#endif // defined(_WIN32) - return true; + if (success) { + path_ = abs_path; } if (reopen) { - *this = open_file(path_); + try { + open(); + return success; + } catch (const std::exception &e) { + utils::error::handle_exception(function_name, e); + } catch (...) { + utils::error::handle_exception(function_name); + } } return false; @@ -203,11 +235,11 @@ auto file::move_to(std::filesystem::path new_path) -> bool { auto file::read_all(data_buffer &data, std::uint64_t offset, std::size_t *total_read) -> bool { #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) data_buffer buffer; - buffer.resize(65536U); + buffer.resize(read_buffer_size); std::size_t current_read{}; while (read(reinterpret_cast(buffer.data()), @@ -237,7 +269,7 @@ auto file::read_all(data_buffer &data, std::uint64_t offset, auto file::read(data_buffer &data, std::uint64_t offset, std::size_t *total_read) -> bool { #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) std::size_t bytes_read{}; @@ -255,14 +287,14 @@ auto file::read(data_buffer &data, std::uint64_t offset, auto file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset, std::size_t *total_read) -> bool { -#if defined(_WIN32) - recur_mutex_lock lock{mtx_}; -#endif // defined(_WIN32) - static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), }; +#if defined(_WIN32) + recur_mutex_lock lock{*mtx_}; +#endif // defined(_WIN32) + if (total_read != nullptr) { (*total_read) = 0U; } @@ -298,18 +330,30 @@ auto file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset, auto file::remove() -> bool { #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) close(); + if (not exists()) { + return true; + } + +#if defined(_WIN32) + return !!::DeleteFileA(path_.c_str()); +#else // !defined(_WIN32) std::error_code ec{}; return std::filesystem::remove(path_, ec); +#endif // defined(_WIN32) } auto file::truncate(std::size_t size) -> bool { + static constexpr const std::string_view function_name{ + static_cast(__FUNCTION__), + }; + #if defined(_WIN32) - recur_mutex_lock lock{mtx_}; + recur_mutex_lock lock{*mtx_}; #endif // defined(_WIN32) auto reopen{false}; @@ -320,23 +364,34 @@ auto file::truncate(std::size_t size) -> bool { std::error_code ec{}; std::filesystem::resize_file(path_, size, ec); + + auto success{ec.value() == 0}; + if (reopen) { - *this = open_file(path_); + try { + open(); + } catch (const std::exception &e) { + utils::error::handle_exception(function_name, e); + success = false; + } catch (...) { + utils::error::handle_exception(function_name); + success = false; + } } - return ec.value() == 0; + return success; } auto file::write(const unsigned char *data, std::size_t to_write, std::size_t offset, std::size_t *total_written) -> bool { -#if defined(_WIN32) - recur_mutex_lock lock{mtx_}; -#endif // defined(_WIN32) - static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), }; +#if defined(_WIN32) + recur_mutex_lock lock{*mtx_}; +#endif // defined(_WIN32) + if (total_written != nullptr) { (*total_written) = 0U; } @@ -374,14 +429,14 @@ auto file::write(const unsigned char *data, std::size_t to_write, } auto file::size() const -> std::uint64_t { -#if defined(_WIN32) - recur_mutex_lock lock{mtx_}; -#endif // defined(_WIN32) - static constexpr const std::string_view function_name{ static_cast(__FUNCTION__), }; +#if defined(_WIN32) + recur_mutex_lock lock{*mtx_}; +#endif // defined(_WIN32) + try { if (file_) { if (fseeko(file_.get(), 0, SEEK_END) == -1) { @@ -395,6 +450,13 @@ auto file::size() const -> std::uint64_t { return static_cast(size); } + + std::uint64_t size{}; + if (not get_file_size(path_, size)) { + throw std::runtime_error("failed to get file size"); + } + + return size; } catch (const std::exception &e) { utils::error::handle_exception(function_name, e); } catch (...) { diff --git a/support/src/utils/file_thread_file.cpp b/support/src/utils/file_thread_file.cpp new file mode 100644 index 00000000..182ab3c8 --- /dev/null +++ b/support/src/utils/file_thread_file.cpp @@ -0,0 +1,64 @@ +/* + Copyright <2018-2024> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "utils/file.hpp" + +namespace repertory::utils::file { +auto thread_file::attach_file(native_handle handle, + bool read_only) -> std::unique_ptr {} + +auto thread_file::open_file(std::string_view path, + bool read_only) -> std::unique_ptr {} + +auto thread_file::open_or_create_file(std::string_view path, bool read_only) + -> std::unique_ptr {} + +thread_file::thread_file(std::unique_ptr file) + : file_(std::move(file)) {} + +void thread_file::close() {} + +void thread_file::flush() const {} + +auto thread_file::move_to(std::string_view path) -> bool {} + +auto thread_file::read_all(data_buffer &data, std::uint64_t offset, + std::size_t *total_read) -> bool {} + +auto thread_file::read(data_buffer &data, std::uint64_t offset, + std::size_t *total_read) -> bool {} + +auto thread_file::read(unsigned char *data, std::size_t to_read, + std::uint64_t offset, std::size_t *total_read) -> bool {} + +auto thread_file::remove() -> bool {} + +auto thread_file::truncate(std::size_t size) -> bool {} + +auto thread_file::write(const unsigned char *data, std::size_t to_write, + std::size_t offset, + std::size_t *total_written) -> bool {} + +auto thread_file::size() const -> std::uint64_t {} + +auto thread_file::write(const data_buffer &data, std::uint64_t offset, + std::size_t *total_written) -> bool {} +} // namespace repertory::utils::file diff --git a/support/src/utils/path.cpp b/support/src/utils/path.cpp index 92af6b0d..bde5ca79 100644 --- a/support/src/utils/path.cpp +++ b/support/src/utils/path.cpp @@ -23,18 +23,11 @@ #include "utils/common.hpp" #include "utils/error.hpp" +#include "utils/file.hpp" #include "utils/string.hpp" #include "utils/unix.hpp" namespace { -static const std::string directory_seperator_str{ - repertory::utils::path::directory_seperator, -}; - -static const std::wstring directory_seperator_str_w{ - repertory::utils::path::directory_seperator_w, -}; - [[nodiscard]] auto resolve(std::string path) -> std::string { #if defined(_WIN32) if (repertory::utils::string::contains(path, "~") || @@ -52,12 +45,17 @@ static const std::wstring directory_seperator_str_w{ #else // !defined (_WIN32) if (repertory::utils::string::contains(path, "~")) { std::string home{}; - repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) { - home = (pw->pw_dir ? pw->pw_dir : ""); - if (home.empty() || ((home == "/") && (getuid() != 0))) { - home = repertory::utils::path::combine("/home", {pw->pw_name}); - } - }); + auto res = + repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) { + home = (pw->pw_dir ? pw->pw_dir : ""); + if (home.empty() || + ((home == repertory::utils::path::slash) && (getuid() != 0))) { + home = repertory::utils::path::combine("/home", {pw->pw_name}); + } + }); + if (res) { + throw std::runtime_error("failed to getpwuid: " + res.reason); + } return repertory::utils::string::replace(path, "~", home); } @@ -70,36 +68,44 @@ static const std::wstring directory_seperator_str_w{ namespace repertory::utils::path { auto absolute(std::string_view path) -> std::string { std::string abs_path{path}; - abs_path = resolve(abs_path); - format_path(abs_path, directory_seperator, not_directory_seperator); + if (abs_path.empty()) { + return abs_path; + } + abs_path = finalize(resolve(abs_path)); #if defined(_WIN32) - if (not abs_path.empty() && ::PathIsRelativeA(abs_path.c_str())) { - std::string temp; - temp.resize(MAX_PATH + 1); - abs_path = _fullpath(temp.data(), abs_path.c_str(), MAX_PATH); + if (not utils::string::contains(abs_path, dot)) { + return abs_path; } + + std::string temp; + temp.resize(repertory::max_path_length + 1); + ::GetFullPathNameA(abs_path.c_str(), static_cast(temp.size()), + temp.data(), nullptr); #else // !defined(_WIN32) - if (not abs_path.empty() && (abs_path.at(0U) != '/')) { - auto found{false}; - std::string tmp{abs_path}; - do { - auto *res = realpath(tmp.c_str(), nullptr); - if (res != nullptr) { - abs_path = res + std::string{directory_seperator} + - abs_path.substr(tmp.size()); - free(res); - found = true; - } else if (tmp == ".") { - found = true; - } else { - tmp = dirname(tmp.data()); - } - } while (not found); + if (not utils::string::contains(abs_path, dot) || + utils::string::begins_with(abs_path, slash)) { + return abs_path; } + + auto found{false}; + std::string tmp{abs_path}; + do { + auto *res = realpath(tmp.c_str(), nullptr); + if (res != nullptr) { + abs_path = + res + std::string{directory_seperator} + abs_path.substr(tmp.size()); + free(res); + found = true; + } else if (tmp == dot) { + found = true; + } else { + tmp = dirname(tmp.data()); + } + } while (not found); #endif // defined(_WIN32) - return format_path(abs_path, directory_seperator, not_directory_seperator); + return finalize(abs_path); } auto absolute(std::wstring_view path) -> std::wstring { @@ -142,7 +148,7 @@ auto find_program_in_path(const std::string &name_without_extension) for (auto &&extension : extension_list) { auto exec_path = combine( search_path, {name_without_extension + std::string{extension}}); - if (std::filesystem::exists(exec_path)) { + if (utils::file::is_file(exec_path)) { found_items[name_without_extension] = exec_path; return exec_path; } @@ -159,14 +165,16 @@ find_program_in_path(std::wstring_view name_without_extension) -> std::wstring { } auto get_parent_directory(std::string_view path) -> std::string { - auto ret = std::filesystem::path{path}.parent_path().string(); -#if !defined(_WIN32) - if (ret == ".") { - ret = "/"; - } -#endif // !defined(_WIN32) + auto abs_path = absolute(path); - return absolute(ret); +#if defined(_WIN32) + ::PathRemoveFileSpecA(abs_path.data()); + abs_path = abs_path.c_str(); +#else // !defined(_WIN32) + abs_path = std::filesystem::path{abs_path}.parent_path().string(); +#endif // defined(_WIN32) + + return finalize(abs_path); } auto get_parent_directory(std::wstring_view path) -> std::wstring { @@ -175,13 +183,12 @@ auto get_parent_directory(std::wstring_view path) -> std::wstring { } auto is_trash_directory(std::string_view path) -> bool { + std::string dir_sep_t{get_directory_seperator()}; + auto trash_path = utils::string::to_lower(absolute(path)); - return utils::string::begins_with(trash_path, - directory_seperator_str + ".trash-") || - utils::string::begins_with(trash_path, - directory_seperator_str + ".trashes") || - utils::string::begins_with(trash_path, - directory_seperator_str + "$recycle.bin"); + return utils::string::begins_with(trash_path, dir_sep_t + ".trash-") || + utils::string::begins_with(trash_path, dir_sep_t + ".trashes") || + utils::string::begins_with(trash_path, dir_sep_t + "$recycle.bin"); } auto is_trash_directory(std::wstring_view path) -> bool { @@ -191,8 +198,8 @@ auto is_trash_directory(std::wstring_view path) -> bool { auto make_file_uri(std::string_view path) -> std::string { auto abs_path = absolute(path); #if defined(_WIN32) - utils::string::replace(abs_path, '\\', '/'); - abs_path = '/' + abs_path; + utils::string::replace(abs_path, backslash, slash); + abs_path = std::string{slash} + abs_path; #endif // defined(_WIN32) return "file://" + abs_path; } @@ -201,31 +208,11 @@ auto make_file_uri(std::wstring_view path) -> std::wstring { return utils::string::from_utf8(make_file_uri(utils::string::to_utf8(path))); } -auto remove_file_name(std::string_view path) -> std::string { - auto abs_path = absolute(path); - -#if defined(_WIN32) - ::PathRemoveFileSpecA(abs_path.data()); - abs_path = abs_path.c_str(); -#else // !defined(_WIN32) - if (abs_path != "/") { - auto idx{abs_path.size() - 1U}; - while ((idx != 0U) && (abs_path.at(idx) != '/')) { - idx--; - } - - abs_path = (idx > 0U) ? absolute(abs_path.substr(0U, idx)) : "/"; - } -#endif // defined(_WIN32) - - return abs_path; -} - auto strip_to_file_name(std::string path) -> std::string { #if defined(_WIN32) return ::PathFindFileNameA(path.c_str()); #else // !defined(_WIN32) - return utils::string::contains(path, "/") ? basename(path.data()) : path; + return utils::string::contains(path, slash) ? basename(path.data()) : path; #endif // defined(_WIN32) } diff --git a/support/src/utils/windows.cpp b/support/src/utils/windows.cpp index 3b19af33..eb01fd02 100644 --- a/support/src/utils/windows.cpp +++ b/support/src/utils/windows.cpp @@ -76,7 +76,7 @@ auto run_process_elevated(std::vector args) -> int { } std::string full_path; - full_path.resize(MAX_PATH + 1); + full_path.resize(repertory::max_path_length + 1); if (::GetModuleFileNameA(nullptr, full_path.data(), MAX_PATH)) { SHELLEXECUTEINFOA sei{}; diff --git a/support/test/include/test.hpp b/support/test/include/test.hpp index 85645e35..9ad4e232 100644 --- a/support/test/include/test.hpp +++ b/support/test/include/test.hpp @@ -38,7 +38,8 @@ using namespace ::testing; namespace repertory::test { #if defined(PROJECT_ENABLE_LIBSODIUM) -[[nodiscard]] auto create_random_file(std::size_t size) -> utils::file::file; +[[nodiscard]] auto +create_random_file(std::size_t size) -> utils::file::i_file &; #endif // defined(PROJECT_ENABLE_LIBSODIUM) [[nodiscard]] auto diff --git a/support/test/src/test.cpp b/support/test/src/test.cpp index cb7e3651..46188dcc 100644 --- a/support/test/src/test.cpp +++ b/support/test/src/test.cpp @@ -24,24 +24,24 @@ namespace { static std::recursive_mutex file_mtx{}; -static std::vector generated_files; +static std::vector> + generated_files{}; static void delete_generated_files() { repertory::recur_mutex_lock lock{file_mtx}; std::optional parent_path; for (auto &&path : generated_files) { if (parent_path->empty()) { - parent_path = std::filesystem::path(path).parent_path().string(); + parent_path = + repertory::utils::path::get_parent_directory(path->get_path()); } - std::error_code ec{}; - std::filesystem::remove(path, ec); + [[maybe_unused]] auto removed = path->remove(); } generated_files.clear(); if (parent_path.has_value()) { - std::error_code ec{}; - std::filesystem::remove_all(*parent_path, ec); + EXPECT_TRUE(repertory::utils::file::remove_directory(*parent_path, true)); } } @@ -54,26 +54,25 @@ static auto deleter{std::make_unique()}; namespace repertory::test { #if defined(PROJECT_ENABLE_LIBSODIUM) -auto create_random_file(std::size_t size) -> utils::file::file { +auto create_random_file(std::size_t size) -> utils::file::i_file & { recur_mutex_lock lock{file_mtx}; auto path = generate_test_file_name("random"); auto file = utils::file::file::open_or_create_file(path); - EXPECT_TRUE(file); - if (file) { + EXPECT_TRUE(*file); + if (*file) { data_buffer buf(size); randombytes_buf(buf.data(), buf.size()); std::size_t bytes_written{}; - EXPECT_TRUE(file.write(buf, 0U, &bytes_written)); + EXPECT_TRUE(file->write(buf, 0U, &bytes_written)); EXPECT_EQ(size, bytes_written); - EXPECT_EQ(size, file.size()); - - generated_files.emplace_back(path); + EXPECT_EQ(size, file->size()); } - return file; + generated_files.emplace_back(std::move(file)); + return *generated_files.back(); } #endif // defined(PROJECT_ENABLE_LIBSODIUM) @@ -86,8 +85,9 @@ auto generate_test_file_name(std::string_view file_name_no_extension) std::string{file_name_no_extension} + std::to_string(generated_files.size()), }); - generated_files.emplace_back(path); - return path; + generated_files.emplace_back( + std::unique_ptr(new utils::file::file{path})); + return generated_files.back()->get_path(); } auto get_test_input_dir() -> std::string { @@ -111,7 +111,7 @@ auto get_test_output_dir() -> std::string { #endif // defined(_WIN32) if (not utils::file::is_directory(path)) { - std::filesystem::create_directories(path); + EXPECT_TRUE(utils::file::create_directories(path)); } return path; diff --git a/support/test/src/utils/common_test.cpp b/support/test/src/utils/common_test.cpp index abb2ce54..f07eabb3 100644 --- a/support/test/src/utils/common_test.cpp +++ b/support/test/src/utils/common_test.cpp @@ -273,7 +273,7 @@ TEST(utils_common, get_environment_variable) { std::string path; #if defined(_WIN32) - path.resize(MAX_PATH + 1U); + path.resize(repertory::max_path_length + 1U); auto size = ::GetEnvironmentVariableA(path_env.c_str(), path.data(), 0U); path.resize(size); diff --git a/support/test/src/utils/encrypting_reader_test.cpp b/support/test/src/utils/encrypting_reader_test.cpp index 536177b2..e273abba 100644 --- a/support/test/src/utils/encrypting_reader_test.cpp +++ b/support/test/src/utils/encrypting_reader_test.cpp @@ -25,13 +25,13 @@ namespace repertory { TEST(utils_encrypting_reader, read_file_data) { const auto token = std::string("moose"); - auto source_file = test::create_random_file( + auto &source_file = test::create_random_file( 8U * utils::encryption::encrypting_reader::get_data_chunk_size()); EXPECT_TRUE(source_file); if (source_file) { stop_type stop_requested{false}; utils::encryption::encrypting_reader reader( - "test.dat", source_file.get_path().string(), stop_requested, token); + "test.dat", source_file.get_path(), stop_requested, token); for (std::uint8_t i = 0U; i < 8U; i++) { data_buffer buffer( @@ -65,13 +65,13 @@ TEST(utils_encrypting_reader, read_file_data) { TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) { const auto token = std::string("moose"); - auto source_file = test::create_random_file( + auto &source_file = test::create_random_file( 8U * utils::encryption::encrypting_reader::get_data_chunk_size()); EXPECT_TRUE(source_file); if (source_file) { stop_type stop_requested{false}; utils::encryption::encrypting_reader reader( - "test.dat", source_file.get_path().string(), stop_requested, token); + "test.dat", source_file.get_path(), stop_requested, token); for (std::uint8_t i = 0U; i < 8U; i += 2U) { data_buffer buffer( @@ -113,13 +113,13 @@ TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) { TEST(utils_encrypting_reader, read_file_data_as_stream) { const auto token = std::string("moose"); - auto source_file = test::create_random_file( + auto &source_file = test::create_random_file( 8U * utils::encryption::encrypting_reader::get_data_chunk_size()); EXPECT_TRUE(source_file); if (source_file) { stop_type stop_requested{false}; utils::encryption::encrypting_reader reader( - "test.dat", source_file.get_path().string(), stop_requested, token); + "test.dat", source_file.get_path(), stop_requested, token); auto io_stream = reader.create_iostream(); EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); EXPECT_TRUE(io_stream->good()); @@ -166,13 +166,13 @@ TEST(utils_encrypting_reader, read_file_data_as_stream) { TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks_as_stream) { const auto token = std::string("moose"); - auto source_file = test::create_random_file( + auto &source_file = test::create_random_file( 8u * utils::encryption::encrypting_reader::get_data_chunk_size()); EXPECT_TRUE(source_file); if (source_file) { stop_type stop_requested{false}; utils::encryption::encrypting_reader reader( - "test.dat", source_file.get_path().string(), stop_requested, token); + "test.dat", source_file.get_path(), stop_requested, token); auto io_stream = reader.create_iostream(); EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); EXPECT_TRUE(io_stream->good()); diff --git a/support/test/src/utils/file_test.cpp b/support/test/src/utils/file_test.cpp index 65f195d8..fba27102 100644 --- a/support/test/src/utils/file_test.cpp +++ b/support/test/src/utils/file_test.cpp @@ -21,57 +21,81 @@ */ #include "test.hpp" +namespace { +static constexpr const auto file_type_count{1U}; +} + namespace repertory { TEST(utils_file, can_create_file) { - auto path = test::generate_test_file_name("utils_file"); - EXPECT_FALSE(utils::file::is_file(path) || utils::file::is_directory(path)); + for (auto idx = 0U; idx < file_type_count; ++idx) { + auto path = test::generate_test_file_name("utils_file"); + EXPECT_FALSE(utils::file::is_file(path) || utils::file::is_directory(path)); - auto file = utils::file::file::open_or_create_file(path); - EXPECT_TRUE(file); + auto file = idx == 0U ? utils::file::file::open_or_create_file(path) + : utils::file::thread_file::open_or_create_file(path); + EXPECT_TRUE(*file); - EXPECT_TRUE(utils::file::is_file(path)); + EXPECT_TRUE(utils::file::is_file(path)); + } } TEST(utils_file, can_open_file) { - auto path = test::generate_test_file_name("utils_file"); + for (auto idx = 0U; idx < file_type_count; ++idx) { + auto path = test::generate_test_file_name("utils_file"); - { - auto file = utils::file::file::open_or_create_file(path); - EXPECT_TRUE(file); - } + { + auto file = idx == 0U + ? utils::file::file::open_or_create_file(path) + : utils::file::thread_file::open_or_create_file(path); + EXPECT_TRUE(*file); + } - { - auto file = utils::file::file::open_file(path); - EXPECT_TRUE(file); + { + auto file = idx == 0U ? utils::file::file::open_file(path) + : utils::file::thread_file::open_file(path); + EXPECT_TRUE(*file); + } } } TEST(utils_file, open_file_fails_if_not_found) { - auto path = test::generate_test_file_name("utils_file"); + for (auto idx = 0U; idx < file_type_count; ++idx) { + auto path = test::generate_test_file_name("utils_file"); - auto file = utils::file::file::open_file(path); - EXPECT_FALSE(file); + auto file = idx == 0U ? utils::file::file::open_file(path) + : utils::file::thread_file::open_file(path); + EXPECT_FALSE(*file); + } } TEST(utils_file, write_fails_for_read_only_file) { - auto path = test::generate_test_file_name("utils_file"); + for (auto idx = 0U; idx < file_type_count; ++idx) { + auto path = test::generate_test_file_name("utils_file"); - auto file = utils::file::file::open_or_create_file(path, true); - EXPECT_TRUE(utils::file::is_file(path)); - EXPECT_TRUE(file); - std::size_t bytes_written{}; - EXPECT_FALSE(file.write(reinterpret_cast("0"), 1U, 0U, - &bytes_written)); - EXPECT_EQ(0U, bytes_written); + auto file = idx == 0U + ? utils::file::file::open_or_create_file(path, true) + : utils::file::thread_file::open_or_create_file(path, true); + EXPECT_TRUE(utils::file::is_file(path)); + EXPECT_TRUE(*file); + std::size_t bytes_written{}; + EXPECT_FALSE(file->write(reinterpret_cast("0"), 1U, + 0U, &bytes_written)); + EXPECT_EQ(0U, bytes_written); + } } TEST(utils_file, can_attach_file) { - auto path = test::generate_test_file_name("utils_file"); - auto file = utils::file::file::open_or_create_file(path); - auto file2 = utils::file::file::attach_file(file.get_handle()); - EXPECT_TRUE(file); - EXPECT_TRUE(file2); - EXPECT_EQ(file.get_path(), file2.get_path()); + for (auto idx = 0U; idx < file_type_count; ++idx) { + auto path = test::generate_test_file_name("utils_file"); + auto file = idx == 0U ? utils::file::file::open_or_create_file(path) + : utils::file::thread_file::open_or_create_file(path); + auto file2 = + idx == 0U ? utils::file::file::attach_file(file->get_handle()) + : utils::file::thread_file::attach_file(file->get_handle()); + EXPECT_TRUE(*file); + EXPECT_TRUE(*file2); + EXPECT_EQ(file->get_path(), file2->get_path()); + } } #if defined(PROJECT_ENABLE_JSON) @@ -100,7 +124,7 @@ TEST(utils_file, read_and_write_json_file_encrypted) { { auto file = utils::file::file::open_file(path); data_buffer encrypted_data{}; - EXPECT_TRUE(file.read_all(encrypted_data, 0U)); + EXPECT_TRUE(file->read_all(encrypted_data, 0U)); data_buffer decrypted_data{}; EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data, diff --git a/support/test/src/utils/path_test.cpp b/support/test/src/utils/path_test.cpp index 2742bbba..25d81ea6 100644 --- a/support/test/src/utils/path_test.cpp +++ b/support/test/src/utils/path_test.cpp @@ -21,12 +21,38 @@ */ #include "test.hpp" +#if defined(_WIN32) +namespace { +static const auto test_path = [](std::string str) -> std::string { +#if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + if (repertory::utils::string::begins_with(str, "\\")) { + str = repertory::utils::string::to_lower( + std::filesystem::current_path().string().substr(0U, 2U)) + + str; + } + + str = std::string{repertory::utils::path::long_notation} + str; +#else // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + if (repertory::utils::string::begins_with(str, "\\")) { + str = repertory::utils::string::to_lower( + std::filesystem::current_path().string().substr(0U, 2U)) + + str; + } +#endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) + + return repertory::utils::string::right_trim(str, '\\'); +}; +} // namespace +#endif // defined(_WIN32) + namespace repertory { TEST(utils_path, constants) { EXPECT_EQ(std::string_view{"\\"}, utils::path::backslash); EXPECT_EQ(std::wstring_view{L"\\"}, utils::path::backslash_w); EXPECT_EQ(std::string_view{"."}, utils::path::dot); EXPECT_EQ(std::wstring_view{L"."}, utils::path::dot_w); + EXPECT_EQ(std::string_view{".\\"}, utils::path::dot_backslash); + EXPECT_EQ(std::wstring_view{L".\\"}, utils::path::dot_backslash_w); EXPECT_EQ(std::string_view{"./"}, utils::path::dot_slash); EXPECT_EQ(std::wstring_view{L"./"}, utils::path::dot_slash_w); EXPECT_EQ(std::string_view{"/"}, utils::path::slash); @@ -82,6 +108,12 @@ TEST(utils_path, get_dot) { EXPECT_EQ(utils::path::dot_w, utils::path::get_dot()); } +TEST(utils_path, get_dot_backslash) { + EXPECT_EQ(utils::path::dot_backslash, utils::path::get_dot_backslash()); + EXPECT_EQ(utils::path::dot_backslash_w, + utils::path::get_dot_backslash()); +} + TEST(utils_path, get_dot_slash) { EXPECT_EQ(utils::path::dot_slash, utils::path::get_dot_slash()); EXPECT_EQ(utils::path::dot_slash_w, utils::path::get_dot_slash()); @@ -92,47 +124,53 @@ TEST(utils_path, get_slash) { EXPECT_EQ(utils::path::slash_w, utils::path::get_slash()); } +TEST(utils_path, get_long_notation) { + EXPECT_EQ(utils::path::long_notation, utils::path::get_long_notation()); + EXPECT_EQ(utils::path::long_notation_w, + utils::path::get_long_notation()); +} + TEST(utils_path, combine) { auto s = utils::path::combine(R"(\test\path)", {}); #if defined(_WIN32) - EXPECT_STREQ(R"(\test\path)", s.c_str()); + EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str()); #else EXPECT_STREQ("/test/path", s.c_str()); #endif s = utils::path::combine(R"(\test)", {R"(\path)"}); #if defined(_WIN32) - EXPECT_STREQ(R"(\test\path)", s.c_str()); + EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str()); #else EXPECT_STREQ("/test/path", s.c_str()); #endif s = utils::path::combine(R"(\test)", {R"(\path)", R"(\again\)"}); #if defined(_WIN32) - EXPECT_STREQ(R"(\test\path\again)", s.c_str()); + EXPECT_STREQ(test_path(R"(\test\path\again)").c_str(), s.c_str()); #else EXPECT_STREQ("/test/path/again", s.c_str()); #endif s = utils::path::combine("/home/test/.dest", {".state"}); #if defined(_WIN32) - EXPECT_STREQ("\\home\\test\\.dest\\.state", s.c_str()); + EXPECT_STREQ(test_path(R"(\home\test\.dest\.state)").c_str(), s.c_str()); #else EXPECT_STREQ("/home/test/.dest/.state", s.c_str()); #endif #if defined(_WIN32) s = utils::path::combine(R"(R:\test)", {R"(\path)", R"(\again\)"}); - EXPECT_STREQ(R"(r:\test\path\again)", s.c_str()); + EXPECT_STREQ(test_path(R"(r:\test\path\again)").c_str(), s.c_str()); s = utils::path::combine("R:", {R"(\path)", R"(\again\)"}); - EXPECT_STREQ(R"(r:\path\again)", s.c_str()); + EXPECT_STREQ(test_path(R"(r:\path\again)").c_str(), s.c_str()); s = utils::path::combine("R:", {}); - EXPECT_STREQ("r:", s.c_str()); + EXPECT_STREQ(test_path("r:").c_str(), s.c_str()); s = utils::path::combine("R:", {"\\"}); - EXPECT_STREQ("r:", s.c_str()); + EXPECT_STREQ(test_path("r:").c_str(), s.c_str()); #endif } @@ -224,104 +262,116 @@ TEST(utils_path, finalize) { s = utils::path::finalize(R"(\)"); #if defined(_WIN32) - EXPECT_STREQ(R"(\)", s.c_str()); + EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str()); #else EXPECT_STREQ("/", s.c_str()); #endif s = utils::path::finalize("/"); #if defined(_WIN32) - EXPECT_STREQ(R"(\)", s.c_str()); + EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str()); #else EXPECT_STREQ("/", s.c_str()); #endif s = utils::path::finalize(R"(\\)"); #if defined(_WIN32) - EXPECT_STREQ(R"(\)", s.c_str()); + EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str()); #else EXPECT_STREQ("/", s.c_str()); #endif s = utils::path::finalize("//"); #if defined(_WIN32) - EXPECT_STREQ(R"(\)", s.c_str()); + EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str()); #else EXPECT_STREQ("/", s.c_str()); #endif s = utils::path::finalize("/cow///moose/////dog/chicken"); #if defined(_WIN32) - EXPECT_STREQ(R"(\cow\moose\dog\chicken)", s.c_str()); + EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str()); #else EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); #endif s = utils::path::finalize("\\cow\\\\\\moose\\\\\\\\dog\\chicken/"); #if defined(_WIN32) - EXPECT_STREQ(R"(\cow\moose\dog\chicken)", s.c_str()); + EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str()); #else EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); #endif s = utils::path::finalize("/cow\\\\/moose\\\\/\\dog\\chicken\\"); #if defined(_WIN32) - EXPECT_STREQ(R"(\cow\moose\dog\chicken)", s.c_str()); + EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str()); #else EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); #endif #if defined(_WIN32) s = utils::path::finalize("D:"); - EXPECT_STREQ("d:", s.c_str()); + EXPECT_STREQ(test_path("d:").c_str(), s.c_str()); s = utils::path::finalize("D:\\"); - EXPECT_STREQ("d:", s.c_str()); + EXPECT_STREQ(test_path("d:").c_str(), s.c_str()); s = utils::path::finalize("D:\\moose"); - EXPECT_STREQ("d:\\moose", s.c_str()); + EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); s = utils::path::finalize("D:\\moose\\"); - EXPECT_STREQ("d:\\moose", s.c_str()); + EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); s = utils::path::finalize("D:/"); - EXPECT_STREQ("d:", s.c_str()); + EXPECT_STREQ(test_path("d:").c_str(), s.c_str()); s = utils::path::finalize("D:/moose"); - EXPECT_STREQ("d:\\moose", s.c_str()); + EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); s = utils::path::finalize("D:/moose/"); - EXPECT_STREQ("d:\\moose", s.c_str()); + EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); #endif // defined(_WIN32) } TEST(utils_path, absolute) { - auto dir = utils::path::absolute(std::filesystem::current_path().string()); + auto dir = utils::path::get_current_path(); auto path = utils::path::absolute("."); EXPECT_STREQ(dir.c_str(), path.c_str()); path = utils::path::absolute("./"); EXPECT_STREQ(dir.c_str(), path.c_str()); + path = utils::path::absolute(R"(.\)"); + EXPECT_STREQ(dir.c_str(), path.c_str()); + #if defined(_WIN32) + path = utils::path::absolute(R"(.\moose)"); + EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str()); + + path = utils::path::absolute(R"(./moose)"); + EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str()); + auto home_env = utils::get_environment_variable("USERPROFILE"); #else // !defined(_WIN32) + path = utils::path::absolute(R"(.\moose)"); + EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str()); + + path = utils::path::absolute(R"(./moose)"); + EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str()); + auto home_env = utils::get_environment_variable("HOME"); #endif // defined(_WIN32) - auto home = utils::path::absolute(home_env); path = utils::path::absolute("~"); EXPECT_STREQ(home.c_str(), path.c_str()); - - // path = utils::path::absolute("~/.local"); } TEST(utils_path, absolute_can_resolve_path_variables) { std::string home{}; #if defined(_WIN32) - home.resize(MAX_PATH + 1U); + home.resize(repertory::max_path_length + 1U); auto size = ::GetEnvironmentVariableA("USERPROFILE", home.data(), 0U); home.resize(size);