diff --git a/CHANGES.md b/CHANGES.md index 71c0f7dd..a4b17da0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,18 @@ ## 1.11.1-rc9 - 18th October 2026 +* additions: + * **unixstl::filesystem_traits** — implemented `find_last_path_name_separator()` / `find_next_path_name_separator()` and `is_path_rooted()` / `is_path_absolute()` / `is_path_UNC()` (NUL-terminated and counted), including Windows drive-letter and UNC classification; + * **unixstl::filesystem_traits** — path-resolve, current-directory, and directory-enumeration members remain declared, pending one conversion policy for that group; + * **unixstl_C_get_home_directory_w()** (**unixstl/system/directory_functions.h**) — reads `HOME` via `getenv()` and converts with `mbstowcs()` (locale multibyte encoding); a short buffer keeps a NUL in the last slot and returns `cchBuffer - 1`, matching the narrow copy; + * **unixstl::system_traits** — implemented `path_str_compare()` and `path_str_n_compare()` (on Windows, case-folded with `towupper()` and treating `/` and `\` as equivalent); + * **unixstl::system_traits** — implemented `get_environment_variable()` (raw and resizeable-buffer) by converting the name with `wcstombs()`, reading it with `getenv()`, and converting the value with `mbstowcs()`; implemented the resizeable-buffer overload of `get_home_directory()`; + * **unixstl::system_traits** — `load_library()` and `free_library()` stay declared together pending one conversion policy (`free_library()` no longer calls `dlclose()` alone); `expand_environment_strings()` remains declared; +* testing: + * Unit tests **test.unit.unixstl.filesystem.filesystem_traits** cover wide separator finding and path classification, and agreement with the narrow specialisation; + * Unit tests **test.unit.unixstl.system.system_traits** cover wide `path_str_compare()` and `path_str_n_compare()`; + * Component tests **test.component.unixstl.system.system_traits** cover wide `get_home_directory()` and `get_environment_variable()` (probe, fixed buffer, and `std::vector` resizeable overload) via `platformstl::environment_variable_scope`; + * Resizeable-buffer cases shared with the resizeable-buffer line (`ensure_dir_end()` / `remove_dir_end()`, and the narrow resizeable `get_home_directory()` / `get_environment_variable()` overloads) remain empty pending that merge; * docs: * Normalised Doxygen `\pre` comments to bare boolean expressions (no outer parentheses; no trailing `;` / `.`); * Prefer `nullptr` over `NULL` / prose in `\pre` text; optional-buffer forms use zero-length check first (`0 == n || nullptr != s`); diff --git a/NEWS.md b/NEWS.md index 7bdefd98..8660f2fa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,7 +3,7 @@ | Date | News Item | Details | | ------------------- | ----------------------------------- | ------- | -| 18th October 2026 | Release of [STLSoft 1.11.1-rc9](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc9) | `\pre` normalised to bare exprs with `nullptr`; zero-first optional buffers; companion project version bumps | +| 18th October 2026 | Release of [STLSoft 1.11.1-rc9](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc9) | **unixstl** `wchar_t` path classification and separators; `path_str_*`; wide `get_home_directory()` and `get_environment_variable()`; `\pre` normalised to bare exprs with `nullptr`; companion version bumps | | 10th October 2026 | Release of [STLSoft 1.11.1-rc8](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc8) | environment_variable_strtoll()`; `unixstl::filesystem_traits<>::rewind_dir()`; `class_type` and `getenv_s()` empty-variable fixes; component tests; `cmake --build` helper scripts; **run_all_component_tests.sh** / **run_all_automated_tests.sh**; **generate_doxygen.sh**; Linguist classification | | 30th September 2026 | Release of [STLSoft 1.11.1-rc7](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc7) | counted NULs; `wchar_t` `string_begins_with`; bit intrinsics; `write_binary()`; `file_lines` mmap (~10–20× vs `fgetc`); compare and allocator units; MinGW static runtime; short test directories; nested **test/performance** | | 21st September 2026 | Release of [STLSoft 1.11.1-rc6](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc6) | ACE/ATL/MFC CI; Clang/macOS `stricmp` aliases | diff --git a/include/unixstl/filesystem/filesystem_traits.hpp b/include/unixstl/filesystem/filesystem_traits.hpp index 338ee825..a839e6c3 100644 --- a/include/unixstl/filesystem/filesystem_traits.hpp +++ b/include/unixstl/filesystem/filesystem_traits.hpp @@ -58,10 +58,10 @@ #define UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION -# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 4 -# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 17 +# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 5 +# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 0 # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 1 -# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 192 +# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 193 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -109,6 +109,7 @@ #ifdef _WIN32 # include +# include #endif /* _WIN32 */ #ifndef STLSOFT_INCL_H_ERRNO # define STLSOFT_INCL_H_ERRNO @@ -3010,7 +3011,21 @@ struct filesystem_traits find_last_path_name_separator( char_type const* path ) - ; + { + UNIXSTL_ASSERT(NULL != path); + + char_type const* last = NULL; + + for (; L'\0' != *path; ++path) + { + if (is_path_name_separator(*path)) + { + last = path; + } + } + + return last; + } static char_type const* @@ -3018,14 +3033,43 @@ struct filesystem_traits char_type const* path , size_t len ) - ; + { + UNIXSTL_ASSERT(0 == len || NULL != path); + + if (0 == len) + { + return NULL; + } + + for (path += (len - 1); 0 != len; --len, --path) + { + if (is_path_name_separator(*path)) + { + return path; + } + } + + return NULL; + } static char_type const* find_next_path_name_separator( char_type const* path ) - ; + { + UNIXSTL_ASSERT(NULL != path); + + for (; L'\0' != *path; ++path) + { + if (is_path_name_separator(*path)) + { + return path; + } + } + + return NULL; + } static char_type const* @@ -3033,7 +3077,19 @@ struct filesystem_traits char_type const* path , size_t len ) - ; + { + UNIXSTL_ASSERT(0 == len || NULL != path); + + for (; 0 != len; --len, ++path) + { + if (is_path_name_separator(*path)) + { + return path; + } + } + + return NULL; + } static bool_type is_dots(char_type const* dir) { @@ -3061,31 +3117,153 @@ struct filesystem_traits } static bool_type is_path_rooted(char_type const* path) - ; + { + UNIXSTL_ASSERT(NULL != path); +#ifdef _WIN32 + + if (L'\\' == path[0] && + L'\\' == path[1]) + { + return true; + } + + if (0 != ::iswalpha(path[0]) && + L':' == path[1]) + { + path += 2; + } +#endif /* _WIN32 */ + + return is_path_name_separator(*path); + } static bool_type is_path_rooted( char_type const* path , size_t len ) - ; + { + UNIXSTL_ASSERT(NULL != path); +#ifdef _WIN32 + + if (len >= 2) + { + if (L'\\' == path[0] && + L'\\' == path[1]) + { + return true; + } + } + + if (len >= 2) + { + if (0 != ::iswalpha(path[0]) && + L':' == path[1]) + { + path += 2; + len -= 2; + } + } +#endif /* _WIN32 */ + + return 0 != len && is_path_name_separator(*path); + } static bool_type is_path_absolute(char_type const* path) - ; + { + UNIXSTL_ASSERT(NULL != path); +#ifdef _WIN32 + + if (is_path_UNC(path)) + { + return true; + } + if (0 != ::iswalpha(path[0]) && + L':' == path[1] && + is_path_name_separator(path[2])) + { + return true; + } + + return false; +#else /* ? _WIN32 */ + + return is_path_rooted(path); +#endif /* _WIN32 */ + } static bool_type is_path_absolute( char_type const* path , size_t len ) - ; + { + if (0 == len) + { + return false; + } +#ifdef _WIN32 + + if (is_path_UNC(path, len)) + { + return true; + } + if (len >= 3) + { + if (0 != ::iswalpha(path[0]) && + L':' == path[1] && + is_path_name_separator(path[2])) + { + return true; + } + } + + return false; +#else /* ? _WIN32 */ + + return is_path_rooted(path, len); +#endif /* _WIN32 */ + } static bool_type is_path_UNC(char_type const* path) - ; + { + UNIXSTL_ASSERT(NULL != path); +#ifdef _WIN32 + + size_type const len = str_len(path); + + return is_path_UNC(path, len); +#else /* ? _WIN32 */ + + STLSOFT_SUPPRESS_UNUSED(path); + + return false; +#endif /* _WIN32 */ + } static bool_type is_path_UNC( char_type const* path , size_t len ) - ; + { +#ifdef _WIN32 + + switch (len) + { + case 0: + case 1: + + return false; + default: + + return L'\\' == path[0] && L'\\' == path[1]; + } +#else /* ? _WIN32 */ + + STLSOFT_SUPPRESS_UNUSED(path); + STLSOFT_SUPPRESS_UNUSED(len); + + return false; +#endif /* _WIN32 */ + } static bool_type is_path_name_separator(char_type ch) { @@ -3120,6 +3298,10 @@ struct filesystem_traits return filesystem_traits_::path_max(); } + /* The following path-resolve, cwd, and dir-enumeration members remain + * declared (for API / metaprogramming compatibility) pending a finished + * conversion-policy implementation for the whole group. + */ static size_type get_full_path_name( diff --git a/include/unixstl/system/directory_functions.h b/include/unixstl/system/directory_functions.h index ee78e61a..486fcd98 100644 --- a/include/unixstl/system/directory_functions.h +++ b/include/unixstl/system/directory_functions.h @@ -4,11 +4,11 @@ * Purpose: Directory functions. * * Created: 1st October 2016 - * Updated: 26th April 2025 + * Updated: 15th October 2026 * * Home: http://stlsoft.org/ * - * Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 2016-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -52,9 +52,9 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_MAJOR 1 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_MINOR 0 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_REVISION 13 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_EDIT 23 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_MINOR 1 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_REVISION 1 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS_EDIT 24 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -80,6 +80,10 @@ # define STLSOFT_INCL_H_ERRNO # include #endif /* !STLSOFT_INCL_H_ERRNO */ +#ifndef STLSOFT_INCL_H_WCHAR +# define STLSOFT_INCL_H_WCHAR +# include +#endif /* !STLSOFT_INCL_H_WCHAR */ #ifndef STLSOFT_INCL_STLSOFT_API_external_h_string # include @@ -237,14 +241,58 @@ unixstl_C_get_home_directory_a( } } -#if 0 /* TODO: implement this */ +/* Policy: read HOME via getenv() (multibyte) and convert to wide with + * mbstowcs() using the locale multibyte encoding. + */ STLSOFT_INLINE -#endif us_size_t unixstl_C_get_home_directory_w( us_char_w_t buffer[] , us_size_t cchBuffer -); +) +{ + us_char_a_t const* const home = unixstl_C_get_home_directory_invoke_getenv_a_("HOME"); + + if (NULL == home) + { + return 0; + } + else + { + size_t const cchHome = STLSOFT_NS_GLOBAL(mbstowcs)(NULL, home, 0); + + if ((size_t)-1 == cchHome) + { + return 0; + } + + if (0 == cchBuffer) + { + return cchHome + 1; + } + + { + size_t const n = STLSOFT_NS_GLOBAL(mbstowcs)(buffer, home, cchBuffer); + + if ((size_t)-1 == n) + { + return 0; + } + + if (cchHome < cchBuffer) + { + return n; + } + + /* Insufficient space: match narrow string_copy_with_nul — + * leave a NUL in the last slot and return cchBuffer - 1. + */ + buffer[cchBuffer - 1] = L'\0'; + + return cchBuffer - 1; + } + } +} /* ///////////////////////////////////////////////////////////////////////// diff --git a/include/unixstl/system/system_traits.hpp b/include/unixstl/system/system_traits.hpp index 3be24177..b737497f 100644 --- a/include/unixstl/system/system_traits.hpp +++ b/include/unixstl/system/system_traits.hpp @@ -5,7 +5,7 @@ * Unicode specialisations thereof. * * Created: 15th November 2002 - * Updated: 8th October 2026 + * Updated: 15th October 2026 * * Home: http://stlsoft.org/ * @@ -53,10 +53,10 @@ #define UNIXSTL_INCL_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION -# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_MAJOR 6 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_MINOR 2 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_REVISION 3 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_EDIT 141 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_MAJOR 7 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_MINOR 0 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_REVISION 1 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_EDIT 144 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -75,6 +75,9 @@ # include #endif /* !UNIXSTL_INCL_UNIXSTL_SYSTEM_H_DIRECTORY_FUNCTIONS */ +#ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER +# include +#endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER */ #ifndef STLSOFT_INCL_STLSOFT_STRING_HPP_C_STRING_TRAITS # include #endif /* !STLSOFT_INCL_STLSOFT_STRING_HPP_C_STRING_TRAITS */ @@ -90,6 +93,7 @@ #elif defined(_WIN32) ||\ defined(_WIN64) # include +# include #endif /* Windows */ #ifndef STLSOFT_INCL_H_ERRNO # define STLSOFT_INCL_H_ERRNO @@ -124,6 +128,10 @@ # define STLSOFT_INCL_H_STDLIB # include #endif /* !STLSOFT_INCL_H_STDLIB */ +#ifndef STLSOFT_INCL_H_WCHAR +# define STLSOFT_INCL_H_WCHAR +# include +#endif /* !STLSOFT_INCL_H_WCHAR */ #ifndef STLSOFT_INCL_H_UNISTD # define STLSOFT_INCL_H_UNISTD # include @@ -760,7 +768,54 @@ struct system_traits path_str_compare( char_type const* ps1 , char_type const* ps2 - ); + ) + { + UNIXSTL_ASSERT(NULL != ps1); + UNIXSTL_ASSERT(NULL != ps2); + + for (;; ++ps1, ++ps2) + { + int_type d = int_type(*ps1) - int_type(*ps2); +#ifdef _WIN32 + + if (0 != d) + { + d = towupper(*ps1) - towupper(*ps2); + } + + if (0 != d) + { + switch (*ps1) + { + case L'/': + case L'\\': + + switch (*ps2) + { + case L'/': + case L'\\': + + d = 0; + break; + } + break; + } + } +#endif + + if (0 != d) + { + return d; + } + + if (L'\0' == *ps1) + { + break; + } + } + + return 0; + } static int @@ -768,7 +823,49 @@ struct system_traits char_type const* ps1 , char_type const* ps2 , size_type cch - ); + ) + { + UNIXSTL_ASSERT(NULL != ps1); + UNIXSTL_ASSERT(NULL != ps2); + + for (; 0 != cch; ++ps1, ++ps2, --cch) + { + int_type d = int_type(*ps1) - int_type(*ps2); +#ifdef _WIN32 + + if (0 != d) + { + d = towupper(*ps1) - towupper(*ps2); + } + + if (0 != d) + { + switch (*ps1) + { + case L'/': + case L'\\': + + switch (*ps2) + { + case L'/': + case L'\\': + + d = 0; + break; + } + break; + } + } +#endif + + if (0 != d) + { + return d; + } + } + + return 0; + } public: static @@ -783,13 +880,31 @@ struct system_traits return unixstl_C_get_home_directory_w(buffer, cchBuffer); } + template< + ss_typename_param_k T_resizeableBuffer + > + static + size_type + get_home_directory( + T_resizeableBuffer& rb + ) + { + size_type const cchRequired = get_home_directory(static_cast(NULL), 0); + + rb.resize(cchRequired); + + return get_home_directory(&rb[0], rb.size()); + } + public: - static module_type load_library(char_type const* name); + /* Dynamic loading stays declare-only on W until load_library and + * free_library can land together with one conversion policy. + */ + static module_type load_library(char_type const* name) + ; static bool_type free_library(module_type hModule) - { - return 0 == ::dlclose(hModule); - } + ; static void* @@ -826,6 +941,10 @@ struct system_traits } public: + /* Policy: convert wide name/value via the locale multibyte encoding + * for getenv(). expand_environment_strings remains declared (compat) + * pending a finished group implementation aligned with ANSI. + */ static size_type get_environment_variable( @@ -833,7 +952,77 @@ struct system_traits , char_type buffer[] , size_type cchBuffer ) - ; + { + UNIXSTL_ASSERT(NULL != name); + + size_t const cchName = ::wcstombs(NULL, name, 0); + + if (static_cast(-1) == cchName) + { + return 0; + } + + STLSOFT_NS_QUAL(auto_buffer) name_m(cchName + 1); + + if (name_m.empty()) + { + return 0; + } + + if (static_cast(-1) == ::wcstombs(&name_m[0], name, name_m.size())) + { + return 0; + } + +# include + + char const* const var = ::getenv(name_m.data()); + +# include + + if (NULL == var) + { + return 0; + } + else + { + size_t const cchValue = ::mbstowcs(NULL, var, 0); + + if (static_cast(-1) == cchValue) + { + return 0; + } + + if (NULL == buffer) + { + return cchValue; + } + else + { + STLSOFT_NS_QUAL(auto_buffer) value_w(cchValue + 1); + + if (value_w.empty()) + { + return 0; + } + + if (static_cast(-1) == ::mbstowcs(&value_w[0], var, value_w.size())) + { + return 0; + } + + size_type const writtenLen = (cchValue < cchBuffer) ? cchValue : cchBuffer; + + char_copy(buffer, value_w.data(), writtenLen); + if (writtenLen < cchBuffer) + { + buffer[writtenLen] = L'\0'; + } + + return (cchValue < cchBuffer) ? cchValue : cchBuffer; + } + } + } template< ss_typename_param_k T_resizeableBuffer @@ -844,7 +1033,18 @@ struct system_traits char_type const* name , T_resizeableBuffer& rb ) - ; + { + size_type const cchRequired = get_environment_variable(name, static_cast(NULL), 0); + + if (0 == cchRequired) + { + return 0; + } + + rb.resize(cchRequired); + + return get_environment_variable(name, &rb[0], rb.size()); + } static size_type diff --git a/test/component/unixstl/system/CMakeLists.txt b/test/component/unixstl/system/CMakeLists.txt index eefe07f3..82b193ab 100644 --- a/test/component/unixstl/system/CMakeLists.txt +++ b/test/component/unixstl/system/CMakeLists.txt @@ -1,2 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten add_subdirectory(environment_variable) +add_subdirectory(system_traits) diff --git a/test/component/unixstl/system/system_traits/CMakeLists.txt b/test/component/unixstl/system/system_traits/CMakeLists.txt new file mode 100644 index 00000000..abafd193 --- /dev/null +++ b/test/component/unixstl/system/system_traits/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.component.unixstl.system.system_traits entry.cpp) diff --git a/test/component/unixstl/system/system_traits/entry.cpp b/test/component/unixstl/system/system_traits/entry.cpp new file mode 100644 index 00000000..afcec26c --- /dev/null +++ b/test/component/unixstl/system/system_traits/entry.cpp @@ -0,0 +1,232 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/component/unixstl/system/system_traits/entry.cpp + * + * Purpose: Component-tests for `unixstl::system_traits` — ANSI + * resizeable-buffer overloads (`get_home_directory`, + * `get_environment_variable`) and wide home / environment APIs. + * + * Created: 15th October 2026 + * Updated: 15th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include +#include +#include + +/* Standard C++ header files */ +#include + +/* Standard C header files */ +#include +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace { + + static void TEST_get_environment_variable_MISSING(); + static void TEST_get_environment_variable_RESIZEABLE_auto_buffer(); + static void TEST_get_environment_variable_RESIZEABLE_vector(); + static void TEST_get_environment_variable_w(); + static void TEST_get_home_directory_RESIZEABLE_auto_buffer(); + static void TEST_get_home_directory_RESIZEABLE_vector(); + static void TEST_get_home_directory_w(); +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.component.unixstl.system.system_traits", verbosity)) + { + XTESTS_RUN_CASE(TEST_get_environment_variable_MISSING); + XTESTS_RUN_CASE(TEST_get_environment_variable_RESIZEABLE_auto_buffer); + XTESTS_RUN_CASE(TEST_get_environment_variable_RESIZEABLE_vector); + XTESTS_RUN_CASE(TEST_get_environment_variable_w); + XTESTS_RUN_CASE(TEST_get_home_directory_RESIZEABLE_auto_buffer); + XTESTS_RUN_CASE(TEST_get_home_directory_RESIZEABLE_vector); + XTESTS_RUN_CASE(TEST_get_home_directory_w); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace { + + typedef stlsoft::auto_buffer ab_m_t; + typedef stlsoft::auto_buffer ab_w_t; + typedef unixstl::system_traits traits_m_t; + typedef unixstl::system_traits traits_w_t; + typedef std::vector vec_w_t; + + static wchar_t const s_wide_missing_name_w[] = + L"UNIXSTL_SYSTEM_TRAITS_W_TEST_MISSING_9F3C2B1A_7E4D_4A6F_9C08_1D2E3F4A5B6C"; + + static char const s_wide_present_name[] = + "UNIXSTL_SYSTEM_TRAITS_W_TEST_9F3C2B1A_7E4D_4A6F_9C08_1D2E3F4A5B6C"; + + static wchar_t const s_wide_present_name_w[] = + L"UNIXSTL_SYSTEM_TRAITS_W_TEST_9F3C2B1A_7E4D_4A6F_9C08_1D2E3F4A5B6C"; + + static char const s_wide_present_value[] = "wide-env-value"; + + static wchar_t const s_wide_present_value_w[] = L"wide-env-value"; + + +/* Shared with resizeable_buffer branch — bodies and ANSI name/value + * constants land at merge (rb size including NUL / probe-then-fill). + */ +static void TEST_get_environment_variable_MISSING() +{ +} + +static void TEST_get_environment_variable_RESIZEABLE_auto_buffer() +{ +} + +static void TEST_get_environment_variable_RESIZEABLE_vector() +{ +} + +static void TEST_get_environment_variable_w() +{ + platformstl::environment_variable_scope const scope( + s_wide_present_name + , s_wide_present_value + ); + + size_t const cch_probe = traits_w_t::get_environment_variable( + s_wide_present_name_w + , static_cast(NULL) + , 0 + ); + + TEST_INT_EQ(::strlen(s_wide_present_value), cch_probe); + + wchar_t buff[64]; + size_t const cch = traits_w_t::get_environment_variable( + s_wide_present_name_w + , buff + , STLSOFT_NUM_ELEMENTS(buff) + ); + + TEST_INT_EQ(cch_probe, cch); + TEST_WS_EQ(s_wide_present_value_w, buff); + + { + vec_w_t rb; + + size_t const n = traits_w_t::get_environment_variable( + s_wide_present_name_w + , rb + ); + + TEST_INT_EQ(cch_probe, n); + TEST_INT_EQ(cch_probe, rb.size()); + TEST_INT_EQ(0, ::wcsncmp(s_wide_present_value_w, rb.data(), cch_probe)); + } + + TEST_INT_EQ( + 0 + , traits_w_t::get_environment_variable( + s_wide_missing_name_w + , buff + , STLSOFT_NUM_ELEMENTS(buff) + ) + ); +} + +static void TEST_get_home_directory_RESIZEABLE_auto_buffer() +{ +} + +static void TEST_get_home_directory_RESIZEABLE_vector() +{ +} + +static void TEST_get_home_directory_w() +{ + size_t const required_m = traits_m_t::get_home_directory(static_cast(NULL), 0); + size_t const required_w = traits_w_t::get_home_directory(static_cast(NULL), 0); + + if (0 == required_m) + { + TEST_INT_EQ(0, required_w); + return; + } + + TEST_INT_EQ(required_m, required_w); + + ab_m_t buff_m(required_m); + ab_w_t buff_w(required_w); + + size_t const cch_m = traits_m_t::get_home_directory(&buff_m[0], buff_m.size()); + size_t const cch_w = traits_w_t::get_home_directory(&buff_w[0], buff_w.size()); + + TEST_INT_EQ(required_m - 1, cch_m); + TEST_INT_EQ(required_w - 1, cch_w); + TEST_INT_EQ(cch_m, cch_w); + + { + ab_w_t expected(cch_m + 1); + + TEST_INT_NE(static_cast(-1), ::mbstowcs(&expected[0], buff_m.data(), expected.size())); + TEST_WS_EQ(expected.data(), buff_w.data()); + } + + { + vec_w_t rb; + + size_t const n = traits_w_t::get_home_directory(rb); + + TEST_INT_EQ(cch_w, n); + TEST_INT_EQ(required_w, rb.size()); + TEST_WS_EQ(buff_w.data(), rb.data()); + } +} +} // anonymous namespace + + +/* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/unit/unixstl/CMakeLists.txt b/test/unit/unixstl/CMakeLists.txt index ba184416..d98de204 100644 --- a/test/unit/unixstl/CMakeLists.txt +++ b/test/unit/unixstl/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(filesystem) add_subdirectory(process) add_subdirectory(shims) +add_subdirectory(system) diff --git a/test/unit/unixstl/filesystem/CMakeLists.txt b/test/unit/unixstl/filesystem/CMakeLists.txt index 7bfdcb84..fb368025 100644 --- a/test/unit/unixstl/filesystem/CMakeLists.txt +++ b/test/unit/unixstl/filesystem/CMakeLists.txt @@ -1,3 +1,4 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(filesystem_traits) add_subdirectory(path) add_subdirectory(squeeze_functions) diff --git a/test/unit/unixstl/filesystem/filesystem_traits/CMakeLists.txt b/test/unit/unixstl/filesystem/filesystem_traits/CMakeLists.txt new file mode 100644 index 00000000..c5ad72a1 --- /dev/null +++ b/test/unit/unixstl/filesystem/filesystem_traits/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.unixstl.filesystem.filesystem_traits entry.cpp) diff --git a/test/unit/unixstl/filesystem/filesystem_traits/entry.cpp b/test/unit/unixstl/filesystem/filesystem_traits/entry.cpp new file mode 100644 index 00000000..375f83c7 --- /dev/null +++ b/test/unit/unixstl/filesystem/filesystem_traits/entry.cpp @@ -0,0 +1,208 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/unixstl/filesystem/filesystem_traits/entry.cpp + * + * Purpose: Unit-tests for `unixstl::filesystem_traits` resizeable-buffer + * `ensure_dir_end` / `remove_dir_end` (C2 empty-`""` behaviour; + * buffer size after remove); and wide path classification / + * separator finders. + * + * Created: 15th October 2026 + * Updated: 15th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include + +/* Standard C header files */ +#include +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace { + + static void TEST_ensure_dir_end_EMPTY_stays_empty(); + static void TEST_ensure_dir_end_APPENDS_separator(); + static void TEST_remove_dir_end_SHRINKS_buffer(); + static void TEST_remove_then_ensure_round_trip(); + + static void TEST_find_path_name_separators_w(); + static void TEST_path_classification_w(); + static void TEST_path_classification_m_matches_w(); +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.unixstl.filesystem.filesystem_traits", verbosity)) + { + XTESTS_RUN_CASE(TEST_ensure_dir_end_EMPTY_stays_empty); + XTESTS_RUN_CASE(TEST_ensure_dir_end_APPENDS_separator); + XTESTS_RUN_CASE(TEST_remove_dir_end_SHRINKS_buffer); + XTESTS_RUN_CASE(TEST_remove_then_ensure_round_trip); + + XTESTS_RUN_CASE(TEST_find_path_name_separators_w); + XTESTS_RUN_CASE(TEST_path_classification_w); + XTESTS_RUN_CASE(TEST_path_classification_m_matches_w); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace { + + typedef unixstl::filesystem_traits traits_m_t; + typedef unixstl::filesystem_traits traits_w_t; + + +/* Shared with resizeable_buffer branch — bodies land at merge (C2/C3). */ +static void TEST_ensure_dir_end_EMPTY_stays_empty() +{ +} + +static void TEST_ensure_dir_end_APPENDS_separator() +{ +} + +static void TEST_remove_dir_end_SHRINKS_buffer() +{ +} + +static void TEST_remove_then_ensure_round_trip() +{ +} + + +static void TEST_find_path_name_separators_w() +{ + { + wchar_t const* const path = L"/usr/local/bin"; + wchar_t const* const last = traits_w_t::find_last_path_name_separator(path); + wchar_t const* const next = traits_w_t::find_next_path_name_separator(path); + + TEST_PTR_NE(NULL, last); + TEST_PTR_NE(NULL, next); + TEST_PTR_EQ(path + 0, next); + TEST_PTR_EQ(path + 10, last); + TEST_PTR_EQ(last, traits_w_t::find_last_path_name_separator(path, ::wcslen(path))); + TEST_PTR_EQ(next, traits_w_t::find_next_path_name_separator(path, ::wcslen(path))); + } + + { + wchar_t const* const path = L"relative"; + + TEST_PTR_EQ(NULL, traits_w_t::find_last_path_name_separator(path)); + TEST_PTR_EQ(NULL, traits_w_t::find_next_path_name_separator(path)); + TEST_PTR_EQ(NULL, traits_w_t::find_last_path_name_separator(path, 0)); + TEST_PTR_EQ(NULL, traits_w_t::find_next_path_name_separator(path, 0)); + } +} + +static void TEST_path_classification_w() +{ + TEST_BOOLEAN_TRUE(traits_w_t::is_path_rooted(L"/")); + TEST_BOOLEAN_TRUE(traits_w_t::is_path_rooted(L"/tmp")); + TEST_BOOLEAN_FALSE(traits_w_t::is_path_rooted(L"tmp")); + TEST_BOOLEAN_FALSE(traits_w_t::is_path_rooted(L"", 0)); + + TEST_BOOLEAN_TRUE(traits_w_t::is_path_absolute(L"/tmp")); + TEST_BOOLEAN_FALSE(traits_w_t::is_path_absolute(L"tmp")); + TEST_BOOLEAN_FALSE(traits_w_t::is_path_absolute(L"", 0)); + + TEST_BOOLEAN_FALSE(traits_w_t::is_path_UNC(L"/tmp")); + TEST_BOOLEAN_FALSE(traits_w_t::is_path_UNC(L"\\\\server\\share")); + TEST_BOOLEAN_FALSE(traits_w_t::is_path_UNC(L"/tmp", 4)); +} + +static void TEST_path_classification_m_matches_w() +{ + char const* const paths_m[] = + { + "/", + "/tmp", + "tmp", + "a/b", + "", + }; + wchar_t const* const paths_w[] = + { + L"/", + L"/tmp", + L"tmp", + L"a/b", + L"", + }; + + { for (size_t i = 0; i != STLSOFT_NUM_ELEMENTS(paths_m); ++i) + { + size_t const len_m = ::strlen(paths_m[i]); + size_t const len_w = ::wcslen(paths_w[i]); + + TEST_BOOLEAN_EQ( + traits_m_t::is_path_rooted(paths_m[i]) + , traits_w_t::is_path_rooted(paths_w[i]) + ); + TEST_BOOLEAN_EQ( + traits_m_t::is_path_rooted(paths_m[i], len_m) + , traits_w_t::is_path_rooted(paths_w[i], len_w) + ); + TEST_BOOLEAN_EQ( + traits_m_t::is_path_absolute(paths_m[i]) + , traits_w_t::is_path_absolute(paths_w[i]) + ); + TEST_BOOLEAN_EQ( + traits_m_t::is_path_absolute(paths_m[i], len_m) + , traits_w_t::is_path_absolute(paths_w[i], len_w) + ); + TEST_BOOLEAN_EQ( + traits_m_t::is_path_UNC(paths_m[i]) + , traits_w_t::is_path_UNC(paths_w[i]) + ); + }} +} +} // anonymous namespace + + +/* ///////////////////////////// end of file //////////////////////////// */ diff --git a/test/unit/unixstl/system/CMakeLists.txt b/test/unit/unixstl/system/CMakeLists.txt new file mode 100644 index 00000000..aabe6a2b --- /dev/null +++ b/test/unit/unixstl/system/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(system_traits) diff --git a/test/unit/unixstl/system/system_traits/CMakeLists.txt b/test/unit/unixstl/system/system_traits/CMakeLists.txt new file mode 100644 index 00000000..32b277bf --- /dev/null +++ b/test/unit/unixstl/system/system_traits/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.unixstl.system.system_traits entry.cpp) diff --git a/test/unit/unixstl/system/system_traits/entry.cpp b/test/unit/unixstl/system/system_traits/entry.cpp new file mode 100644 index 00000000..a8877e36 --- /dev/null +++ b/test/unit/unixstl/system/system_traits/entry.cpp @@ -0,0 +1,95 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/unit/unixstl/system/system_traits/entry.cpp + * + * Purpose: Unit-tests for `unixstl::system_traits` wide `path_str_*`. + * Environment-dependent coverage lives in + * `test/component/unixstl/system/system_traits`. + * + * Created: 15th October 2026 + * Updated: 15th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* Standard C header files */ +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace { + + static void TEST_path_str_compare_w(); +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.unixstl.system.system_traits", verbosity)) + { + XTESTS_RUN_CASE(TEST_path_str_compare_w); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace { + + typedef unixstl::system_traits traits_m_t; + typedef unixstl::system_traits traits_w_t; + + +static void TEST_path_str_compare_w() +{ + TEST_INT_EQ(0, traits_w_t::path_str_compare(L"/a/b", L"/a/b")); + TEST_INT_NE(0, traits_w_t::path_str_compare(L"/a/b", L"/a/c")); + TEST_INT_EQ(0, traits_w_t::path_str_n_compare(L"/a/b", L"/a/c", 3)); + TEST_INT_NE(0, traits_w_t::path_str_n_compare(L"/a/b", L"/a/c", 4)); + + TEST_BOOLEAN_EQ( + traits_m_t::path_str_compare("/tmp/x", "/tmp/y") < 0 + , traits_w_t::path_str_compare(L"/tmp/x", L"/tmp/y") < 0 + ); +} +} // anonymous namespace + + +/* ///////////////////////////// end of file //////////////////////////// */