diff --git a/.gitattributes b/.gitattributes
index 76a2f865..1ad0590c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,6 +3,11 @@
# Sources: GitHub Docs (line endings), git-scm gitattributes (diff=cpp),
# gitattributes/gitattributes C++.gitattributes + Common.gitattributes,
# github-linguist overrides.
+#
+# Linguist language-bar policy (Synesis): classify public C API headers as C
+# and *.cmake as CMake scripts; keep helper scripts, IDE project files, and
+# CMakeLists.txt config out of the advertised language mix. Prefer
+# -linguist-detectable over linguist-detectable=false.
# Default: detect text, normalize to LF in the repository
* text=auto
@@ -97,3 +102,25 @@ TODO text
# --- GitHub Linguist ---
**/build/** linguist-generated
**/cmake-build-*/** linguist-generated
+
+# Classification (C-primary / C API with C++ tests; *.cmake as CMake scripts)
+*.c linguist-language=C
+*.cmake linguist-language=CMake
+*.cpp linguist-language=C++
+*.cxx linguist-language=C++
+*.h linguist-language=C
+*.hpp linguist-language=C++
+*.hxx linguist-language=C++
+
+# Keep the bar on library languages (helpers / legacy IDE / docs /
+# CMakeLists.txt config; leave *.cmake detectable as CMake)
+*.cmd -linguist-detectable
+*.dsp -linguist-detectable
+*.dsw -linguist-detectable
+*.html -linguist-detectable
+*.sh -linguist-detectable
+*.sln -linguist-detectable
+*.vcxproj -linguist-detectable
+*.vimrc -linguist-detectable
+CMakeLists.txt -linguist-detectable
+makefile -linguist-detectable
diff --git a/.github/workflows/ci-cell.yml b/.github/workflows/ci-cell.yml
index acac2798..0a9c3979 100644
--- a/.github/workflows/ci-cell.yml
+++ b/.github/workflows/ci-cell.yml
@@ -478,7 +478,7 @@ jobs:
if [ "${{ inputs.c-compiler }}" = "mingw" ]; then
export PATH="/mingw64/bin:$PATH"
fi
- SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_unit_tests.sh -M --component-only
+ SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_component_tests.sh -M
test-scratch:
name: Scratch tests (${{ inputs.cell-id }})
diff --git a/CHANGES.md b/CHANGES.md
index c9fee270..45a2339e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,39 @@
# STLSoft - CHANGES
+## 1.11.1-rc8 - 10th October 2026
+
+* additions:
+ * Added `stlsoft_C_environment_variable_strtoll_m()` and `stlsoft::environment_variable_strtoll()`, with UNIX `getenv()` / `strtoll()` and Windows `getenv_s()` / `STLSOFT_C_AUTO_BUFFER` support; added component tests;
+ * Added `unixstl_C_get_current_process_id()` (**unixstl/process/functions.h**, new), `winstl_C_get_current_process_id()` (**winstl/process/functions.h**), and `platformstl_C_get_current_process_id()` (**platformstl/process/functions.h**, new), in terms of `getpid()` / `_getpid()`;
+ * **unixstl::filesystem_traits** — added `rewind_dir()` (wrapping `rewinddir()`) to complement `open_dir()`, `read_dir()`, and `close_dir()`, so that a directory search may be restarted from its first entry without closing and reopening the handle;
+* fixes:
+ * Fixed `stlsoft_C_environment_variable_exists_m()` when `getenv_s()` reports an existing empty variable; added component tests for `stlsoft::environment_variable_exists()`;
+ * **unixstl::filesystem_traits** — `class_type` is now defined in terms of `char_type` (the wide-character specialisation previously named the narrow specialisation);
+ * **unixstl** / **winstl** `system_traits` and `filesystem_traits` — canonicalised the conditional-compilation guards (no behavioural change);
+* testing:
+ * Component tests for `unixstl::filesystem_traits` (**test/component/unixstl/filesystem/filesystem_traits/opendir**) cover `open_dir()`, `read_dir()`, `rewind_dir()` (before any read, after a partial read, after exhaustion, on an empty directory, and repeatedly), and `close_dir()`;
+ * Scratch test **test/scratch/unixstl/filesystem/rewind_dir** — exercises `rewind_dir()` over a directory given on the command-line;
+ * Example **examples/by_project/unixstl/filesystem/rewind_dir** — enumerates a directory in two passes (count, then collect), using `rewind_dir()` between them;
+ * **test.unit.versions** — aligned with **1.11.1-rc8** (**_STLSOFT_VER_1_11_1_RC8**);
+* version:
+ * **unixstl** — **1.8.6 beta 7** (**_UNIXSTL_VER_1_8_6_B07**), with **STLSoft 1.11.1-rc8**;
+ * **winstl** — **1.13.0 beta 4** (**_WINSTL_VER_1_13_0_B04**), with **STLSoft 1.11.1-rc8**;
+ * **inetstl** — **1.5.0 alpha 3** (**_INETSTL_VER_1_5_0_A03**), with **STLSoft 1.11.1-rc8**;
+* boilerplate:
+ * Added **run_all_component_tests.sh** / **.cmd**, **run_all_performance_tests.sh** / **.cmd** (with `--gap-groups`), and **run_all_automated_tests.sh** / **.cmd** (unit then component; `--unit-only` / `--component-only`);
+ * Added **generate_doxygen.sh**, **Doxyfile**, and **doc/mainpage.md** — HTML API documentation generated from the public headers into **_build/doxygen/html**;
+ * **remove_cmake_artefacts.sh** — no longer removes **docs** or **Doxyfile**; iterates arrays safely;
+ * **batch_compile_check**, **execute_performance_tests.sh** — quote the `$(dirname ...)` directory resolution;
+ * Scripts — **build_cmake.sh**, **clean_cmake.sh**, **ctest_cmake.sh**, **prepare_cmake.sh**, **remove_cmake_artefacts.sh**, **run_all_examples.sh**, and **run_all_scratch_tests.sh** rewritten to the common Synesis helper-script layout: builds go through `cmake --build` (with `--config` selected from `SIS_CMAKE_CONFIG` for multi-config generators) rather than `make` / `mingw32-make`; coloured output (`-A` / `--always-use-colours`, `SIS_CMAKE_ALWAYS_USE_COLOURS`, `NO_COLOR`); quoted paths and arguments; `.sis/project_name.txt` used in all status messages;
+ * **prepare_cmake.sh** — added `--build-shared-libs` and `-s` / `--stlsoft-root-dir`; honours `SIS_CMAKE_GENERATOR` and `SIS_CMAKE_MINGW`; every `-D` option is passed on all generator paths;
+ * **run_all_unit_tests.sh** / **.cmd** — now run unit-test programs only (`--unit-only` accepted for compatibility; `--component-only` removed);
+ * **run_all_examples.sh** — retains `--skip-interactive` (**.github/ci_skip_interactive_examples.txt**, which prevents headless CI hanging on GUI examples) and its discovery of every executable under the **examples** tree, and now exports `SIS_EXAMPLE_SMOKE=1`; **run_all_scratch_tests.sh** / **.cmd** — removed `--verbosity`;
+ * **.gitattributes** — Linguist language-bar policy: public C headers and sources are classified as C, C++ headers and sources as C++, and `*.cmake` as CMake; helper scripts, IDE project files, **CMakeLists.txt**, and **makefile** are excluded from the advertised language mix (`-linguist-detectable`);
+ * CI — **ci-cell.yml** runs component tests via **run_all_component_tests.sh**;
+ * **INSTALL.md** — documented **run_all_component_tests.sh**, **run_all_automated_tests.sh**, and **generate_doxygen.sh**; corrected the unit-test discovery patterns;
+
+
## 1.11.1-rc7 - 30th September 2026
* **simple_string** — counted construction, `assign`, `append`, `push_back`, and concatenation keep an embedded NUL, including assignment of a single `'\0'`;
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 93f51914..f8d841ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,7 @@
# Purpose: Top-level CMake lists file for STLSoft
#
# Created: 31st March 2021
-# Updated: 30th September 2026
+# Updated: 10th October 2026
#
# ######################################################################## #
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 00000000..2b908973
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,72 @@
+# Doxyfile for STLSoft (C/C++ API; 100% header-only)
+
+PROJECT_NAME = "STLSoft"
+PROJECT_BRIEF = "Robust, lightweight, cross-platform, template software"
+PROJECT_NUMBER = 1.11.1-rc8
+
+# Overridden at generation time by generate_doxygen.sh (respects
+# SIS_CMAKE_BUILD_DIR).
+OUTPUT_DIRECTORY = _build/doxygen
+CREATE_SUBDIRS = NO
+
+OUTPUT_LANGUAGE = English
+TAB_SIZE = 4
+# Many header names recur across sub-projects (e.g. **functions.h**), so
+# retain the path relative to include/.
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = include
+STRIP_FROM_INC_PATH = include
+TIMESTAMP = NO
+
+INPUT = include \
+ doc
+FILE_PATTERNS = *.h \
+ *.hpp \
+ *.md
+RECURSIVE = YES
+
+USE_MDFILE_AS_MAINPAGE = doc/mainpage.md
+
+EXAMPLE_PATH = examples
+EXAMPLE_RECURSIVE = YES
+
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+
+GENERATE_HTML = YES
+GENERATE_LATEX = NO
+HTML_OUTPUT = html
+
+GENERATE_TREEVIEW = YES
+DISABLE_INDEX = NO
+SEARCHENGINE = YES
+SOURCE_BROWSER = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+VERBATIM_HEADERS = YES
+
+MARKDOWN_SUPPORT = YES
+JAVADOC_AUTOBRIEF = YES
+QUIET = YES
+
+# Undocumented-member and parameter warnings are disabled (unlike the
+# smaller C libraries) because the very large template corpus would bury
+# genuine documentation errors.
+WARN_IF_UNDOCUMENTED = NO
+WARN_NO_PARAMDOC = NO
+WARN_IF_DOC_ERROR = YES
+
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = YES
+SKIP_FUNCTION_MACROS = YES
+# Skip internal version/edit noise.
+PREDEFINED = STLSOFT_DOCUMENTATION_SKIP_SECTION
+
+HAVE_DOT = NO
+
+
+# ########################### end of file ########################### #
diff --git a/EXAMPLES.md b/EXAMPLES.md
index e12dae08..e5104e6a 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -15,6 +15,7 @@
| **glob_sequence** | [by_project/unixstl/filesystem/glob_sequence](./examples/by_project/unixstl/filesystem/glob_sequence/main.cpp) | Unix | List selected filesystem entries with `unixstl::glob_sequence` |
| **home_directory** | [by_project/unixstl/system/home_directory](./examples/by_project/unixstl/system/home_directory/main.cpp) | Unix | Illustrates use of `unixstl::home_directory` |
| **readdir_sequence** | [by_project/unixstl/filesystem/readdir_sequence](./examples/by_project/unixstl/filesystem/readdir_sequence/main.cpp) | Unix | List filesystem entries with `unixstl::readdir_sequence` |
+| **rewind_dir** | [by_project/unixstl/filesystem/rewind_dir](./examples/by_project/unixstl/filesystem/rewind_dir/main.cpp) | Unix | Enumerate a directory twice with `unixstl::filesystem_traits<>::rewind_dir()` |
| | | | |
| **C.output_debug_line** | [by_project/winstl/diagnostics/C.output_debug_line](./examples/by_project/winstl/diagnostics/C.output_debug_line/main.c) | Windows | Emit debug strings with the C API `winstl_C_diagnostics_output_debug_line_*` |
| **codepages** | [by_project/winstl/i18n/codepages](./examples/by_project/winstl/i18n/codepages/main.cpp) | Windows | Enumerate system code pages with `winstl::codepage_sequence` |
diff --git a/INSTALL.md b/INSTALL.md
index dcb57aaa..13e5c0ee 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -23,17 +23,17 @@ There are three options for installing **STLSoft**, depending on how you obtain
If you obtain the latest release as an archive from the [**STLSoft** GitHub Releases](https://github.com/synesissoftware/STLSoft/releases) page (legacy archives may also still appear on [SourceForge](https://sourceforge.net/projects/stlsoft/)):
-1. Download the latest distribution, e.g. [**STLSoft-1.11.1-rc7.zip**](https://github.com/synesissoftware/STLSoft/archive/refs/tags/1.11.1-rc7.zip);
+1. Download the latest distribution, e.g. [**STLSoft-1.11.1-rc8.zip**](https://github.com/synesissoftware/STLSoft/archive/refs/tags/1.11.1-rc8.zip);
2. Unzip it to a directory of your choice, as in:
```bash
$ mkdir -p ~/open-source
$ cd ~/open-source
-$ unzip STLSoft-1.11.1-rc7.zip
+$ unzip STLSoft-1.11.1-rc8.zip
```
-3. Define an environment variable `STLSOFT`, whose value is the directory in which you unzipped it, e.g. `STLSOFT=~/open-source/STLSoft-1.11.1-rc7`, and then specify `$(STLSOFT)/include` (**UNIX**) or `%STLSOFT%\include` (**Windows**) in your project files and makefiles;
+3. Define an environment variable `STLSOFT`, whose value is the directory in which you unzipped it, e.g. `STLSOFT=~/open-source/STLSoft-1.11.1-rc8`, and then specify `$(STLSOFT)/include` (**UNIX**) or `%STLSOFT%\include` (**Windows**) in your project files and makefiles;
### Cloning project, using environment variables
@@ -100,16 +100,16 @@ $ sudo cmake --install ${SIS_CMAKE_BUILD_DIR:-./_build} --config Release
Unless you are certain that your projects, and all the projects upon which they depend, all use **STLSoft** via **CMake** then it is recommended that you define an `STLSOFT` environment variable.
For example, if you're using **UNIX** and you install to
-**/usr/local/stlsoft/STLSoft-1.11.1-rc7**
+**/usr/local/stlsoft/STLSoft-1.11.1-rc8**
then you should set the environment variable `STLSOFT` to
-**/usr/local/stlsoft/STLSoft-1.11.1-rc7**
-in which case the file `stlsoft/stlsoft.h`, for example, will be located in `/usr/local/stlsoft/STLSoft-1.11.1-rc7/include/stlsoft/stlsoft.h`.
+**/usr/local/stlsoft/STLSoft-1.11.1-rc8**
+in which case the file `stlsoft/stlsoft.h`, for example, will be located in `/usr/local/stlsoft/STLSoft-1.11.1-rc8/include/stlsoft/stlsoft.h`.
Conversely, if you're using **Windows** and you install to
-**C:\3pty\STLSoft\STLSoft-1.11.1-rc7**
+**C:\3pty\STLSoft\STLSoft-1.11.1-rc8**
then you should set the environment variable `STLSOFT` to
-**C:\3pty\STLSoft\STLSoft-1.11.1-rc7**
-in which case the file `stlsoft/stlsoft.h`, for example, will be located in `C:\3pty\STLSoft\STLSoft-1.11.1-rc7\include\stlsoft\stlsoft.h`.
+**C:\3pty\STLSoft\STLSoft-1.11.1-rc8**
+in which case the file `stlsoft/stlsoft.h`, for example, will be located in `C:\3pty\STLSoft\STLSoft-1.11.1-rc8\include\stlsoft\stlsoft.h`.
The makefiles / project files of several other open-source projects expect this symbol, including:
@@ -143,7 +143,10 @@ This project comes with a number of scripts useful for building with **CMake**,
| **build_cmake.sh** | Executes **CMake**-generated artefacts to (re)build the project.
Meaningful only once `prepare_cmake.sh` has been run to generate the **CMake** build artefacts.
Use `./build_cmake.sh --help` for further information. |
| **clean_cmake.sh** | Executes **CMake**-generated artefacts to clean the project.
Meaningful only once `prepare_cmake.sh` has been run and a build has been run.
Use `./clean_cmake.sh --help` for further information. |
| **run_all_examples.sh** | Runs all (matching) example programs.
Performs a build and then recursively runs executables under the examples tree. See also [**EXAMPLES.md**](./EXAMPLES.md).
Use `./run_all_examples.sh --help` for further information. |
-| **run_all_unit_tests.sh** | Runs all (matching) unit-test programs.
Performs a build and then recursively runs executable programs matching the shell patterns `test_*` and `test.*`.
Use `./run_all_unit_tests.sh --help` for further information. |
+| **run_all_unit_tests.sh** | Runs all (matching) unit-test programs.
Performs a build and then recursively runs executable programs matching the shell patterns `test_unit*` and `test.unit.*`.
Use `./run_all_unit_tests.sh --help` for further information. |
+| **run_all_component_tests.sh** | Runs all (matching) component-test programs.
Performs a build and then recursively runs executable programs matching the shell patterns `test_component*` and `test.component.*`.
Use `./run_all_component_tests.sh --help` for further information. |
+| **run_all_automated_tests.sh** | Runs all (matching) automated test programs: unit tests followed by component tests.
Use `./run_all_automated_tests.sh --help` for further information. |
+| **generate_doxygen.sh** | Generates HTML API documentation from the public headers via **Doxygen**.
Writes to `./_build/doxygen/html/` (see **SIS_CMAKE_BUILD_DIR**); requires `doxygen` on the `PATH`.
Use `./generate_doxygen.sh --help` for further information. |
| **remove_cmake_artefacts.sh** | Removes all known **CMake** artefacts.
Removes known **CMake** build artefacts from the build directory (currently `./_build`) to prepare for complete regeneration using `prepare_cmake.sh`.
Use `./remove_cmake_artefacts.sh --help` for further information. |
diff --git a/NEWS.md b/NEWS.md
index ade40c74..74839ad5 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -3,6 +3,7 @@
| Date | News Item | Details |
| ------------------- | ----------------------------------- | ------- |
+| 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 |
| 4th August 2026 | Release of [STLSoft 1.11.1-rc5](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc5) | `basic_simple_string<>#resize()`; **CHANGES.md**; docs |
diff --git a/build_cmake.sh b/build_cmake.sh
index 55e6c4ac..a675a9da 100755
--- a/build_cmake.sh
+++ b/build_cmake.sh
@@ -1,22 +1,119 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
IgnoreRemainingFlagsAndOptions=0
+SisUseColours=0
Targets=()
# ##########################################################
-# functions
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
-function join_by { local IFS="$1"; shift; echo "$*"; }
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
# ##########################################################
@@ -26,21 +123,16 @@ while [[ $# -gt 0 ]]; do
if [ $IgnoreRemainingFlagsAndOptions -ne 0 ]; then
- Targets+=($1)
-
+ Targets+=("$1")
shift
-
continue
- else
-
- if [ ! ${1:0:1} = '-' ]; then
-
- Targets+=($1)
+ fi
- shift
+ if [ "${1:0:1}" != '-' ]; then
- continue
- fi
+ Targets+=("$1")
+ shift
+ continue
fi
case $1 in
@@ -48,18 +140,33 @@ while [[ $# -gt 0 ]]; do
IgnoreRemainingFlagsAndOptions=1
;;
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--help)
[ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
cat << EOF
Executes CMake-generated artefacts to (re)build project
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ (no arguments)
+ builds all default targets
+
+ ...
+ builds one or more specific targets
+
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
standard flags:
@@ -72,7 +179,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -85,41 +192,26 @@ done
# ##########################################################
# main()
-if [ ! -d "$CMakeDir" ]; then
+if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts"
+ >&2 echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found or not configured; use script 'prepare_cmake.sh' first"
exit 1
-else
-
- cd $CMakeDir
-
- if [ ! -f "$CMakeDir/Makefile" ]; then
-
- >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' does not contain expected file 'Makefile', so a build cannot be performed. It is recommended that you remove all CMake artefacts using script 'remove_cmake_artefacts.sh' followed by regeneration via 'prepare_cmake.sh'"
-
- cd ->/dev/null
-
- exit 1
- else
-
- if [ -z "$Targets" ]; then
-
- echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`)"
- else
-
- echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`) with specific target(s) $(join_by , "${Targets[@]}")"
- fi
+fi
- $MakeCmd ${Targets[*]}
- status=$?
+if [ ${#Targets[@]} -eq 0 ]; then
- cd ->/dev/null
+ echo "Executing build of ${ProjectNameClr} (via cmake --build)"
+ sis_cmake_build
+ status=$?
+else
- exit $status
- fi
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) with specific target(s): ${Targets[*]}"
+ sis_cmake_build "${Targets[@]}"
+ status=$?
fi
+exit $status
-# ############################## end of file ############################# #
+# ############################## end of file ############################# #
diff --git a/clean_cmake.sh b/clean_cmake.sh
index ead9c205..733afbdd 100755
--- a/clean_cmake.sh
+++ b/clean_cmake.sh
@@ -1,13 +1,117 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
+SisUseColours=0
+
+
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
# ##########################################################
@@ -16,18 +120,27 @@ ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--help)
[ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
cat << EOF
-Executes CMake-generated artefacts to clean project
+Cleans CMake-generated build artefacts via cmake --build --target clean
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
standard flags:
@@ -40,7 +153,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -53,35 +166,16 @@ done
# ##########################################################
# main()
-if [ ! -d "$CMakeDir" ]; then
+if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts"
+ >&2 echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found or not configured; nothing to clean"
exit 1
-else
-
- cd $CMakeDir
-
- if [ ! -f "$CMakeDir/Makefile" ]; then
-
- >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' does not contain expected file 'Makefile', so a clean cannot be performed. It is recommended that you remove all CMake artefacts using script 'remove_cmake_artefacts.sh' followed by regeneration via 'prepare_cmake.sh'"
-
- cd ->/dev/null
-
- exit 1
- else
-
- echo "Cleaning ${ProjectName} build (via command \`$MakeCmd clean\`)"
-
- $MakeCmd clean
- status=$?
-
- cd ->/dev/null
-
- exit $status
- fi
fi
+echo "Executing clean of ${ProjectNameClr} (via cmake --build --target clean)"
+sis_cmake_build clean
+exit $?
-# ############################## end of file ############################# #
+# ############################## end of file ############################# #
diff --git a/ctest_cmake.sh b/ctest_cmake.sh
index 24c21723..4e09ee21 100755
--- a/ctest_cmake.sh
+++ b/ctest_cmake.sh
@@ -1,16 +1,119 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
-CMakeVerbose=
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
+CTestVerbose=
RunMake=1
+SisUseColours=0
+
+
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
# ##########################################################
@@ -19,13 +122,17 @@ RunMake=1
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--no-make|-M)
RunMake=0
;;
--verbose|-V)
- CMakeVerbose=--verbose
+ CTestVerbose=--verbose
;;
--help)
@@ -33,15 +140,20 @@ while [[ $# -gt 0 ]]; do
cat << EOF
Runs CMake's CTest test program(s)
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
-M
--no-make
- does not execute CMake and make before running tests
+ does not execute a build before running tests
-V
--verbose
@@ -59,7 +171,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -76,29 +188,48 @@ status=0
if [ $RunMake -ne 0 ]; then
- echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`) and then running all component and unit test programs"
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running CTest"
- mkdir -p $CMakeDir || exit 1
+ mkdir -p "$CMakeDir" || exit 1
- cd $CMakeDir
+ if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- $MakeCmd
- status=$?
+ >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first"
+
+ exit 1
+ fi
- cd ->/dev/null
+ sis_cmake_build
+ status=$?
else
- if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then
+ if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
+
+ >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build"
- >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step"
+ exit 1
fi
fi
if [ $status -eq 0 ]; then
- echo "Running CMake tests"
+ echo
+ echo "Running ${ProjectNameClr} CMake tests"
+
+ # Multi-config generators (e.g. Visual Studio) need -C ;
+ # mirror sis_cmake_build's CMAKE_CONFIGURATION_TYPES detection.
+ ctest_args=(--test-dir "$CMakeDir" --output-on-failure)
+ if [ -n "$CTestVerbose" ]; then
+
+ ctest_args+=("$CTestVerbose")
+ fi
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ ctest_args+=(-C "${SIS_CMAKE_CONFIG:-Release}")
+ fi
- ctest --test-dir $CMakeDir $CMakeVerbose
+ ctest "${ctest_args[@]}"
status=$?
fi
diff --git a/dev/bin/batch_compile_check b/dev/bin/batch_compile_check
index b5274772..2bd4c0a6 100755
--- a/dev/bin/batch_compile_check
+++ b/dev/bin/batch_compile_check
@@ -14,7 +14,7 @@
ScriptPath=$0
-ProjectDir=$(cd $(dirname "$ScriptPath"); pwd)
+ProjectDir=$(cd "$(dirname "$ScriptPath")" && pwd)
Basename=$(basename "$ScriptPath")
IncludeDir="$ProjectDir/../../include"
ScratchDir="$ProjectDir/../../scratch"
diff --git a/doc/mainpage.md b/doc/mainpage.md
new file mode 100644
index 00000000..1ed8959d
--- /dev/null
+++ b/doc/mainpage.md
@@ -0,0 +1,71 @@
+# STLSoft {#mainpage}
+
+**STLSoft** is a suite of **C** and **C++** libraries that provides STL extensions, general-purpose utility components, and facades over operating-system and technology-specific APIs. It is robust, lightweight, and cross-platform.
+
+The overarching characteristic of **STLSoft** is that it is *lightweight*. The libraries are **100% header-only**: all components are defined entirely within header files, so users need only `#include` the requisite files to access the functionality. **STLSoft** is *not* a framework; each component is as thin as possible for its given function, intended as a building block for higher-level software.
+
+
+## Components
+
+The components are organised as *sub-projects* (technology / platform facets), each rooted at its own header directory:
+
+| Sub-project | Header | Summary |
+| ----------- | ------ | ------- |
+| **ACESTL** | `` | Components for the **ACE** framework |
+| **ATLSTL** | `` | Components for the **Active Template Library (ATL)** |
+| **COMSTL** | `` | Components for the **Component Object Model (COM)** |
+| **InetSTL** | `` | Components for internet APIs |
+| **MFCSTL** | `` | Components for the **Microsoft Foundation Classes (MFC)** |
+| **PlatformSTL** | `` | Platform-selected facades over **UnixSTL** or **WinSTL** |
+| **STLSoft** | `` | General-purpose components and base-level features |
+| **UnixSTL** | `` | Components for **Unix** operating-system APIs |
+| **WinSTL** | `` | Components for **Windows** operating-system APIs |
+
+Many functional areas (for example **filesystem**, **dl**, and **memory**) appear in more than one sub-project with *intersecting conformance*: similar components are structurally compatible, without being related by type, only to the degree of the intersection of their functionality.
+
+
+## Quick start
+
+Include the requisite header(s) and use the component; there is nothing to link:
+
+```cpp
+#include
+
+#include
+#include
+
+int main()
+{
+ // up to 128 chars on the stack; beyond that, on the heap
+ stlsoft::auto_buffer buff(10);
+
+ ::memset(&buff[0], '1', buff.size());
+
+ buff.resize(1000, '2');
+
+ return EXIT_SUCCESS;
+}
+```
+
+Consumer **CMake** projects may use `find_package(STLSoft)` and link
+`STLSoft::STLSoft`. Otherwise, define an environment variable `STLSOFT` and
+add `$(STLSOFT)/include` to the include path. See
+[INSTALL.md](https://github.com/synesissoftware/STLSoft/blob/master/INSTALL.md)
+for build and install instructions, and
+[EXAMPLES.md](https://github.com/synesissoftware/STLSoft/blob/master/EXAMPLES.md)
+for the catalogue of example programs.
+
+
+## Related projects
+
+Projects that depend on **STLSoft**:
+
+| Project | Use |
+| ------- | --- |
+| [b64](https://github.com/synesissoftware/b64) | C++ API is implemented in terms of **STLSoft** |
+| [Pantheios](https://github.com/synesissoftware/Pantheios) | Logging API library |
+| [shwild](https://github.com/synesissoftware/shwild) | Optional dependency (`USE_STLSOFT_PKG`) |
+| [xTests](https://github.com/synesissoftware/xTests) | Testing library; used for the **STLSoft** unit and component tests |
+
+
+
diff --git a/examples/by_project/unixstl/filesystem/CMakeLists.txt b/examples/by_project/unixstl/filesystem/CMakeLists.txt
index 7dd9ccf9..06433e78 100644
--- a/examples/by_project/unixstl/filesystem/CMakeLists.txt
+++ b/examples/by_project/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(glob_sequence)
add_subdirectory(readdir_sequence)
+add_subdirectory(rewind_dir)
diff --git a/examples/by_project/unixstl/filesystem/rewind_dir/CMakeLists.txt b/examples/by_project/unixstl/filesystem/rewind_dir/CMakeLists.txt
new file mode 100644
index 00000000..69e650c3
--- /dev/null
+++ b/examples/by_project/unixstl/filesystem/rewind_dir/CMakeLists.txt
@@ -0,0 +1,2 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+define_example_program(example.unixstl.filesystem.rewind_dir main.cpp)
diff --git a/examples/by_project/unixstl/filesystem/rewind_dir/main.cpp b/examples/by_project/unixstl/filesystem/rewind_dir/main.cpp
new file mode 100644
index 00000000..23fec8ac
--- /dev/null
+++ b/examples/by_project/unixstl/filesystem/rewind_dir/main.cpp
@@ -0,0 +1,127 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * Purpose: Illustrates the use of `unixstl::filesystem_traits<>` functions
+ * `open_dir()`, `read_dir()`, `rewind_dir()`, and `close_dir()` to
+ * enumerate a directory in two passes: the first to count the
+ * entries, and the second - after rewinding - to collect their
+ * names into a container sized in advance.
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+
+typedef unixstl::filesystem_traits fs_traits_t;
+
+
+int main(int argc, char* argv[])
+{
+ char const* const program_name = platformstl::get_executable_name_from_path(argv[0]).ptr;
+ char const* root_dir = ss_nullptr_k;
+
+ if (argc > 1 && 0 == strcmp("--help", argv[1]))
+ {
+ std::cout
+ << "USAGE: "
+ << program_name
+ << " []"
+ << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+
+ switch (argc)
+ {
+ case 1:
+
+ root_dir = ".";
+ break;
+ case 2:
+
+ root_dir = argv[1];
+ break;
+
+ default:
+
+ std::cerr
+ << program_name
+ << ": "
+ << "too many arguments; use --help for usage"
+ << std::endl;
+
+ return EXIT_FAILURE;
+ }
+
+ DIR* const h = fs_traits_t::open_dir(root_dir);
+
+ if (NULL == h)
+ {
+ std::cerr
+ << program_name
+ << ": "
+ << "could not open directory '"
+ << root_dir
+ << "'"
+ << std::endl;
+
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ std::vector names;
+ size_t count = 0;
+
+ // pass 1: count the entries
+
+ while (NULL != fs_traits_t::read_dir(h))
+ {
+ ++count;
+ }
+
+ std::cout
+ << "'"
+ << root_dir
+ << "' contains "
+ << count
+ << " entries (including '.' and '..')"
+ << std::endl;
+
+ // rewind, so that the next read_dir() returns the first entry again
+
+ fs_traits_t::rewind_dir(h);
+
+ // pass 2: collect the names, without any reallocation
+
+ names.reserve(count);
+
+ for (struct dirent const* de; NULL != (de = fs_traits_t::read_dir(h)); )
+ {
+ names.push_back(de->d_name);
+ }
+
+ fs_traits_t::close_dir(h);
+
+ for (std::vector::const_iterator i = names.begin(); names.end() != i; ++i)
+ {
+ std::cout
+ << "\t"
+ << *i
+ << std::endl;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/execute_performance_tests.sh b/execute_performance_tests.sh
index e15fbc44..b26b638c 100755
--- a/execute_performance_tests.sh
+++ b/execute_performance_tests.sh
@@ -1,7 +1,7 @@
#! /bin/bash
ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
+Dir=$(cd "$(dirname "$ScriptPath")" && pwd)
Basename=$(basename "$ScriptPath")
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
diff --git a/generate_doxygen.sh b/generate_doxygen.sh
new file mode 100755
index 00000000..504b6619
--- /dev/null
+++ b/generate_doxygen.sh
@@ -0,0 +1,83 @@
+#! /bin/bash
+
+#############################################################################
+# File: generate_doxygen.sh
+#
+# Purpose: Generates HTML API documentation from public headers via Doxygen
+#
+#############################################################################
+
+ScriptPath=$0
+Dir=$(cd "$(dirname "$ScriptPath")" && pwd)
+Basename=$(basename "$ScriptPath")
+CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
+ProjectNameFile="$Dir/.sis/project_name.txt"
+ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+
+
+# ##########################################################
+# command-line handling
+
+while [[ $# -gt 0 ]]; do
+
+ case $1 in
+ --help)
+
+ [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
+ cat << EOF
+Generates HTML API documentation from public headers via Doxygen
+
+$ScriptPath [ ... flags/options ... ]
+
+Flags/options:
+
+ standard flags:
+
+ --help
+ displays this help and terminates
+
+Environment:
+
+ SIS_CMAKE_BUILD_DIR
+ CMake build directory (default: /_build); documentation is
+ written to /doxygen/html/
+
+EOF
+
+ exit 0
+ ;;
+ *)
+
+ >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+
+ exit 1
+ ;;
+ esac
+
+ shift
+done
+
+
+# ##########################################################
+# main()
+
+cd "$Dir" || exit 1
+
+if ! command -v doxygen >/dev/null 2>&1; then
+ >&2 echo "$ScriptPath: doxygen not found on PATH"
+ exit 1
+fi
+
+mkdir -p "${CMakeDir}/doxygen"
+
+{
+ cat Doxyfile
+ echo ""
+ echo "# Output directory (overridden by ${Basename})"
+ echo "OUTPUT_DIRECTORY = ${CMakeDir}/doxygen"
+} | doxygen -
+
+echo "${ProjectName} API documentation written to ${CMakeDir}/doxygen/html/index.html"
+
+
+# ############################## end of file ############################# #
diff --git a/include/inetstl/filesystem/filesystem_traits.hpp b/include/inetstl/filesystem/filesystem_traits.hpp
index 74fe9d1c..e8a2ffa6 100644
--- a/include/inetstl/filesystem/filesystem_traits.hpp
+++ b/include/inetstl/filesystem/filesystem_traits.hpp
@@ -5,11 +5,11 @@
* Unicode specialisations thereof.
*
* Created: 30th April 1999
- * Updated: 20th March 2025
+ * Updated: 8th 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) 1999-2019, Matthew Wilson and Synesis Software
* All rights reserved.
*
@@ -53,10 +53,10 @@
#define INETSTL_INCL_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
-# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 5
-# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 0
-# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 2
-# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 98
+# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 5
+# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 0
+# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 2
+# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 99
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -135,23 +135,23 @@ struct filesystem_traits
/// @{
public:
/// The character type
- typedef C char_type;
+ typedef C char_type;
/// The size type
- typedef is_size_t size_type;
+ typedef is_size_t size_type;
/// The difference type
- typedef is_ptrdiff_t difference_type;
+ typedef is_ptrdiff_t difference_type;
/// The find data type
- typedef WIN32_FIND_DATA find_data_type;
+ typedef WIN32_FIND_DATA find_data_type;
/// The stat data type
- typedef WIN32_FIND_DATA stat_data_type;
+ typedef WIN32_FIND_DATA stat_data_type;
/// The current instantion of the type
- typedef filesystem_traits class_type;
+ typedef filesystem_traits class_type;
/// The (signed) integer type
- typedef is_int_t int_type;
+ typedef is_int_t int_type;
/// The Boolean type
- typedef is_bool_t bool_type;
+ typedef is_bool_t bool_type;
/// The type of system error codes
- typedef DWORD error_type;
+ typedef DWORD error_type;
/// @}
/// \name File-system entry names
@@ -274,15 +274,15 @@ struct filesystem_traits
: public STLSOFT_NS_QUAL(c_string_traits)
{
public:
- typedef is_char_a_t char_type;
- typedef is_size_t size_type;
- typedef is_ptrdiff_t difference_type;
- typedef WIN32_FIND_DATAA find_data_type;
- typedef WIN32_FIND_DATAA stat_data_type;
- typedef filesystem_traits class_type;
- typedef is_int_t int_type;
- typedef is_bool_t bool_type;
- typedef DWORD error_type;
+ typedef is_char_a_t char_type;
+ typedef is_size_t size_type;
+ typedef is_ptrdiff_t difference_type;
+ typedef WIN32_FIND_DATAA find_data_type;
+ typedef WIN32_FIND_DATAA stat_data_type;
+ typedef filesystem_traits class_type;
+ typedef is_int_t int_type;
+ typedef is_bool_t bool_type;
+ typedef DWORD error_type;
public:
// File-system entry names
@@ -553,15 +553,15 @@ struct filesystem_traits
: public STLSOFT_NS_QUAL(c_string_traits)
{
public:
- typedef is_char_w_t char_type;
- typedef is_size_t size_type;
- typedef is_ptrdiff_t difference_type;
- typedef WIN32_FIND_DATAW find_data_type;
- typedef WIN32_FIND_DATAW stat_data_type;
- typedef filesystem_traits class_type;
- typedef is_int_t int_type;
- typedef is_bool_t bool_type;
- typedef DWORD error_type;
+ typedef is_char_w_t char_type;
+ typedef is_size_t size_type;
+ typedef is_ptrdiff_t difference_type;
+ typedef WIN32_FIND_DATAW find_data_type;
+ typedef WIN32_FIND_DATAW stat_data_type;
+ typedef filesystem_traits class_type;
+ typedef is_int_t int_type;
+ typedef is_bool_t bool_type;
+ typedef DWORD error_type;
public:
diff --git a/include/inetstl/inetstl.h b/include/inetstl/inetstl.h
index db1fe52e..aca3309d 100644
--- a/include/inetstl/inetstl.h
+++ b/include/inetstl/inetstl.h
@@ -5,7 +5,7 @@
* platform discriminations, and definitions of types.
*
* Created: 24th April 2004
- * Updated: 17th September 2026
+ * Updated: 8th October 2026
*
* Home: http://stlsoft.org/
*
@@ -50,8 +50,8 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define INETSTL_VER_INETSTL_H_INETSTL_MAJOR 3
# define INETSTL_VER_INETSTL_H_INETSTL_MINOR 11
-# define INETSTL_VER_INETSTL_H_INETSTL_REVISION 3
-# define INETSTL_VER_INETSTL_H_INETSTL_EDIT 578
+# define INETSTL_VER_INETSTL_H_INETSTL_REVISION 4
+# define INETSTL_VER_INETSTL_H_INETSTL_EDIT 579
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/** \file inetstl/inetstl.h
@@ -145,12 +145,13 @@
# define _INETSTL_VER_1_4_2 0x010402ff /*!< Version 1.4.2 (with STLSoft 1.11.1 alpha 8) */
# define _INETSTL_VER_1_5_0_A01 0x01050041 /*!< Version 1.5.0 alpha 1 (with STLSoft 1.11.1 beta 2) */
# define _INETSTL_VER_1_5_0_A02 0x01050042 /*!< Version 1.5.0 alpha 2 (with STLSoft 1.11.1 rc 2) */
+# define _INETSTL_VER_1_5_0_A03 0x01050043 /*!< Version 1.5.0 alpha 3 (with STLSoft 1.11.1 rc 8) */
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
#define _INETSTL_VER_MAJOR 1
#define _INETSTL_VER_MINOR 5
#define _INETSTL_VER_PATCH 0
-#define _INETSTL_VER_ALPHABETA 0x42
+#define _INETSTL_VER_ALPHABETA 0x43
#define _INETSTL_VER \
(0\
@@ -194,8 +195,8 @@
* STLSoft version compatibility check(s)
*/
-#if _STLSOFT_VER < 0x010b0182
-# error This version of the InetSTL libraries requires STLSoft version 1.11.1 beta 2, or later
+#if _STLSOFT_VER < 0x010B01C8
+# error This version of the InetSTL libraries requires STLSoft version 1.11.1 rc 8, or later
#endif /* _STLSOFT_VER */
diff --git a/include/platformstl/platformstl.h b/include/platformstl/platformstl.h
index 3227cca8..d2862a2d 100644
--- a/include/platformstl/platformstl.h
+++ b/include/platformstl/platformstl.h
@@ -5,7 +5,7 @@
* platform discriminations, and definitions of types.
*
* Created: 20th March 2005
- * Updated: 17th September 2026
+ * Updated: 10th October 2026
*
* Home: http://stlsoft.org/
*
@@ -49,8 +49,8 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_MAJOR 1
# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_MINOR 15
-# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_REVISION 8
-# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_EDIT 63
+# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_REVISION 9
+# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_EDIT 64
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/** \file platformstl/platformstl.h
@@ -145,13 +145,14 @@
# define _PLATFORMSTL_VER_1_9_2 0x010902ff /*!< Version 1.9.2 (with STLSoft 1.10.5) */
# define _PLATFORMSTL_VER_1_9_3 0x010903ff /*!< Version 1.9.3 (with STLSoft 1.11.1 alpha 8) */
# define _PLATFORMSTL_VER_1_10_0_A01 0x010a0041 /*!< Version 1.10.0 alpha 1 (with STLSoft 1.11.1 alpha 15) */
-# define _PLATFORMSTL_VER_1_10_0_B01 0x010a0081 /*!< Version 1.10.0 alpha 2 (with STLSoft 1.11.1 rc 3) */
+# define _PLATFORMSTL_VER_1_10_0_B01 0x010A0081 /*!< Version 1.10.0 beta 1 (with STLSoft 1.11.1 rc 3) */
+# define _PLATFORMSTL_VER_1_10_0_B02 0x010A0082 /*!< Version 1.10.0 beta 2 (with STLSoft 1.11.1 rc 8) */
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
#define _PLATFORMSTL_VER_MAJOR 1
#define _PLATFORMSTL_VER_MINOR 10
#define _PLATFORMSTL_VER_PATCH 0
-#define _PLATFORMSTL_VER_ALPHABETA 0x81
+#define _PLATFORMSTL_VER_ALPHABETA 0x82
#define _PLATFORMSTL_VER \
(0\
diff --git a/include/platformstl/process/functions.h b/include/platformstl/process/functions.h
new file mode 100644
index 00000000..d7af5bd0
--- /dev/null
+++ b/include/platformstl/process/functions.h
@@ -0,0 +1,155 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: platformstl/process/functions.h
+ *
+ * Purpose: Process functions.
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * Home: http://stlsoft.org/
+ *
+ * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * - Neither the name(s) of Matthew Wilson and Synesis Information Systems
+ * nor the names of any contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/** \file platformstl/process/functions.h
+ *
+ * \brief [C, C++] Process functions
+ * (\ref group__library__System "System" Library).
+ */
+
+#ifndef PLATFORMSTL_INCL_PLATFORMSTL_PROCESS_H_FUNCTIONS
+#define PLATFORMSTL_INCL_PLATFORMSTL_PROCESS_H_FUNCTIONS
+
+#ifndef PLATFORMSTL_DOCUMENTATION_SKIP_SECTION
+# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_MAJOR 1
+# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_MINOR 0
+# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_REVISION 1
+# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_EDIT 1
+#endif /* !PLATFORMSTL_DOCUMENTATION_SKIP_SECTION */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+#ifndef PLATFORMSTL_INCL_PLATFORMSTL_H_PLATFORMSTL
+# include
+#endif /* !PLATFORMSTL_INCL_PLATFORMSTL_H_PLATFORMSTL */
+#ifdef STLSOFT_TRACE_INCLUDE
+# pragma message(__FILE__)
+#endif /* STLSOFT_TRACE_INCLUDE */
+
+#if 0
+#elif defined(PLATFORMSTL_OS_IS_UNIX)
+
+# include
+#elif defined(PLATFORMSTL_OS_IS_WINDOWS)
+
+# include
+#else
+
+# error Operating system not discriminated
+#endif
+
+#include
+#include
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * namespace
+ */
+
+#if !defined(PLATFORMSTL_NO_NAMESPACE) && \
+ !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
+# if defined(STLSOFT_NO_NAMESPACE)
+/* There is no stlsoft namespace, so must define ::platformstl */
+namespace platformstl
+{
+# else
+/* Define stlsoft::platformstl_project */
+namespace stlsoft
+{
+namespace platformstl_project
+{
+# endif /* STLSOFT_NO_NAMESPACE */
+#endif /* !PLATFORMSTL_NO_NAMESPACE */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * functions (C)
+ */
+
+STLSOFT_INLINE
+int
+platformstl_C_get_current_process_id(void)
+{
+#if 0
+#elif defined(PLATFORMSTL_OS_IS_UNIX)
+
+ return UNIXSTL_NS_QUAL(unixstl_C_get_current_process_id)();
+#elif defined(PLATFORMSTL_OS_IS_WINDOWS)
+
+ return WINSTL_NS_QUAL(winstl_C_get_current_process_id)();
+#else
+
+# error Platform not discriminated
+#endif
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * namespace
+ */
+
+#ifndef PLATFORMSTL_NO_NAMESPACE
+# if defined(STLSOFT_NO_NAMESPACE) || \
+ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
+} /* namespace platformstl */
+# else
+} /* namespace platformstl_project */
+} /* namespace stlsoft */
+# endif /* STLSOFT_NO_NAMESPACE */
+#endif /* !PLATFORMSTL_NO_NAMESPACE */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * inclusion control
+ */
+
+#ifdef STLSOFT_CF_PRAGMA_ONCE_SUPPORT
+# pragma once
+#endif /* STLSOFT_CF_PRAGMA_ONCE_SUPPORT */
+
+#endif /* !PLATFORMSTL_INCL_PLATFORMSTL_PROCESS_H_FUNCTIONS */
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/include/stlsoft/stlsoft.h b/include/stlsoft/stlsoft.h
index 1293605b..51d82f0c 100644
--- a/include/stlsoft/stlsoft.h
+++ b/include/stlsoft/stlsoft.h
@@ -5,7 +5,7 @@
* and platform discriminations, and definitions of types.
*
* Created: 15th January 2002
- * Updated: 30th September 2026
+ * Updated: 10th October 2026
*
* Home: http://stlsoft.org/
*
@@ -55,8 +55,8 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define STLSOFT_VER_STLSOFT_H_STLSOFT_MAJOR 3
# define STLSOFT_VER_STLSOFT_H_STLSOFT_MINOR 57
-# define STLSOFT_VER_STLSOFT_H_STLSOFT_REVISION 19
-# define STLSOFT_VER_STLSOFT_H_STLSOFT_EDIT 607
+# define STLSOFT_VER_STLSOFT_H_STLSOFT_REVISION 20
+# define STLSOFT_VER_STLSOFT_H_STLSOFT_EDIT 608
#else /* ? STLSOFT_DOCUMENTATION_SKIP_SECTION */
/* # include "./internal/doxygen_defs.h" */
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -421,13 +421,14 @@
# define _STLSOFT_VER_1_11_1_RC4 0x010b01c4 /*!< Version 1.11.1 rc 4 (1st July 2026) */
# define _STLSOFT_VER_1_11_1_RC5 0x010b01c5 /*!< Version 1.11.1 rc 5 (4th August 2026) */
# define _STLSOFT_VER_1_11_1_RC6 0x010b01c6 /*!< Version 1.11.1 rc 6 (21st September 2026) */
-# define _STLSOFT_VER_1_11_1_RC7 0x010b01c7 /*!< Version 1.11.1 rc 7 (30th September 2026) */
+# define _STLSOFT_VER_1_11_1_RC7 0x010B01C7 /*!< Version 1.11.1 rc 7 (30th September 2026) */
+# define _STLSOFT_VER_1_11_1_RC8 0x010B01C8 /*!< Version 1.11.1 rc 8 (10th October 2026) */
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
#define _STLSOFT_VER_MAJOR 1
#define _STLSOFT_VER_MINOR 11
#define _STLSOFT_VER_PATCH 1
-#define _STLSOFT_VER_ALPHABETA 0xc7
+#define _STLSOFT_VER_ALPHABETA 0xC8
#define _STLSOFT_VER \
(0\
diff --git a/include/stlsoft/system/environment/functions.h b/include/stlsoft/system/environment/functions.h
index 5b81b824..ce665a09 100644
--- a/include/stlsoft/system/environment/functions.h
+++ b/include/stlsoft/system/environment/functions.h
@@ -4,7 +4,7 @@
* Purpose: Environment functions.
*
* Created: 4th March 2011
- * Updated: 28th September 2026
+ * Updated: 8th October 2026
*
* Home: http://stlsoft.org/
*
@@ -52,9 +52,9 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_MAJOR 1
-# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_MINOR 0
-# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_REVISION 3
-# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_EDIT 15
+# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_MINOR 1
+# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_REVISION 0
+# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_EDIT 18
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -69,6 +69,10 @@
# pragma message(__FILE__)
#endif /* STLSOFT_TRACE_INCLUDE */
+#ifndef STLSOFT_INCL_STLSOFT_MEMORY_H_AUTO_BUFFER
+# include
+#endif /* !STLSOFT_INCL_STLSOFT_MEMORY_H_AUTO_BUFFER */
+
#ifndef STLSOFT_INCL_STLSOFT_INTERNAL_H_SAFESTR
# include
#endif /* !STLSOFT_INCL_STLSOFT_INTERNAL_H_SAFESTR */
@@ -81,6 +85,10 @@
# define STLSOFT_INCL_H_STDLIB
# include
#endif /* !STLSOFT_INCL_H_STDLIB */
+#ifndef STLSOFT_INCL_H_STRING
+# define STLSOFT_INCL_H_STRING
+# include
+#endif /* !STLSOFT_INCL_H_STRING */
/* /////////////////////////////////////////////////////////////////////////
@@ -98,11 +106,13 @@ namespace stlsoft
*/
/** Indicates whether the named environment variable exists.
+ *
+ * \param name The name of the environment variable. May not be NULL;
*
* \retval 0 The environment variable does not exist;
* \retval !0 The environment variable does exist;
*
- * \pre (NULL != name)
+ * \pre NULL != name;
*/
STLSOFT_INLINE
ss_truthy_t
@@ -119,7 +129,7 @@ stlsoft_C_environment_variable_exists_m(
if (0 == e)
{
- if ('\0' == buff[0])
+ if (0 == n)
{
// variable doesn't exist
return 0;
@@ -162,6 +172,155 @@ stlsoft_C_environment_variable_exists_a(
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
+/** Attempts to read a signed 64-bit integer value from the named
+ * environment variable.
+ *
+ * \param name The name of the environment variable. May not be NULL;
+ * \param result Pointer to a variable to receive the numeric result. On
+ * failure it is set to 0;
+ *
+ * \retval 0 The variable does not exist, the value is not a complete
+ * signed 64-bit integer, or the value is out of range. errno is EINVAL or
+ * ERANGE;
+ * \retval !0 The value was parsed and written through \c result;
+ *
+ * \pre NULL != name;
+ * \pre NULL != result;
+ *
+ * \note Interpreted with base 0, as with strtoll(): optional leading
+ * whitespace, an optional sign, then decimal, octal (leading 0), or
+ * hexadecimal (leading 0x). Unlike strtoll(), which accepts a numeric
+ * prefix followed by other characters, the entire value must be consumed.
+ * The complete environment-variable value is read before parsing.
+ */
+STLSOFT_INLINE
+ss_truthy_t
+stlsoft_C_environment_variable_strtoll_m(
+ char const* name
+, ss_sint64_t* result
+) STLSOFT_NOEXCEPT
+{
+ STLSOFT_ASSERT(NULL != name);
+ STLSOFT_ASSERT(NULL != result);
+
+#if 0
+#elif defined(STLSOFT_USING_SAFE_STR_FUNCTIONS) && \
+ defined(STLSOFT_COMPILER_IS_MSVC)
+
+ {
+ STLSOFT_C_AUTO_BUFFER_DECLARE(char, 21, buff);
+ size_t n;
+ errno_t e;
+
+ STLSOFT_C_AUTO_BUFFER_INITIALISE_FROM_INTERNAL(buff);
+
+ e = STLSOFT_NS_GLOBAL(getenv_s)(
+ &n
+ , buff.ptr
+ , buff.size
+ , name
+ );
+
+ if (ERANGE == e)
+ {
+ e = STLSOFT_C_AUTO_BUFFER_RESIZE(buff, n);
+
+ if (0 == e)
+ {
+ e = STLSOFT_NS_GLOBAL(getenv_s)(
+ &n
+ , buff.ptr
+ , buff.size
+ , name
+ );
+ }
+ }
+
+ if (0 != e)
+ {
+ STLSOFT_C_AUTO_BUFFER_FREE(buff);
+
+ errno = e;
+
+ *result = 0;
+
+ return 0;
+ }
+ else
+ {
+ char* endptr;
+
+ errno = 0;
+
+ *result = STLSOFT_NS_GLOBAL(strtoll)(buff.ptr, &endptr, 0);
+
+ if (buff.ptr == endptr ||
+ '\0' != *endptr ||
+ ERANGE == errno)
+ {
+ if (0 == errno)
+ {
+ errno = EINVAL;
+ }
+
+ *result = 0;
+
+ STLSOFT_C_AUTO_BUFFER_FREE(buff);
+
+ return 0;
+ }
+ else
+ {
+ STLSOFT_C_AUTO_BUFFER_FREE(buff);
+
+ return 1;
+ }
+ }
+ }
+#else
+
+ {
+ char const* const value = STLSOFT_NS_GLOBAL(getenv)(name);
+
+ if (NULL == value)
+ {
+ errno = EINVAL;
+
+ *result = 0;
+
+ return 0;
+ }
+ else
+ {
+ char* endptr;
+
+ errno = 0;
+
+ *result = STLSOFT_NS_GLOBAL(strtoll)(value, &endptr, 0);
+
+ if (value == endptr ||
+ '\0' != *endptr ||
+ ERANGE == errno)
+ {
+ if (0 == errno)
+ {
+ errno = EINVAL;
+ }
+
+ *result = 0;
+
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+#endif
+}
+
+
/* /////////////////////////////////////////////////////////////////////////
* language
*/
@@ -176,12 +335,22 @@ stlsoft_C_environment_variable_exists_a(
/** \see stlsoft_C_environment_variable_exists_m
*/
inline
-ss_truthy_t
+bool
environment_variable_exists(
char const* name
-)
+) STLSOFT_NOEXCEPT
{
- return stlsoft_C_environment_variable_exists_m(name);
+ return 0 != stlsoft_C_environment_variable_exists_m(name);
+}
+
+inline
+bool
+environment_variable_strtoll(
+ char const* name
+, ss_sint64_t* result
+) STLSOFT_NOEXCEPT
+{
+ return 0 != stlsoft_C_environment_variable_strtoll_m(name, result);
}
diff --git a/include/unixstl/filesystem/filesystem_traits.hpp b/include/unixstl/filesystem/filesystem_traits.hpp
index b8aab10e..c392cdd0 100644
--- a/include/unixstl/filesystem/filesystem_traits.hpp
+++ b/include/unixstl/filesystem/filesystem_traits.hpp
@@ -5,7 +5,7 @@
* Unicode specialisations thereof.
*
* Created: 15th November 2002
- * Updated: 6th May 2025
+ * Updated: 8th October 2026
*
* Thanks: To Sergey Nikulov, for spotting a preprocessor typo that broke
* GCC -pedantic; to Michal Makowski and Zar Eindl for reporting
@@ -14,7 +14,7 @@
*
* 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) 2002-2019, Matthew Wilson and Synesis Software
* All rights reserved.
*
@@ -59,9 +59,9 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 4
-# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 16
+# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 17
# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 1
-# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 188
+# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 190
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -120,7 +120,7 @@
#endif /* !STLSOFT_INCL_H_FCNTL */
#ifdef _WIN32
# include
-# if defined(STLSOFT_COMPILER_IS_INTEL) || \
+# if defined(STLSOFT_COMPILER_IS_INTEL) ||\
defined(STLSOFT_COMPILER_IS_MSVC)
# include
# endif /* os && compiler */
@@ -177,7 +177,7 @@
*/
#ifndef UNIXSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
/* There is no stlsoft namespace, so must define ::unixstl */
namespace unixstl
@@ -731,6 +731,11 @@ struct filesystem_traits
static DIR* open_dir(char_type const* dir);
/// Read an entry from the file-system search
static struct dirent const* read_dir(DIR* h);
+ /// Resets the file-system search to the first entry, such that the
+ /// next call to read_dir() returns the first entry
+ ///
+ /// \pre NULL != h
+ static void rewind_dir(DIR* h);
/// Closes the handle of the file-system search
static void close_dir(DIR* h);
/// @}
@@ -791,7 +796,7 @@ struct filesystem_traits
static bool_type is_device(char_type const* path);
/// Returns whether the given path represents a socket
static bool_type is_socket(char_type const* path);
-#endif /* OS */
+#endif /* _WIN32 */
/// Returns whether the given path represents a link
static bool_type is_link(char_type const* path);
@@ -811,7 +816,7 @@ struct filesystem_traits
static bool_type is_device(stat_data_type const* stat_data);
/// Returns whether the given stat info represents a socket
static bool_type is_socket(stat_data_type const* stat_data);
-#endif /* OS */
+#endif /* _WIN32 */
/// Returns whether the given stat info represents a link
static bool_type is_link(stat_data_type const* stat_data);
@@ -900,7 +905,8 @@ struct filesystem_traits_
public: // path classification and analysis
static size_type path_max()
{
-#if defined(PATH_MAX)
+#if 0
+#elif defined(PATH_MAX)
return PATH_MAX;
#else /* ? PATH_MAX */
@@ -924,7 +930,7 @@ struct filesystem_traits
typedef us_ptrdiff_t difference_type;
typedef struct stat stat_data_type;
typedef struct stat fstat_data_type;
- typedef filesystem_traits class_type;
+ typedef filesystem_traits class_type;
typedef path_classification_t path_classification_type;
typedef unixstl_C_path_classification_results_m_t path_classification_results_type;
typedef stlsoft_C_string_slice_m_t path_classification_string_slice_type;
@@ -1356,7 +1362,6 @@ struct filesystem_traits
static bool_type is_path_rooted(char_type const* path)
{
UNIXSTL_ASSERT(NULL != path);
-
#ifdef _WIN32
// It might be a UNC path. This is handled by the second test below, but
@@ -1389,7 +1394,6 @@ struct filesystem_traits
)
{
UNIXSTL_ASSERT(NULL != path);
-
#ifdef _WIN32
if (len >= 2)
@@ -1426,7 +1430,6 @@ struct filesystem_traits
static bool_type is_path_absolute(char_type const* path)
{
UNIXSTL_ASSERT(NULL != path);
-
#ifdef _WIN32
// If it's really on Windows, then it can only be absolute if ...
@@ -1460,7 +1463,6 @@ struct filesystem_traits
{
return false;
}
-
#ifdef _WIN32
// If it's really on Windows, then it can only be absolute if ...
@@ -1491,7 +1493,6 @@ struct filesystem_traits
static bool_type is_path_UNC(char_type const* path)
{
UNIXSTL_ASSERT(NULL != path);
-
#ifdef _WIN32
size_type const len = str_len(path);
@@ -1674,7 +1675,6 @@ struct filesystem_traits
{
return true;
}
-
#ifdef _WIN32
if (is_root_drive_(path, cchPath))
@@ -1686,7 +1686,7 @@ struct filesystem_traits
{
return true;
}
-#endif
+#endif /* _WIN32 */
return false;
}
@@ -1763,9 +1763,9 @@ struct filesystem_traits
if (NULL == slash2)
{
-#if 0
+# if 0
WINSTL_API_EXTERNAL_ErrorHandling_SetLastError(ERROR_INVALID_ARGUMENT);
-#endif /* 0 */
+# endif /* 0 */
return 0;
}
@@ -1798,7 +1798,7 @@ struct filesystem_traits
get_full_path_name_impl2(
char_type const* fileName
, size_type const len
- , char_type* buffer
+ , char_type buffer[]
, size_type cchBuffer
)
{
@@ -1828,7 +1828,7 @@ struct filesystem_traits
return get_full_path_name_impl2(cwd.data(), n2 + len, buffer, cchBuffer);
}
-#endif
+#endif /* _WIN32 */
// The next thing to so is determine whether the path is absolute, in
// which case we'll just copy it into the buffer
@@ -1836,10 +1836,10 @@ struct filesystem_traits
#ifdef _WIN32
if (is_path_absolute(fileName))
-#else
+#else /* ? _WIN32 */
if (is_path_rooted(fileName))
-#endif
+#endif /* _WIN32 */
{
// Given path is absolute, so simply copy into buffer
if (NULL == buffer)
@@ -1952,9 +1952,12 @@ struct filesystem_traits
}
// For some reason OS-X's GCC needs this : it's confused.
-#if defined(STLSOFT_COMPILER_IS_GCC) && \
- STLSOFT_GCC_VER >= 40200 && \
- STLSOFT_GCC_VER < 40300
+#if 0
+#elif 1 &&\
+ defined(STLSOFT_COMPILER_IS_GCC) &&\
+ STLSOFT_GCC_VER >= 40200 &&\
+ STLSOFT_GCC_VER < 40300 &&\
+ 1
UNIXSTL_ASSERT(0); // should never get here
return 0;
#endif
@@ -2185,6 +2188,12 @@ return 0;
{
return ::readdir(h);
}
+ static void rewind_dir(DIR* h)
+ {
+ UNIXSTL_ASSERT(NULL != h);
+
+ ::rewinddir(h);
+ }
static void close_dir(DIR* h)
{
::closedir(h);
@@ -2197,9 +2206,12 @@ return 0;
char_type const* dir
)
{
-#if defined(_WIN32) && \
- ( defined(STLSOFT_COMPILER_IS_MSVC) || \
- defined(STLSOFT_COMPILER_IS_INTEL))
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ 0)
return 0 == ::_chdir(dir);
#else /* ? _WIN32 */
@@ -2240,10 +2252,15 @@ return 0;
private:
static char_type const* call_getcwd_(char_type buffer[], size_type cchBuffer)
{
-#if defined(STLSOFT_COMPILER_IS_INTEL) || \
- defined(STLSOFT_COMPILER_IS_MSVC)
+#if 0
+#elif 0 ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ 0
+
return ::_getcwd(buffer, int(cchBuffer));
#else /* ? compiler */
+
return ::getcwd(buffer, int(cchBuffer));
#endif /* compiler */
}
@@ -2256,7 +2273,8 @@ return 0;
, size_type cchBuffer
)
{
-#if defined(_WIN32)
+#if 0
+#elif defined(_WIN32)
char_type local[1 + _MAX_PATH];
size_type const cchLocal = STLSOFT_NUM_ELEMENTS(local);
@@ -2373,7 +2391,7 @@ return 0;
return class_type::stat(path, &sd) && class_type::is_socket(&sd);
}
-#endif /* OS */
+#endif /* _WIN32 */
static bool_type is_link(char_type const* path)
{
#ifdef _WIN32
@@ -2393,7 +2411,6 @@ return 0;
{
UNIXSTL_ASSERT(NULL != path);
UNIXSTL_ASSERT(NULL != stat_data);
-
#ifdef _WIN32
if (NULL != class_type::str_pbrk(path, "*?"))
@@ -2477,42 +2494,42 @@ return 0;
static bool_type is_device(stat_data_type const* stat_data)
{
-#if 1
+# if 1
switch (stat_data->st_mode & S_IFMT)
{
-#ifdef S_IFBLK
+# ifdef S_IFBLK
case S_IFBLK:
-#endif // S_IFBLK
-#ifdef S_IFCHR
+# endif // S_IFBLK
+# ifdef S_IFCHR
case S_IFCHR:
-#endif // S_IFCHR
-#ifdef S_IFIFO
+# endif // S_IFCHR
+# ifdef S_IFIFO
case S_IFIFO:
-#endif // S_IFIFO
-#ifdef S_IFWHT
+# endif // S_IFIFO
+# ifdef S_IFWHT
case S_IFWHT:
-#endif // S_IFWHT
+# endif // S_IFWHT
return true;
default:
return false;
}
-#else /* ? 0 */
+# else /* ? 0 */
return false;
-#endif /* 0 */
+# endif /* 0 */
}
static bool_type is_socket(stat_data_type const* stat_data)
{
-#if 1
+# if 1
return S_IFSOCK == (stat_data->st_mode & S_IFMT);
-#else /* ? 0 */
+# else /* ? 0 */
return filesystem_traits_util_::is_file_type_(stat_data, S_IFSOCK);
-#endif /* 0 */
+# endif /* 0 */
}
-#endif /* OS */
+#endif /* _WIN32 */
static bool_type is_link(stat_data_type const* stat_data)
{
#ifdef _WIN32
@@ -2558,10 +2575,13 @@ return 0;
static bool_type create_directory(char_type const* dir, mode_type permissions)
{
-#if defined(_WIN32) && \
- ( defined(STLSOFT_COMPILER_IS_INTEL) || \
- defined(STLSOFT_COMPILER_IS_GCC) || \
- defined(STLSOFT_COMPILER_IS_MSVC))
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ defined(STLSOFT_COMPILER_IS_GCC) ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ 0)
STLSOFT_SUPPRESS_UNUSED(permissions);
@@ -2580,9 +2600,12 @@ return 0;
static bool_type remove_directory(char_type const* dir)
{
-#if defined(_WIN32) && \
- ( defined(STLSOFT_COMPILER_IS_INTEL) || \
- defined(STLSOFT_COMPILER_IS_MSVC))
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ 0)
return 0 == ::_rmdir(dir);
#else /* ? _WIN32 */
@@ -2615,9 +2638,12 @@ return 0;
static file_handle_type open_file(char_type const* fileName, int oflag, int pmode)
{
-#if defined(_WIN32) && \
- ( defined(STLSOFT_COMPILER_IS_INTEL) || \
- defined(STLSOFT_COMPILER_IS_MSVC))
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ 0)
return ::_open(fileName, oflag, pmode);
#else /* ? _WIN32 */
@@ -2630,9 +2656,12 @@ return 0;
static bool_type close_file(file_handle_type h)
{
-#if defined(_WIN32) && \
- ( defined(STLSOFT_COMPILER_IS_INTEL) || \
- defined(STLSOFT_COMPILER_IS_MSVC))
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ 0)
return 0 == ::_close(h);
#else /* ? _WIN32 */
@@ -2685,7 +2714,7 @@ struct filesystem_traits
typedef us_ptrdiff_t difference_type;
typedef struct stat stat_data_type;
typedef struct stat fstat_data_type;
- typedef filesystem_traits class_type;
+ typedef filesystem_traits class_type;
typedef us_int_t int_type;
typedef us_bool_t bool_type;
typedef int file_handle_type;
@@ -3164,6 +3193,8 @@ struct filesystem_traits
;
static struct dirent const* read_dir(DIR* h)
;
+ static void rewind_dir(DIR* h)
+ ;
static void close_dir(DIR* h)
;
@@ -3199,7 +3230,7 @@ struct filesystem_traits
*/
#ifndef UNIXSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
} // namespace unixstl
# else
diff --git a/include/unixstl/process/functions.h b/include/unixstl/process/functions.h
new file mode 100644
index 00000000..ddf65e78
--- /dev/null
+++ b/include/unixstl/process/functions.h
@@ -0,0 +1,173 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: unixstl/process/functions.h
+ *
+ * Purpose: Process functions.
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * Home: http://stlsoft.org/
+ *
+ * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * - Neither the name(s) of Matthew Wilson and Synesis Information Systems
+ * nor the names of any contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/** \file unixstl/process/functions.h
+ *
+ * \brief [C, C++] Process functions
+ * (\ref group__library__System "System" Library).
+ */
+
+#ifndef UNIXSTL_INCL_UNIXSTL_PROCESS_H_FUNCTIONS
+#define UNIXSTL_INCL_UNIXSTL_PROCESS_H_FUNCTIONS
+
+#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
+# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_MAJOR 1
+# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_MINOR 0
+# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_REVISION 1
+# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_EDIT 1
+#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+#ifndef UNIXSTL_INCL_UNIXSTL_H_UNIXSTL
+# include
+#endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */
+#ifdef STLSOFT_TRACE_INCLUDE
+# pragma message(__FILE__)
+#endif /* STLSOFT_TRACE_INCLUDE */
+
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ 0)
+
+# ifndef STLSOFT_INCL_H_PROCESS
+# define STLSOFT_INCL_H_PROCESS
+# include
+# endif /* !STLSOFT_INCL_H_PROCESS */
+#else
+
+# ifndef STLSOFT_INCL_SYS_H_TYPES
+# define STLSOFT_INCL_SYS_H_TYPES
+# include
+# endif /* !STLSOFT_INCL_SYS_H_TYPES */
+# ifndef STLSOFT_INCL_H_UNISTD
+# define STLSOFT_INCL_H_UNISTD
+# include
+# endif /* !STLSOFT_INCL_H_UNISTD */
+#endif
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * namespace
+ */
+
+#if !defined(UNIXSTL_NO_NAMESPACE) && \
+ !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
+# if defined(STLSOFT_NO_NAMESPACE)
+/* There is no stlsoft namespace, so must define ::unixstl */
+namespace unixstl
+{
+# else
+/* Define stlsoft::unixstl_project */
+namespace stlsoft
+{
+namespace unixstl_project
+{
+# endif /* STLSOFT_NO_NAMESPACE */
+#endif /* !UNIXSTL_NO_NAMESPACE */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * functions
+ */
+
+/** \brief Obtains the id of the calling process.
+ *
+ * \ingroup group__library__System
+ *
+ * \return The process id, as obtained from \c _getpid() on Windows / UNIXem
+ * builds or \c getpid() otherwise.
+ */
+STLSOFT_INLINE
+int
+unixstl_C_get_current_process_id(void)
+{
+#if 0
+#elif defined(_WIN32) && \
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ 0)
+
+ return STLSOFT_NS_GLOBAL(_getpid)();
+#else
+
+ STLSOFT_STATIC_ASSERT(sizeof(pid_t) <= sizeof(int));
+
+ return STLSOFT_STATIC_CAST(int, STLSOFT_NS_GLOBAL(getpid)());
+#endif
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * namespace
+ */
+
+#ifndef UNIXSTL_NO_NAMESPACE
+# if defined(STLSOFT_NO_NAMESPACE) || \
+ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
+} /* namespace unixstl */
+# else
+} /* namespace unixstl_project */
+} /* namespace stlsoft */
+# endif /* STLSOFT_NO_NAMESPACE */
+#endif /* !UNIXSTL_NO_NAMESPACE */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * inclusion control
+ */
+
+#ifdef STLSOFT_CF_PRAGMA_ONCE_SUPPORT
+# pragma once
+#endif /* STLSOFT_CF_PRAGMA_ONCE_SUPPORT */
+
+#endif /* !UNIXSTL_INCL_UNIXSTL_PROCESS_H_FUNCTIONS */
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/include/unixstl/system/system_traits.hpp b/include/unixstl/system/system_traits.hpp
index 1dd668ff..3be24177 100644
--- a/include/unixstl/system/system_traits.hpp
+++ b/include/unixstl/system/system_traits.hpp
@@ -5,11 +5,11 @@
* Unicode specialisations thereof.
*
* Created: 15th November 2002
- * Updated: 20th March 2025
+ * Updated: 8th 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) 2002-2019, Matthew Wilson and Synesis Software
* All rights reserved.
*
@@ -55,8 +55,8 @@
#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 2
-# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_EDIT 140
+# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_REVISION 3
+# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_EDIT 141
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -86,8 +86,9 @@
# include
#endif /* !STLSOFT_INCL_STLSOFT_API_external_h_string */
-#if defined(_WIN32) || \
- defined(_WIN64)
+#if 0
+#elif defined(_WIN32) ||\
+ defined(_WIN64)
# include
#endif /* Windows */
#ifndef STLSOFT_INCL_H_ERRNO
@@ -98,10 +99,11 @@
# define STLSOFT_INCL_H_FCNTL
# include
#endif /* !STLSOFT_INCL_H_FCNTL */
-#if defined(_WIN32) || \
- defined(_WIN64)
+#if 0
+#elif defined(_WIN32) ||\
+ defined(_WIN64)
# include
-# if defined(STLSOFT_COMPILER_IS_INTEL) || \
+# if defined(STLSOFT_COMPILER_IS_INTEL) ||\
defined(STLSOFT_COMPILER_IS_MSVC)
# include
# endif /* os && compiler */
@@ -149,7 +151,7 @@
*/
#ifndef UNIXSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
/* There is no stlsoft namespace, so must define ::unixstl */
namespace unixstl
@@ -408,9 +410,12 @@ struct system_traits_
public:
static bool_type close_handle(handle_type h)
{
-#if defined(_WIN32) && \
- ( defined(STLSOFT_COMPILER_IS_MSVC) || \
- defined(STLSOFT_COMPILER_IS_INTEL))
+#if 0
+#elif defined(_WIN32) &&\
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ 0)
return 0 == ::_close(h);
#else /* ? _WIN32 */
@@ -462,7 +467,7 @@ struct system_traits
typedef us_char_a_t char_type;
typedef us_size_t size_type;
typedef us_ptrdiff_t difference_type;
- typedef system_traits class_type;
+ typedef system_traits class_type;
typedef us_int_t int_type;
typedef us_bool_t bool_type;
typedef void* module_type;
@@ -484,7 +489,6 @@ struct system_traits
for (;; ++ps1, ++ps2)
{
int_type d = int_type(*ps1) - int_type(*ps2);
-
#ifdef _WIN32
if (0 != d)
@@ -540,7 +544,6 @@ struct system_traits
for (; 0 != cch; ++ps1, ++ps2, --cch)
{
int_type d = int_type(*ps1) - int_type(*ps2);
-
#ifdef _WIN32
if (0 != d)
@@ -743,7 +746,7 @@ struct system_traits
typedef us_char_w_t char_type;
typedef us_size_t size_type;
typedef us_ptrdiff_t difference_type;
- typedef system_traits class_type;
+ typedef system_traits class_type;
typedef us_int_t int_type;
typedef us_bool_t bool_type;
typedef void* module_type;
@@ -869,7 +872,7 @@ struct system_traits
/* ////////////////////////////////////////////////////////////////////// */
#ifndef UNIXSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
} // namespace unixstl
# else
diff --git a/include/unixstl/unixstl.h b/include/unixstl/unixstl.h
index 390583b5..33335107 100644
--- a/include/unixstl/unixstl.h
+++ b/include/unixstl/unixstl.h
@@ -5,7 +5,7 @@
* platform discriminations, and definitions of types.
*
* Created: 15th January 2002
- * Updated: 17th September 2026
+ * Updated: 10th October 2026
*
* Home: http://stlsoft.org/
*
@@ -50,8 +50,8 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_MAJOR 3
# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_MINOR 11
-# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_REVISION 3
-# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_EDIT 130
+# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_REVISION 5
+# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_EDIT 132
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/** \file unixstl/unixstl.h
@@ -163,12 +163,13 @@
# define _UNIXSTL_VER_1_8_6_B04 0x01080684 /*!< Version 1.8.8 beta 4 (with STLSoft 1.11.1 beta 8) */
# define _UNIXSTL_VER_1_8_6_B05 0x01080685 /*!< Version 1.8.8 beta 5 (with STLSoft 1.11.1 rc 2) */
# define _UNIXSTL_VER_1_8_6_B06 0x01080686 /*!< Version 1.8.8 beta 6 (with STLSoft 1.11.1 rc 3) */
+# define _UNIXSTL_VER_1_8_6_B07 0x01080687 /*!< Version 1.8.8 beta 7 (with STLSoft 1.11.1 rc 8) */
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
#define _UNIXSTL_VER_MAJOR 1
#define _UNIXSTL_VER_MINOR 8
#define _UNIXSTL_VER_PATCH 6
-#define _UNIXSTL_VER_ALPHABETA 0x86
+#define _UNIXSTL_VER_ALPHABETA 0x87
#define _UNIXSTL_VER \
(0\
@@ -199,8 +200,8 @@
* STLSoft version compatibility check(s)
*/
-#if _STLSOFT_VER < 0x010b0187
-# error This version of the UNIXSTL libraries requires STLSoft version 1.11.1 beta 7, or later
+#if _STLSOFT_VER < 0x010B01C8
+# error This version of the UNIXSTL libraries requires STLSoft version 1.11.1 rc 8, or later
#endif /* _STLSOFT_VER */
diff --git a/include/winstl/filesystem/filesystem_traits.hpp b/include/winstl/filesystem/filesystem_traits.hpp
index fa1f9f8a..fe194b8a 100644
--- a/include/winstl/filesystem/filesystem_traits.hpp
+++ b/include/winstl/filesystem/filesystem_traits.hpp
@@ -149,7 +149,7 @@
*/
#ifndef WINSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
/* There is no stlsoft namespace, so must define ::winstl */
namespace winstl
@@ -973,7 +973,7 @@ struct filesystem_traits
typedef DWORD error_type;
typedef DWORD file_attributes_type;
private:
-#if !defined(STLSOFT_COMPILER_IS_MSVC) || \
+#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\
_MSC_VER >= 1200
typedef STLSOFT_NS_QUAL(auto_buffer) buffer_type_;
#endif /* compiler */
@@ -1696,9 +1696,10 @@ struct filesystem_traits
{
return WINSTL_CONST_MAX_PATH;
}
+#if 0
+#elif defined(STLSOFT_COMPILER_IS_MSVC) &&\
+ _MSC_VER < 1200
-#if defined(STLSOFT_COMPILER_IS_MSVC) && \
- _MSC_VER < 1200
static
size_type
get_full_path_name(
@@ -2236,7 +2237,7 @@ struct filesystem_traits
WINSTL_ASSERT(NULL != path);
WINSTL_ASSERT(NULL != stat_data);
-#if !defined(STLSOFT_COMPILER_IS_MSVC) || \
+#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\
_MSC_VER >= 1200
size_type const len = class_type::str_len(path);
@@ -2594,7 +2595,7 @@ struct filesystem_traits
typedef DWORD error_type;
typedef DWORD file_attributes_type;
private:
-#if !defined(STLSOFT_COMPILER_IS_MSVC) || \
+#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\
_MSC_VER >= 1200
typedef STLSOFT_NS_QUAL(auto_buffer) buffer_type_;
#endif /* compiler */
@@ -3596,7 +3597,7 @@ struct filesystem_traits
WINSTL_ASSERT(NULL != path);
WINSTL_ASSERT(NULL != stat_data);
-#if !defined(STLSOFT_COMPILER_IS_MSVC) || \
+#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\
_MSC_VER >= 1200
size_type const len = class_type::str_len(path);
@@ -3933,7 +3934,7 @@ struct filesystem_traits
*/
#ifndef WINSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
} // namespace winstl
# else
diff --git a/include/winstl/process/functions.h b/include/winstl/process/functions.h
index ed7f362b..b5915821 100644
--- a/include/winstl/process/functions.h
+++ b/include/winstl/process/functions.h
@@ -4,11 +4,11 @@
* Purpose: Process functions.
*
* Created: 12th March 2006
- * Updated: 24th December 2024
+ * Updated: 8th October 2026
*
* Home: http://stlsoft.org/
*
- * Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
+ * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems
* Copyright (c) 2006-2019, Matthew Wilson and Synesis Software
* All rights reserved.
*
@@ -52,9 +52,9 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_MAJOR 1
-# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_MINOR 1
-# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_REVISION 7
-# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_EDIT 35
+# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_MINOR 2
+# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_REVISION 1
+# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_EDIT 37
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -69,6 +69,11 @@
# pragma message(__FILE__)
#endif /* STLSOFT_TRACE_INCLUDE */
+#ifndef STLSOFT_INCL_H_PROCESS
+# define STLSOFT_INCL_H_PROCESS
+# include
+#endif /* !STLSOFT_INCL_H_PROCESS */
+
#ifndef STLSOFT_INCL_STLSOFT_QUALITY_H_CONTRACT
# include
#endif /* !STLSOFT_INCL_STLSOFT_QUALITY_H_CONTRACT */
@@ -144,6 +149,8 @@ winstl_C_CreateProcess9_a(
, LPPROCESS_INFORMATION pi
)
{
+ /* apply Null Object (Variable) pattern */
+
STARTUPINFO si_;
PROCESS_INFORMATION pi_;
BOOL b;
@@ -158,7 +165,18 @@ winstl_C_CreateProcess9_a(
pi = &pi_;
}
- b = STLSOFT_NS_GLOBAL(CreateProcessA)(NULL, stlsoft_const_cast(ws_char_a_t*, cmdLine), processAttributes, threadAttributes, inheritsHandles, creationFlags, stlsoft_const_cast(ws_char_a_t*, environment), currentDirectory, si, pi);
+ b = STLSOFT_NS_GLOBAL(CreateProcessA)(
+ NULL
+ , stlsoft_const_cast(ws_char_a_t*, cmdLine)
+ , processAttributes
+ , threadAttributes
+ , inheritsHandles
+ , creationFlags
+ , stlsoft_const_cast(ws_char_a_t*, environment)
+ , currentDirectory
+ , si
+ , pi
+ );
if (b)
{
@@ -228,6 +246,21 @@ winstl_C_CreateProcess0A(
return winstl_C_CreateProcessEA(cmdLine, NULL);
}
+/**
+ * \brief Obtains the id of the calling process.
+ *
+ * \ingroup group__library__System
+ *
+ * \return The process id, as obtained from \c _getpid(), which equals
+ * \c GetCurrentProcessId() cast to \c int.
+ */
+STLSOFT_INLINE
+int
+winstl_C_get_current_process_id(void)
+{
+ return STLSOFT_NS_GLOBAL(_getpid)();
+}
+
/* /////////////////////////////////////////////////////////////////////////
* obsolete functions
diff --git a/include/winstl/system/system_traits.hpp b/include/winstl/system/system_traits.hpp
index 74b0bc17..bc65f0af 100644
--- a/include/winstl/system/system_traits.hpp
+++ b/include/winstl/system/system_traits.hpp
@@ -82,8 +82,9 @@
# include
#endif /* !STLSOFT_INCL_STLSOFT_INTERNAL_H_SAFESTR */
-#if defined(WINSTL_OS_IS_WIN64) || \
- defined(_Wp64)
+#if 0
+#elif defined(WINSTL_OS_IS_WIN64) ||\
+ defined(_Wp64)
# define _WINSTL_SYSTEM_TRAITS_USE_TRUNCATION_TESTING
#endif /* _WIN64 || _M_IA64 */
@@ -150,7 +151,7 @@
*/
#ifndef WINSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
/* There is no stlsoft namespace, so must define ::winstl */
namespace winstl
@@ -1854,7 +1855,7 @@ struct system_traits
*/
#ifndef WINSTL_NO_NAMESPACE
-# if defined(STLSOFT_NO_NAMESPACE) || \
+# if defined(STLSOFT_NO_NAMESPACE) ||\
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
} // namespace winstl
# else
diff --git a/include/winstl/winstl.h b/include/winstl/winstl.h
index 361f6292..592659ec 100644
--- a/include/winstl/winstl.h
+++ b/include/winstl/winstl.h
@@ -5,7 +5,7 @@
* platform discriminations, and definitions of types.
*
* Created: 15th January 2002
- * Updated: 30th September 2026
+ * Updated: 8th October 2026
*
* Home: http://stlsoft.org/
*
@@ -50,8 +50,8 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define WINSTL_VER_WINSTL_H_WINSTL_MAJOR 3
# define WINSTL_VER_WINSTL_H_WINSTL_MINOR 18
-# define WINSTL_VER_WINSTL_H_WINSTL_REVISION 14
-# define WINSTL_VER_WINSTL_H_WINSTL_EDIT 244
+# define WINSTL_VER_WINSTL_H_WINSTL_REVISION 15
+# define WINSTL_VER_WINSTL_H_WINSTL_EDIT 245
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/** \file winstl/winstl.h
@@ -184,12 +184,13 @@
# define _WINSTL_VER_1_13_0_B01 0x010d0081 /*!< Version 1.13.0 beta 1 (with STLSoft 1.11.1 rc 3) */
# define _WINSTL_VER_1_13_0_B02 0x010d0082 /*!< Version 1.13.0 beta 2 (with STLSoft 1.11.1 rc 4) */
# define _WINSTL_VER_1_13_0_B03 0x010d0083 /*!< Version 1.13.0 beta 3 (with STLSoft 1.11.1 rc 7) */
+# define _WINSTL_VER_1_13_0_B04 0x010d0084 /*!< Version 1.13.0 beta 4 (with STLSoft 1.11.1 rc 8) */
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
#define _WINSTL_VER_MAJOR 1
#define _WINSTL_VER_MINOR 13
#define _WINSTL_VER_PATCH 0
-#define _WINSTL_VER_ALPHABETA 0x83
+#define _WINSTL_VER_ALPHABETA 0x84
#define _WINSTL_VER \
(0\
@@ -290,8 +291,8 @@
* STLSoft version compatibility check(s)
*/
-#if _STLSOFT_VER < 0x010b0151
-# error This version of the WinSTL libraries requires STLSoft version 1.11.1 alpha 17, or later
+#if _STLSOFT_VER < 0x010B01C8
+# error This version of the WinSTL libraries requires STLSoft version 1.11.1 rc 8, or later
#endif /* _STLSOFT_VER */
diff --git a/prepare_cmake.sh b/prepare_cmake.sh
index b1f2da31..6852f32c 100755
--- a/prepare_cmake.sh
+++ b/prepare_cmake.sh
@@ -1,41 +1,146 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
-CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-if [[ -n "$MSYSTEM" ]]; then
+# ##########################################################
+# functions - 1
- DefaultMakeCmd=mingw32-make.exe
- MinGW=1
-else
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
- DefaultMakeCmd=make
-fi
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
+CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
-
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
+BuildSharedLibs=0
Configuration=Release
ExamplesDisabled=0
+MinGW=$(sis_cmake_is_truey "${SIS_CMAKE_MINGW:-}" && echo 1 || echo 0)
MSVC_MT=0
-MinGW="${MinGW:=0}"
NO_ACE=0
NO_cstring=0
NO_shwild=0
RunMake=0
+SisUseColours=0
+STLSoftDirGiven=
TestingDisabled=0
USE_UNIXem=0
VerboseMakefile=0
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
+
+
# ##########################################################
# command-line handling
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
+ --build-shared-libs)
+
+ BuildSharedLibs=1
+ ;;
--cmake-verbose-makefile|-v)
VerboseMakefile=1
@@ -76,6 +181,11 @@ while [[ $# -gt 0 ]]; do
RunMake=1
;;
+ --stlsoft-root-dir|-s)
+
+ shift
+ STLSoftDirGiven=$1
+ ;;
--use-unixem)
USE_UNIXem=1
@@ -86,41 +196,43 @@ while [[ $# -gt 0 ]]; do
cat << EOF
Creates/reinitialises the CMake build script(s)
-$ScriptPath [ ... flags/options ... ]
-
-IMPORTANT: If you have not previously performed a CMake installation of xTests, the library that STLSoft uses for its own tests, you MUST specify the -E and -T flags. Once you have installed STLSoft, you can then configure/build/install STLSoft, and then come back and prepare STLSoft without -E/-T and exercise its examples and tests.
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
+ --build-shared-libs
+ builds ${ProjectName} as a shared library (BUILD_SHARED_LIBS=ON)
+
-v
--cmake-verbose-makefile
- configures CMake to run verbosely (by setting CMAKE_VERBOSE_MAKEFILE
- to be ON)
+ configures CMake to run verbosely (CMAKE_VERBOSE_MAKEFILE=ON)
-d
--debug-configuration
- use Debug configuration (by setting CMAKE_BUILD_TYPE=Debug). Default
- is to use Release
+ use Debug configuration (CMAKE_BUILD_TYPE=Debug). Default is Release
-E
--disable-examples
- disables building of examples (by setting BUILD_EXAMPLES=OFF)
+ disables building of examples (BUILD_EXAMPLES=OFF)
-T
--disable-testing
- disables building of tests (by setting BUILD_TESTING=OFF). This is
- necessary, for example, when installing on a system that does not
- (yet) have xTests - which itself depends on STLSOFT - installed
+ disables building of tests (BUILD_TESTING=OFF). Unless testing is
+ disabled the STLSoft and xTests libraries will be required to be
+ available to CMake
--mingw
uses explicitly the "MinGW Makefiles" generator
--msvc-mt
- when using Visual C++ (MSVC), the static runtime library will be
- selected; the default is the dynamic runtime library. Has no effect
- when not using Visual C++
+ when using Visual C++ (MSVC), select the static runtime library
--no-ace
suppresses discovery of ACE (ACESTL examples/tests will not be built)
@@ -129,17 +241,24 @@ Flags/options:
suppresses discovery of cstring package
--no-shwild
- suppresses discovery of shwild package
+ prevents recognising shwild library (NO_SHWILD=ON); xTests
+ pattern-match assertions are then unavailable
-m
--run-make
- executes make after a successful running of CMake
+ executes a build via cmake --build after a successful configure
+
+ -s
+ --stlsoft-root-dir
+ specifies the STLSoft root-directory, which will be passed to CMake
+ as the variable STLSOFT, and which will override the environment
+ variable STLSOFT (if present)
--use-unixem
- when building on Windows, use the UNIXem library and define the
- preprocessor symbol _STLSOFT_FORCE_ANY_COMPILER so as to emulate and
- exercise UNIXSTL, not WinSTL (or COMSTL, etc.). Has no effect when not
- executing on Windows
+ when building on Windows, use the UNIXem library and define the
+ preprocessor symbol _STLSOFT_FORCE_ANY_COMPILER so as to emulate and
+ exercise UNIXSTL, not WinSTL (or COMSTL, etc.). Has no effect when
+ not executing on Windows
standard flags:
@@ -147,15 +266,13 @@ Flags/options:
--help
displays this help and terminates
-IMPORTANT: If you have not previously performed a CMake installation of xTests, the library that STLSoft uses for its own tests, you MUST specify the -E and -T flags. Once you have installed STLSoft, you can then configure/build/install STLSoft, and then come back and prepare STLSoft without -E/-T and exercise its examples and tests.
-
EOF
exit 0
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -168,72 +285,74 @@ done
# ##########################################################
# main()
-mkdir -p $CMakeDir || exit 1
+mkdir -p "$CMakeDir" || exit 1
-cd $CMakeDir
-
-echo "Executing CMake for ${ProjectName} (in ${CMakeDir})"
+echo
+echo "Executing CMake for ${ProjectNameClr} (in ${CMakeDirClr})"
+if [ $BuildSharedLibs -eq 0 ]; then CMakeBuildSharedLibsFlag="OFF" ; else CMakeBuildSharedLibsFlag="ON" ; fi
if [ $ExamplesDisabled -eq 0 ]; then CMakeBuildExamplesFlag="ON" ; else CMakeBuildExamplesFlag="OFF" ; fi
if [ $MSVC_MT -eq 0 ]; then CMakeMsvcMtFlag="OFF" ; else CMakeMsvcMtFlag="ON" ; fi
if [ $NO_ACE -eq 0 ]; then CMakeNoACE="OFF" ; else CMakeNoACE="ON" ; fi
if [ $NO_cstring -eq 0 ]; then CMakeNoCstring="OFF" ; else CMakeNoCstring="ON" ; fi
if [ $NO_shwild -eq 0 ]; then CMakeNoShwild="OFF" ; else CMakeNoShwild="ON" ; fi
+if [ -z "$STLSoftDirGiven" ]; then CMakeSTLSoftVariable="" ; else CMakeSTLSoftVariable="-DSTLSOFT=$STLSoftDirGiven/" ; fi
if [ $TestingDisabled -eq 0 ]; then CMakeBuildTestingFlag="ON" ; else CMakeBuildTestingFlag="OFF" ; fi
if [ $USE_UNIXem -ne 0 ]; then CMakeUSE_UNIXem="ON" ; else CMakeUSE_UNIXem="OFF" ; fi
if [ $VerboseMakefile -eq 0 ]; then CMakeVerboseMakefileFlag="OFF" ; else CMakeVerboseMakefileFlag="ON" ; fi
+# NOTE: the generator is the *only* thing that may differ between the MinGW
+# and the default paths; every -D option is passed in both cases, so that no
+# flag can be silently ignored according to the generator selected.
+
+CMakeGeneratorArgs=()
+
+if [ -n "${SIS_CMAKE_GENERATOR:-}" ] && [ $MinGW -eq 0 ]; then
+
+ CMakeGeneratorArgs=(-G "$SIS_CMAKE_GENERATOR")
+fi
+
if [ $MinGW -ne 0 ]; then
- cmake \
- -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \
- -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \
- -DCMAKE_BUILD_TYPE=$Configuration \
- -DNO_ACE:BOOL=$CMakeNoACE \
- -DNO_CSTRING:BOOL=$CMakeNoCstring \
- -DNO_SHWILD:BOOL=$CMakeNoShwild \
- -DUSE_UNIXEM:BOOL=$CMakeUSE_UNIXem \
- -G "MinGW Makefiles" \
- -S $Dir \
- -B $CMakeDir \
- || (cd ->/dev/null ; exit 1)
-else
-
- cmake \
- -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \
- -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \
- -DCMAKE_BUILD_TYPE=$Configuration \
- -DCMAKE_VERBOSE_MAKEFILE:BOOL=$CMakeVerboseMakefileFlag \
- -DMSVC_USE_MT:BOOL=$CMakeMsvcMtFlag \
- -DNO_ACE:BOOL=$CMakeNoACE \
- -DNO_CSTRING:BOOL=$CMakeNoCstring \
- -DNO_SHWILD:BOOL=$CMakeNoShwild \
- -DUSE_UNIXEM:BOOL=$CMakeUSE_UNIXem \
- -S $Dir \
- -B $CMakeDir \
- || (cd ->/dev/null ; exit 1)
+ CMakeGeneratorArgs=(-G "MinGW Makefiles")
fi
+cmake \
+ -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \
+ -DBUILD_SHARED_LIBS:BOOL=$CMakeBuildSharedLibsFlag \
+ -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \
+ -DCMAKE_BUILD_TYPE=$Configuration \
+ -DCMAKE_VERBOSE_MAKEFILE:BOOL=$CMakeVerboseMakefileFlag \
+ -DMSVC_USE_MT:BOOL=$CMakeMsvcMtFlag \
+ -DNO_ACE:BOOL=$CMakeNoACE \
+ -DNO_CSTRING:BOOL=$CMakeNoCstring \
+ -DNO_SHWILD:BOOL=$CMakeNoShwild \
+ -DUSE_UNIXEM:BOOL=$CMakeUSE_UNIXem \
+ $CMakeSTLSoftVariable \
+ "${CMakeGeneratorArgs[@]}" \
+ -B "$CMakeDir" \
+ -S "$Dir" \
+ || exit 1
+
status=0
if [ $RunMake -ne 0 ]; then
- echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`)"
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build)"
- $MakeCmd
+ sis_cmake_build
status=$?
fi
-cd ->/dev/null
-
if [ $VerboseMakefile -ne 0 ]; then
- echo -e "contents of $CMakeDir:"
- ls -al $CMakeDir
+ echo
+ echo -e "contents of ${CMakeDirClr}:"
+ ls -al "$CMakeDir"
fi
exit $status
# ############################## end of file ############################# #
-
diff --git a/remove_cmake_artefacts.sh b/remove_cmake_artefacts.sh
index 97c1bc00..377d3ab8 100755
--- a/remove_cmake_artefacts.sh
+++ b/remove_cmake_artefacts.sh
@@ -1,17 +1,20 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
Directories=(
CMakeFiles
Testing
cmake
- docs
examples
projects
src
@@ -21,19 +24,77 @@ Files=(
CMakeCache.txt
CTestTestfile.cmake
DartConfiguration.tcl
- Doxyfile
Makefile
cmake_install.cmake
install_manifest.txt
)
+SisUseColours=0
+
+
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
# ##########################################################
# operating environment detection
-OsName="$(uname -s)"
+OsName="$(uname -s 2>/dev/null || echo Unknown)"
case "${OsName}" in
- CYGWIN*|MINGW*|MSYS_NT*)
+ CYGWIN*|MINGW*|MSYS_NT*|Windows_NT)
Directories+=(
ARM64
@@ -45,9 +106,6 @@ case "${OsName}" in
"*.sln"
"*.vcxproj"
)
- ;;
- *)
-
;;
esac
@@ -58,18 +116,27 @@ esac
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--help)
[ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
cat << EOF
Removes all known CMake artefacts
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
standard flags:
@@ -82,7 +149,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -97,59 +164,50 @@ done
if [ ! -d "$CMakeDir" ]; then
- echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts"
+ echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts"
exit 0
-else
-
- echo "Removing all ${ProjectName} cmake artefacts in '$CMakeDir'"
+fi
- num_dirs_removed=0
- num_files_removed=0
+echo "Removing all ${ProjectNameClr} cmake artefacts in '${CMakeDirClr}'"
- for d in ${Directories[@]}
- do
+num_dirs_removed=0
+num_files_removed=0
- fq_dir_path="$CMakeDir/$d"
+for d in "${Directories[@]}"; do
- [ -d "$fq_dir_path" ] || continue
+ fq_dir_path="$CMakeDir/$d"
- echo "removing directory '$d'"
+ [ -d "$fq_dir_path" ] || continue
- rm -dfr "$fq_dir_path"
+ echo "removing directory '$d'"
- num_dirs_removed=$((num_dirs_removed+1))
- done
+ rm -dfr "$fq_dir_path"
- cd "$CMakeDir"
+ num_dirs_removed=$((num_dirs_removed+1))
+done
- for f in ${Files[@]}
- do
+for f in "${Files[@]}"; do
- for fq_file_path in $f
- do
+ for fq_file_path in "$CMakeDir"/$f; do
- [ -f "$fq_file_path" ] || continue
+ [ -f "$fq_file_path" ] || continue
- echo "removing file '$fq_file_path'"
+ echo "removing file '$fq_file_path'"
- rm -f "$fq_file_path"
+ rm -f "$fq_file_path"
- num_files_removed=$((num_files_removed+1))
- done
+ num_files_removed=$((num_files_removed+1))
done
+done
- cd ->/dev/null
-
- if [ 0 -eq $num_dirs_removed ] && [ 0 -eq $num_files_removed ]; then
+if [ 0 -eq $num_dirs_removed ] && [ 0 -eq $num_files_removed ]; then
- echo "nothing to do"
- else
+ echo "nothing to do"
+else
- echo "removed $num_dirs_removed directories and $num_files_removed files"
- fi
+ echo "removed $num_dirs_removed directories and $num_files_removed files"
fi
# ############################## end of file ############################# #
-
diff --git a/run_all_automated_tests.cmd b/run_all_automated_tests.cmd
new file mode 100644
index 00000000..b6dd937d
--- /dev/null
+++ b/run_all_automated_tests.cmd
@@ -0,0 +1,106 @@
+@echo off
+
+SETLOCAL
+
+SET SCRIPT_DIRECTORY=%~dp0
+SET SCRIPT_PATH_DOC=%~n0[%~x0]
+
+SET UnitOnly=0
+SET ComponentOnly=0
+
+FOR %%a IN (%*) DO (
+
+ IF /I {--help}=={%%a} (
+
+ IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" (
+
+ type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt"
+ )
+ ECHO ^
+
+Runs all ^(matching^) automated test programs ^(unit and component^) ^
+
+^
+
+%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^
+
+^
+
+Flags/options: ^
+
+ behaviour: ^
+
+^
+
+ --unit-only ^
+
+ runs only unit-test programs ^
+
+^
+
+ --component-only ^
+
+ runs only component-test programs ^
+
+^
+
+ standard flags: ^
+
+^
+
+ --help ^
+
+ displays this help and terminates ^
+
+
+ EXIT /B 0
+ ) ELSE IF /I {--unit-only}=={%%a} (
+
+ SET UnitOnly=1
+ ) ELSE IF /I {--component-only}=={%%a} (
+
+ SET ComponentOnly=1
+ ) ELSE (
+
+ ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2
+
+ EXIT /B 1
+ )
+)
+
+IF %UnitOnly% NEQ 0 IF %ComponentOnly% NEQ 0 (
+
+ ECHO %SCRIPT_PATH_DOC%: --unit-only and --component-only are mutually exclusive 1>&2
+
+ EXIT /B 1
+)
+
+IF %UnitOnly% NEQ 0 (
+ CALL "%SCRIPT_DIRECTORY%run_all_unit_tests.cmd"
+ EXIT /B %ERRORLEVEL%
+)
+
+IF %ComponentOnly% NEQ 0 (
+ CALL "%SCRIPT_DIRECTORY%run_all_component_tests.cmd"
+ EXIT /B %ERRORLEVEL%
+)
+
+SET "ProjectName="
+FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p"
+
+IF NOT DEFINED ProjectName (
+
+ ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2
+
+ EXIT /B 1
+)
+
+ECHO Running all %ProjectName% automated test programs ^(unit and component^)
+
+CALL "%SCRIPT_DIRECTORY%run_all_unit_tests.cmd"
+IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
+
+CALL "%SCRIPT_DIRECTORY%run_all_component_tests.cmd"
+EXIT /B %ERRORLEVEL%
+
+ENDLOCAL
diff --git a/run_all_automated_tests.sh b/run_all_automated_tests.sh
new file mode 100755
index 00000000..2c13b16e
--- /dev/null
+++ b/run_all_automated_tests.sh
@@ -0,0 +1,180 @@
+#! /bin/bash
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
+CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
+ProjectNameFile="$Dir/.sis/project_name.txt"
+ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
+ComponentOnly=0
+ForwardedArgs=()
+SisUseColours=0
+UnitOnly=0
+
+
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# command-line handling
+
+while [[ $# -gt 0 ]]; do
+
+ case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - handled above; forward so category runners see it
+ ForwardedArgs+=("$1")
+ ;;
+ --unit-only)
+
+ UnitOnly=1
+ ;;
+ --component-only)
+
+ ComponentOnly=1
+ ;;
+ --help)
+
+ [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
+ cat << EOF
+Runs all (matching) automated test programs (unit and component)
+
+${ScriptPath} [ ... flags/options ... ]
+
+Flags/options:
+
+ behaviour:
+
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
+ --component-only
+ runs only component-test programs
+
+ --unit-only
+ runs only unit-test programs
+
+ (all other flags are forwarded to the category runner script)
+
+
+ standard flags:
+
+ --help
+ displays this help and terminates
+
+EOF
+
+ exit 0
+ ;;
+ *)
+
+ ForwardedArgs+=("$1")
+ ;;
+ esac
+
+ shift
+done
+
+if [ $UnitOnly -ne 0 ] && [ $ComponentOnly -ne 0 ]; then
+
+ >&2 echo "${ScriptPathClr}: ${SisClr_Red}${SisClr_Bold}--unit-only${SisClr_None} and ${SisClr_Red}${SisClr_Bold}--component-only${SisClr_None} are mutually exclusive"
+
+ exit 1
+fi
+
+
+# ##########################################################
+# main()
+
+status=0
+
+if [ $UnitOnly -ne 0 ]; then
+
+ "$Dir/run_all_unit_tests.sh" "${ForwardedArgs[@]}"
+ exit $?
+fi
+
+if [ $ComponentOnly -ne 0 ]; then
+
+ "$Dir/run_all_component_tests.sh" "${ForwardedArgs[@]}"
+ exit $?
+fi
+
+echo
+echo "Running all ${ProjectNameClr} automated test programs (unit and component)"
+
+"$Dir/run_all_unit_tests.sh" "${ForwardedArgs[@]}"
+status=$?
+
+if [ $status -eq 0 ]; then
+
+ "$Dir/run_all_component_tests.sh" "${ForwardedArgs[@]}"
+ status=$?
+fi
+
+exit $status
+
+
+# ############################## end of file ############################# #
diff --git a/run_all_component_tests.cmd b/run_all_component_tests.cmd
new file mode 100644
index 00000000..970c9bab
--- /dev/null
+++ b/run_all_component_tests.cmd
@@ -0,0 +1,94 @@
+@echo off
+
+SETLOCAL
+
+SET SCRIPT_DIRECTORY=%~dp0
+SET SCRIPT_PATH_DOC=%~n0[%~x0]
+
+IF DEFINED SIS_CMAKE_BUILD_DIR (
+
+ SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR%
+) ELSE (
+
+ SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build
+)
+
+FOR %%a IN (%*) DO (
+
+ IF /I {--help}=={%%a} (
+
+ IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" (
+
+ type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt"
+ )
+ ECHO ^
+
+Runs all ^(matching^) component-test programs ^
+
+^
+
+%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^
+
+^
+
+Flags/options: ^
+
+ behaviour: ^
+
+^
+
+ --component-only ^
+
+ accepted for compatibility; this script always runs component tests only ^
+
+^
+
+ standard flags: ^
+
+^
+
+ --help ^
+
+ displays this help and terminates ^
+
+
+ EXIT /B 0
+ ) ELSE IF /I {--component-only}=={%%a} (
+
+ REM Benign: this script is already component-only
+ ) ELSE (
+
+ ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2
+
+ EXIT /B 1
+ )
+)
+
+if NOT EXIST "%CMAKE_DIR%" (
+
+ ECHO "CMake build directory '%CMAKE_DIR%' does not exist"
+
+ EXIT /B 1
+)
+
+SET "ProjectName="
+FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p"
+
+IF NOT DEFINED ProjectName (
+
+ ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2
+
+ EXIT /B 1
+)
+
+ECHO Running all %ProjectName% component-test programs
+
+FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*component.*\.exe$`) DO (
+
+ ECHO .
+ ECHO executing %%f
+ %%f
+ IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
+)
+
+ENDLOCAL
diff --git a/run_all_component_tests.sh b/run_all_component_tests.sh
new file mode 100755
index 00000000..7473fc54
--- /dev/null
+++ b/run_all_component_tests.sh
@@ -0,0 +1,303 @@
+#! /bin/bash
+
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
+CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
+ProjectNameFile="$Dir/.sis/project_name.txt"
+ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
+ListOnly=0
+RunMake=1
+SisUseColours=0
+Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}}
+
+
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
+
+
+# ##########################################################
+# command-line handling
+
+while [[ $# -gt 0 ]]; do
+
+ case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
+ --component-only)
+
+ # Benign: this script is already component-only (aggregate / CI may
+ # pass it)
+ ;;
+ --list-only|-l)
+
+ ListOnly=1
+ ;;
+ --no-make|-M)
+
+ RunMake=0
+ ;;
+ --verbosity)
+
+ shift
+ Verbosity=$1
+ ;;
+ --help)
+
+ [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
+ cat << EOF
+Runs all (matching) component-test programs
+
+${ScriptPath} [ ... flags/options ... ]
+
+Flags/options:
+
+ behaviour:
+
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
+ --component-only
+ accepted for compatibility; this script always runs component tests
+ only
+
+ -l
+ --list-only
+ lists the target programs but does not execute them
+
+ -M
+ --no-make
+ does not execute a build before running programs
+
+ --verbosity
+ specifies an explicit verbosity, forwarded to each program
+
+
+ standard flags:
+
+ --help
+ displays this help and terminates
+
+EOF
+
+ exit 0
+ ;;
+ *)
+
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
+
+ exit 1
+ ;;
+ esac
+
+ shift
+done
+
+
+# ##########################################################
+# main()
+
+status=0
+
+if [ $RunMake -ne 0 ]; then
+
+ if [ $ListOnly -eq 0 ]; then
+
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all component-test programs"
+
+ mkdir -p "$CMakeDir" || exit 1
+
+ if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
+
+ >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first"
+
+ exit 1
+ fi
+
+ sis_cmake_build
+ status=$?
+ fi
+else
+
+ if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
+
+ >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build"
+
+ exit 1
+ fi
+fi
+
+if [ $status -eq 0 ]; then
+
+ if [ $ListOnly -ne 0 ]; then
+
+ echo
+ echo "Listing all ${ProjectNameClr} component-test programs"
+ else
+
+ echo
+ echo "Running all ${ProjectNameClr} component-test programs"
+ fi
+
+ NumPrograms=0
+
+ while IFS= read -r -d '' f; do
+
+ case "$f" in
+ *.pdb|*.ilk|*.log|*.obj|*.o)
+ continue
+ ;;
+ esac
+
+ NumPrograms=$((NumPrograms + 1))
+
+ fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}"
+
+ if [ $ListOnly -ne 0 ]; then
+
+ echo "would execute ${fClr}:"
+
+ continue
+ fi
+
+ if [ $Verbosity -ge 3 ]; then
+
+ echo
+ fi
+ if [ $Verbosity -ge 2 ]; then
+
+ echo "executing ${fClr}:"
+ fi
+
+ if "$f" --verbosity="$Verbosity"; then
+
+ :
+ else
+
+ status=$?
+
+ break 1
+ fi
+ done < <(find "$CMakeDir" -type f \( -name 'test_component*' -o -name 'test.component.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z)
+
+ if [ $NumPrograms -eq 0 ]; then
+
+ >&2 echo "${ScriptPathClr}: no matching executable component-test programs under '${CMakeDirClr}' (execute bits missing after artifact download?)"
+
+ if [ $ListOnly -eq 0 ]; then
+
+ exit 1
+ fi
+ fi
+fi
+
+exit $status
+
+
+# ############################## end of file ############################# #
diff --git a/run_all_examples.sh b/run_all_examples.sh
index 09362e68..8744843c 100755
--- a/run_all_examples.sh
+++ b/run_all_examples.sh
@@ -1,39 +1,120 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
ListOnly=0
RunMake=1
+SisUseColours=0
SkipInteractive=0
# ##########################################################
# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
-if command -v tput > /dev/null; then
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
- SisClr_Red=${FG_BLUE:-$(tput setaf 1)}
SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
SisClr_None=${FD_NONE:-$(tput sgr0)}
-else
-
- SisClr_Blue=
- SisClr_Red=
- SisClr_Bold=
- SisClr_None=
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
fi
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
# ##########################################################
-# helpers
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
example_stem()
{
@@ -58,6 +139,8 @@ names_match()
[ "$a" = "$b" ]
}
+# Examples listed in .github/ci_skip_interactive_examples.txt require an
+# interactive desktop (GUI dialogs, etc.) and would hang a headless CI run.
is_skipped_interactive_example()
{
local name f_norm line line_stem skip_file="$Dir/.github/ci_skip_interactive_examples.txt"
@@ -66,7 +149,7 @@ is_skipped_interactive_example()
if [ ! -f "$skip_file" ]; then
- >&2 echo "$ScriptPath: --skip-interactive: skip list not found at '$skip_file'"
+ >&2 echo "${ScriptPathClr}: --skip-interactive: skip list not found at '$skip_file'"
return 1
fi
@@ -100,6 +183,10 @@ is_skipped_interactive_example()
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--list-only|-l)
ListOnly=1
@@ -118,19 +205,24 @@ while [[ $# -gt 0 ]]; do
cat << EOF
Runs all (matching) example programs
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
-l
--list-only
lists the target programs but does not execute them
-M
--no-make
- does not execute CMake and make before running examples
+ does not execute a build before running programs
--skip-interactive
skips examples listed in .github/ci_skip_interactive_examples.txt
@@ -149,7 +241,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -168,22 +260,26 @@ if [ $RunMake -ne 0 ]; then
if [ $ListOnly -eq 0 ]; then
- echo "Executing build (via command \`$MakeCmd\`) and then running all ${ProjectName} example programs"
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all example programs"
- mkdir -p $CMakeDir || exit 1
+ mkdir -p "$CMakeDir" || exit 1
- cd $CMakeDir
+ if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- $MakeCmd
- status=$?
+ >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first"
- cd ->/dev/null
+ exit 1
+ fi
+
+ sis_cmake_build
+ status=$?
fi
else
- if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then
+ if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step"
+ >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build"
exit 1
fi
@@ -191,19 +287,25 @@ fi
if [ $status -eq 0 ]; then
- if [ ! -d "$CMakeDir/examples" ]; then
+ if [ $ListOnly -ne 0 ]; then
- >&2 echo "$ScriptPath: examples build tree not found at '$CMakeDir/examples'"
+ echo
+ echo "Listing all ${ProjectNameClr} example programs"
+ else
- exit 1
+ echo
+ echo "Running all ${ProjectNameClr} example programs"
fi
- if [ $ListOnly -ne 0 ]; then
+ # Examples that require human input may honour SIS_EXAMPLE_SMOKE for a
+ # no-arg built-in tmpfile demo (see example.c.cstring_vector).
+ export SIS_EXAMPLE_SMOKE=1
- echo "Listing all ${ProjectName} example programs"
- else
+ if [ ! -d "$CMakeDir/examples" ]; then
+
+ >&2 echo "${ScriptPathClr}: examples build tree not found at '${CMakeDirClr}/examples'"
- echo "Running all ${ProjectName} example programs"
+ exit 1
fi
# Exclude CMake / build artefacts that can become +x after artifact restore.
@@ -227,7 +329,7 @@ if [ $status -eq 0 ]; then
if [ ${#ExamplePrograms[@]} -eq 0 ]; then
- >&2 echo "$ScriptPath: no matching executable example programs under '$CMakeDir/examples' (execute bits missing after artifact download?)"
+ >&2 echo "${ScriptPathClr}: no matching executable example programs under '${CMakeDirClr}/examples' (execute bits missing after artifact download?)"
if [ $ListOnly -eq 0 ]; then
@@ -238,16 +340,17 @@ if [ $status -eq 0 ]; then
for f in "${ExamplePrograms[@]}"
do
+ fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}"
+
if is_skipped_interactive_example "$f"; then
if [ $ListOnly -ne 0 ]; then
- echo "would skip $SisClr_Blue$SisClr_Bold$f$SisClr_None (interactive; --skip-interactive)"
-
+ echo "would skip ${fClr} (interactive; --skip-interactive)"
else
echo
- echo "skipping $SisClr_Blue$SisClr_Bold$f$SisClr_None (interactive; --skip-interactive)"
+ echo "skipping ${fClr} (interactive; --skip-interactive)"
fi
continue
@@ -255,15 +358,15 @@ if [ $status -eq 0 ]; then
if [ $ListOnly -ne 0 ]; then
- echo "would execute $SisClr_Blue$SisClr_Bold$f$SisClr_None:"
+ echo "would execute ${fClr}:"
continue
fi
echo
- echo "executing $SisClr_Blue$SisClr_Bold$f$SisClr_None:"
+ echo "executing ${fClr}:"
- if $f; then
+ if "$f"; then
:
else
diff --git a/run_all_performance_tests.cmd b/run_all_performance_tests.cmd
new file mode 100644
index 00000000..adb8746f
--- /dev/null
+++ b/run_all_performance_tests.cmd
@@ -0,0 +1,96 @@
+@echo off
+
+SETLOCAL
+
+SET SCRIPT_DIRECTORY=%~dp0
+SET SCRIPT_PATH_DOC=%~n0[%~x0]
+
+IF DEFINED SIS_CMAKE_BUILD_DIR (
+
+ SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR%
+) ELSE (
+
+ SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build
+)
+
+FOR %%a IN (%*) DO (
+
+ IF /I {--help}=={%%a} (
+
+ IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" (
+
+ type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt"
+ )
+ ECHO ^
+
+Runs all ^(matching^) performance-test programs ^
+
+^
+
+%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^
+
+^
+
+Flags/options: ^
+
+ behaviour: ^
+
+^
+
+ --gap-groups ^
+
+ sets SIS_PERFTESTS_GROUPGAPS=1 so performance programs emit a ^
+
+ blank line between scenario groups ^
+
+^
+
+ standard flags: ^
+
+^
+
+ --help ^
+
+ displays this help and terminates ^
+
+
+ EXIT /B 0
+ ) ELSE IF /I {--gap-groups}=={%%a} (
+
+ SET SIS_PERFTESTS_GROUPGAPS=1
+ ) ELSE (
+
+ ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2
+
+ EXIT /B 1
+ )
+)
+
+if NOT EXIST "%CMAKE_DIR%" (
+
+ ECHO "CMake build directory '%CMAKE_DIR%' does not exist"
+
+ EXIT /B 1
+)
+
+SET "ProjectName="
+FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p"
+
+IF NOT DEFINED ProjectName (
+
+ ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2
+
+ EXIT /B 1
+)
+
+ECHO Running all %ProjectName% performance-test programs
+
+FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*performance.*\.exe$`) DO (
+
+ ECHO .
+ ECHO executing %%f
+ %%f
+ IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
+)
+
+ENDLOCAL
diff --git a/run_all_performance_tests.sh b/run_all_performance_tests.sh
new file mode 100755
index 00000000..6f3c3739
--- /dev/null
+++ b/run_all_performance_tests.sh
@@ -0,0 +1,290 @@
+#! /bin/bash
+
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
+CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
+ProjectNameFile="$Dir/.sis/project_name.txt"
+ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
+GapGroups=0
+ListOnly=0
+RunMake=1
+SisUseColours=0
+
+
+# ##########################################################
+# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
+
+ SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
+ SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
+ SisClr_None=${FD_NONE:-$(tput sgr0)}
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
+fi
+
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
+
+
+# ##########################################################
+# command-line handling
+
+while [[ $# -gt 0 ]]; do
+
+ case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
+ --gap-groups)
+
+ GapGroups=1
+ ;;
+ --list-only|-l)
+
+ ListOnly=1
+ ;;
+ --no-make|-M)
+
+ RunMake=0
+ ;;
+ --help)
+
+ [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
+ cat << EOF
+Runs all (matching) performance-test programs
+
+${ScriptPath} [ ... flags/options ... ]
+
+Flags/options:
+
+ behaviour:
+
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
+ --gap-groups
+ sets SIS_PERFTESTS_GROUPGAPS=1 so performance programs emit a blank
+ line between scenario groups
+
+ -l
+ --list-only
+ lists the target programs but does not execute them
+
+ -M
+ --no-make
+ does not execute a build before running programs
+
+
+ standard flags:
+
+ --help
+ displays this help and terminates
+
+EOF
+
+ exit 0
+ ;;
+ *)
+
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
+
+ exit 1
+ ;;
+ esac
+
+ shift
+done
+
+
+# ##########################################################
+# main()
+
+if [ $GapGroups -ne 0 ]; then
+
+ export SIS_PERFTESTS_GROUPGAPS=1
+fi
+
+status=0
+
+if [ $RunMake -ne 0 ]; then
+
+ if [ $ListOnly -eq 0 ]; then
+
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all performance-test programs"
+
+ mkdir -p "$CMakeDir" || exit 1
+
+ if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
+
+ >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first"
+
+ exit 1
+ fi
+
+ sis_cmake_build
+ status=$?
+ fi
+else
+
+ if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
+
+ >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build"
+
+ exit 1
+ fi
+fi
+
+if [ $status -eq 0 ]; then
+
+ if [ $ListOnly -ne 0 ]; then
+
+ echo
+ echo "Listing all ${ProjectNameClr} performance-test programs"
+ else
+
+ echo
+ echo "Running all ${ProjectNameClr} performance-test programs"
+ fi
+
+ NumPrograms=0
+
+ while IFS= read -r -d '' f; do
+
+ case "$f" in
+ *.pdb|*.ilk|*.log|*.obj|*.o)
+ continue
+ ;;
+ esac
+
+ NumPrograms=$((NumPrograms + 1))
+
+ fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}"
+
+ if [ $ListOnly -ne 0 ]; then
+
+ echo "would execute ${fClr}:"
+
+ continue
+ fi
+
+ echo
+ echo "executing ${fClr}:"
+
+ if "$f"; then
+
+ :
+ else
+
+ status=$?
+
+ break 1
+ fi
+ done < <(find "$CMakeDir" -type f \( -name 'test_performance*' -o -name 'test.performance.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z)
+
+ if [ $NumPrograms -eq 0 ]; then
+
+ echo "${ScriptPathClr}: found no performance-test programs under '${CMakeDirClr}' (none found)"
+
+ exit 0
+ fi
+fi
+
+exit $status
+
+
+# ############################## end of file ############################# #
diff --git a/run_all_scratch_tests.cmd b/run_all_scratch_tests.cmd
index 4b18544b..9b05623f 100644
--- a/run_all_scratch_tests.cmd
+++ b/run_all_scratch_tests.cmd
@@ -1,173 +1,85 @@
-@ECHO OFF
+@echo off
-SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
+SETLOCAL
-SET ScriptPath=%~n0[%~x0]
-SET ScriptDir=%~dp0
+SET SCRIPT_DIRECTORY=%~dp0
+SET SCRIPT_PATH_DOC=%~n0[%~x0]
IF DEFINED SIS_CMAKE_BUILD_DIR (
- SET CMakeDir=%SIS_CMAKE_BUILD_DIR%
+ SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR%
) ELSE (
- SET CMakeDir=%ScriptDir%_build
+ SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build
)
-SET ListOnly=0
-SET RunMake=1
-SET TestVerbosityDefault=3
-SET TestVerbosity=%TEST_VERBOSITY%
+FOR %%a IN (%*) DO (
-IF NOT DEFINED TestVerbosity SET TestVerbosity=%TestVerbosityDefault%
-IF DEFINED XTESTS_VERBOSITY SET TestVerbosity=%XTESTS_VERBOSITY%
+ IF /I {--help}=={%%a} (
-SET BuildConfig=Release
-IF DEFINED CMAKE_BUILD_TYPE SET BuildConfig=%CMAKE_BUILD_TYPE%
+ IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" (
+ type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt"
+ )
+ ECHO ^
-:: #########################################################
-:: command-line handling
+Runs all ^(matching^) scratch-test programs ^
-:parse_args
-IF "%~1"=="" GOTO args_done
+^
-IF /I "%~1"=="--help" GOTO show_help
-IF /I "%~1"=="-l" (
- SET ListOnly=1
- GOTO next_arg
-)
-IF /I "%~1"=="--list-only" (
- SET ListOnly=1
- GOTO next_arg
-)
-IF /I "%~1"=="-M" (
- SET RunMake=0
- GOTO next_arg
-)
-IF /I "%~1"=="--no-make" (
- SET RunMake=0
- GOTO next_arg
-)
-IF /I "%~1"=="--verbosity" (
- SHIFT
- IF "%~1"=="" (
- ECHO %ScriptPath%: --verbosity requires an argument 1>&2
- EXIT /B 1
- )
- SET TestVerbosity=%~1
- GOTO next_arg
-)
+%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^
-ECHO %ScriptPath%: unrecognised argument '%~1'; use --help for usage 1>&2
-EXIT /B 1
+^
-:next_arg
-SHIFT
-GOTO parse_args
+Flags/options: ^
-:args_done
+ behaviour: ^
+^
-:: #########################################################
-:: main()
+ standard flags: ^
-SET status=0
+^
-IF %RunMake% NEQ 0 (
- IF %ListOnly% EQU 0 (
- ECHO Executing build and then running all scratch test programs
- CALL :do_build
- IF ERRORLEVEL 1 SET status=1
- )
-) ELSE (
- IF NOT EXIST "%CMakeDir%\CMakeCache.txt" (
- ECHO %ScriptPath%: cannot run in '--no-make' mode without a previous successful build step 1>&2
- EXIT /B 1
- )
-)
+ --help ^
+
+ displays this help and terminates ^
+
+
+ EXIT /B 0
+ ) ELSE (
-IF %status% EQU 0 (
- IF %ListOnly% NEQ 0 (
- ECHO Listing all scratch test programs
- ) ELSE (
- ECHO Running all scratch test programs
- )
-
- FOR /F "usebackq delims=" %%f IN (`DIR /A:-D /B /S "%CMakeDir%" 2^>NUL ^| FINDSTR /I /R "test[._]scratch[._].*\.exe$"`) DO (
- IF !ListOnly! NEQ 0 (
- ECHO would execute %%f:
- ) ELSE (
- IF !TestVerbosity! GEQ 3 ECHO.
- IF !TestVerbosity! GEQ 2 ECHO executing %%f:
- CALL "%%f"
- IF ERRORLEVEL 1 SET status=1
- )
- )
+ ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2
+
+ EXIT /B 1
+ )
)
-IF !status! NEQ 0 (
- ENDLOCAL
+if NOT EXIST "%CMAKE_DIR%" (
+
+ ECHO "CMake build directory '%CMAKE_DIR%' does not exist"
+
EXIT /B 1
)
-ENDLOCAL
-EXIT /B 0
+SET "ProjectName="
+FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p"
-:: #########################################################
-:: helpers
+IF NOT DEFINED ProjectName (
-:do_build
-IF DEFINED SIS_CMAKE_MAKE_COMMAND (
- PUSHd "%CMakeDir%"
- CALL %SIS_CMAKE_MAKE_COMMAND%
- SET build_status=!ERRORLEVEL!
- POPD
- EXIT /B !build_status!
-)
-IF EXIST "%CMakeDir%\Makefile" (
- PUSHd "%CMakeDir%"
- IF DEFINED MSYSTEM (
- mingw32-make.exe
- ) ELSE (
- make
- )
- SET build_status=!ERRORLEVEL!
- POPD
- EXIT /B !build_status!
+ ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2
+
+ EXIT /B 1
)
-cmake --build "%CMakeDir%" --config %BuildConfig%
-EXIT /B %ERRORLEVEL%
+ECHO Running all %ProjectName% scratch-test programs
+
+FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*scratch.*\.exe$`) DO (
-:show_help
-IF EXIST "%ScriptDir%.sis\script_info_lines.txt" (
- type "%ScriptDir%.sis\script_info_lines.txt"
+ ECHO .
+ ECHO executing %%f
+ %%f
+ IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
)
-ECHO.
-ECHO Runs all (matching) scratch-test programs
-ECHO.
-ECHO %ScriptPath% [ ... flags/options ... ]
-ECHO.
-ECHO Flags/options:
-ECHO.
-ECHO behaviour:
-ECHO.
-ECHO -l
-ECHO --list-only
-ECHO lists the target programs but does not execute them
-ECHO.
-ECHO -M
-ECHO --no-make
-ECHO does not execute CMake and make before running tests
-ECHO.
-ECHO --verbosity ^
-ECHO reserved for output control; scratch programs are invoked without xTests verbosity flags
-ECHO.
-ECHO standard flags:
-ECHO.
-ECHO --help
-ECHO displays this help and terminates
-EXIT /B 0
-
-
-:: ############################# end of file ############################ ::
+
+ENDLOCAL
diff --git a/run_all_scratch_tests.sh b/run_all_scratch_tests.sh
index 14626b2e..5e6659a8 100755
--- a/run_all_scratch_tests.sh
+++ b/run_all_scratch_tests.sh
@@ -1,36 +1,120 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
ListOnly=0
RunMake=1
-Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}}
+SisUseColours=0
# ##########################################################
# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
-if command -v tput > /dev/null; then
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
+
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
- SisClr_Red=${FG_BLUE:-$(tput setaf 1)}
SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
SisClr_None=${FD_NONE:-$(tput sgr0)}
-else
-
- SisClr_Blue=
- SisClr_Red=
- SisClr_Bold=
- SisClr_None=
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
fi
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
+
# ##########################################################
# command-line handling
@@ -38,6 +122,10 @@ fi
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--list-only|-l)
ListOnly=1
@@ -46,34 +134,30 @@ while [[ $# -gt 0 ]]; do
RunMake=0
;;
- --verbosity)
-
- shift
- Verbosity=$1
- ;;
--help)
[ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
cat << EOF
Runs all (matching) scratch-test programs
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
-l
--list-only
lists the target programs but does not execute them
-M
--no-make
- does not execute CMake and make before running tests
-
- --verbosity
- specifies an explicit verbosity (reserved; scratch programs are
- invoked without xTests verbosity flags)
+ does not execute a build before running programs
standard flags:
@@ -87,7 +171,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -105,22 +189,27 @@ status=0
if [ $RunMake -ne 0 ]; then
if [ $ListOnly -eq 0 ]; then
- echo "Executing build (via command \`$MakeCmd\`) and then running all ${ProjectName} scratch test programs"
- mkdir -p $CMakeDir || exit 1
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all scratch-test programs"
- cd $CMakeDir
+ mkdir -p "$CMakeDir" || exit 1
- $MakeCmd
- status=$?
+ if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
+
+ >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first"
- cd ->/dev/null
+ exit 1
+ fi
+
+ sis_cmake_build
+ status=$?
fi
else
- if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then
+ if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step"
+ >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build"
exit 1
fi
@@ -130,43 +219,58 @@ if [ $status -eq 0 ]; then
if [ $ListOnly -ne 0 ]; then
- echo "Listing all ${ProjectName} scratch test programs"
+ echo
+ echo "Listing all ${ProjectNameClr} scratch-test programs"
else
- echo "Running all ${ProjectName} scratch test programs"
+ echo
+ echo "Running all ${ProjectNameClr} scratch-test programs"
fi
- for f in $(find "$CMakeDir" -type f '(' -name 'test_scratch*' -o -name 'test.scratch.*' ')' -exec test -x {} \; -print | sort)
- do
+ NumPrograms=0
- if [ $ListOnly -ne 0 ]; then
+ while IFS= read -r -d '' f; do
- echo "would execute $SisClr_Blue$SisClr_Bold$f$SisClr_None:"
+ case "$f" in
+ *.pdb|*.ilk|*.log|*.obj|*.o)
+ continue
+ ;;
+ esac
- continue
- fi
+ NumPrograms=$((NumPrograms + 1))
- if [ $Verbosity -ge 3 ]; then
+ fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}"
- echo
- fi
- if [ $Verbosity -ge 2 ]; then
+ if [ $ListOnly -ne 0 ]; then
+
+ echo "would execute ${fClr}:"
- echo "executing $SisClr_Blue$SisClr_Bold$f$SisClr_None:"
+ continue
fi
- if $f; then
+ echo
+ echo "executing ${fClr}:"
+
+ if "$f"; then
:
else
status=$?
+
+ break 1
fi
- done
+ done < <(find "$CMakeDir" -type f \( -name 'test_scratch*' -o -name 'test.scratch.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z)
+
+ if [ $NumPrograms -eq 0 ]; then
+
+ echo "${ScriptPathClr}: found no scratch-test programs under '${CMakeDirClr}' (none found)"
+
+ exit 0
+ fi
fi
exit $status
# ############################## end of file ############################# #
-
diff --git a/run_all_unit_tests.cmd b/run_all_unit_tests.cmd
index de418c98..18b040a8 100644
--- a/run_all_unit_tests.cmd
+++ b/run_all_unit_tests.cmd
@@ -1,209 +1,94 @@
-@ECHO OFF
+@echo off
-SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
+SETLOCAL
-SET ScriptPath=%~n0[%~x0]
-SET ScriptDir=%~dp0
+SET SCRIPT_DIRECTORY=%~dp0
+SET SCRIPT_PATH_DOC=%~n0[%~x0]
IF DEFINED SIS_CMAKE_BUILD_DIR (
- SET CMakeDir=%SIS_CMAKE_BUILD_DIR%
+ SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR%
) ELSE (
- SET CMakeDir=%ScriptDir%_build
+ SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build
)
-SET ListOnly=0
-SET RunMake=1
-SET UnitOnly=0
-SET ComponentOnly=0
-SET TestVerbosityDefault=3
-SET TestVerbosity=%TEST_VERBOSITY%
+FOR %%a IN (%*) DO (
-IF NOT DEFINED TestVerbosity SET TestVerbosity=%TestVerbosityDefault%
-IF DEFINED XTESTS_VERBOSITY SET TestVerbosity=%XTESTS_VERBOSITY%
+ IF /I {--help}=={%%a} (
-SET BuildConfig=Release
-IF DEFINED CMAKE_BUILD_TYPE SET BuildConfig=%CMAKE_BUILD_TYPE%
+ IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" (
+ type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt"
+ )
+ ECHO ^
-:: #########################################################
-:: command-line handling
+Runs all ^(matching^) unit-test programs ^
-:parse_args
-IF "%~1"=="" GOTO args_done
+^
-IF /I "%~1"=="--help" GOTO show_help
-IF /I "%~1"=="-l" (
- SET ListOnly=1
- GOTO next_arg
-)
-IF /I "%~1"=="--list-only" (
- SET ListOnly=1
- GOTO next_arg
-)
-IF /I "%~1"=="-M" (
- SET RunMake=0
- GOTO next_arg
-)
-IF /I "%~1"=="--no-make" (
- SET RunMake=0
- GOTO next_arg
-)
-IF /I "%~1"=="--unit-only" (
- SET UnitOnly=1
- GOTO next_arg
-)
-IF /I "%~1"=="--component-only" (
- SET ComponentOnly=1
- GOTO next_arg
-)
-IF /I "%~1"=="--verbosity" (
- SHIFT
- IF "%~1"=="" (
- ECHO %ScriptPath%: --verbosity requires an argument 1>&2
- EXIT /B 1
- )
- SET TestVerbosity=%~1
- GOTO next_arg
-)
+%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^
-ECHO %ScriptPath%: unrecognised argument '%~1'; use --help for usage 1>&2
-EXIT /B 1
+^
-:next_arg
-SHIFT
-GOTO parse_args
+Flags/options: ^
-:args_done
+ behaviour: ^
-IF %UnitOnly% NEQ 0 IF %ComponentOnly% NEQ 0 (
- ECHO %ScriptPath%: --unit-only and --component-only are mutually exclusive 1>&2
- EXIT /B 1
-)
+^
-IF %UnitOnly% NEQ 0 (
- SET TestKindDescription=unit test
- SET FindPattern=test[._]unit[._].*\.exe$
-) ELSE IF %ComponentOnly% NEQ 0 (
- SET TestKindDescription=component test
- SET FindPattern=test[._]component[._].*\.exe$
-) ELSE (
- SET TestKindDescription=component and unit test
- SET FindPattern=test[._]unit[._].*\.exe$ test[._]component[._].*\.exe$
-)
+ --unit-only ^
+ accepted for compatibility; this script always runs unit tests only ^
-:: #########################################################
-:: main()
+^
-SET status=0
+ standard flags: ^
-IF %RunMake% NEQ 0 (
- IF %ListOnly% EQU 0 (
- ECHO Executing build and then running all !TestKindDescription! programs
- CALL :do_build
- IF ERRORLEVEL 1 SET status=1
- )
-) ELSE (
- IF NOT EXIST "%CMakeDir%\CMakeCache.txt" (
- ECHO %ScriptPath%: cannot run in '--no-make' mode without a previous successful build step 1>&2
- EXIT /B 1
- )
-)
+^
+
+ --help ^
-IF %status% EQU 0 (
- IF %ListOnly% NEQ 0 (
- ECHO Listing all !TestKindDescription! programs
- ) ELSE (
- ECHO Running all !TestKindDescription! programs
- )
-
- FOR /F "usebackq delims=" %%f IN (`DIR /A:-D /B /S "%CMakeDir%" 2^>NUL ^| FINDSTR /I /R "%FindPattern%"`) DO (
- IF !ListOnly! NEQ 0 (
- ECHO would execute %%f:
- ) ELSE (
- ECHO.
- ECHO executing %%f:
- CALL "%%f" --verbosity=!TestVerbosity!
- IF ERRORLEVEL 1 (
- SET status=1
- GOTO tests_done
- )
- )
- )
+ displays this help and terminates ^
+
+
+ EXIT /B 0
+ ) ELSE IF /I {--unit-only}=={%%a} (
+
+ REM Benign: this script is already unit-only
+ ) ELSE (
+
+ ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2
+
+ EXIT /B 1
+ )
)
-:tests_done
-IF !status! NEQ 0 (
- ENDLOCAL
+if NOT EXIST "%CMAKE_DIR%" (
+
+ ECHO "CMake build directory '%CMAKE_DIR%' does not exist"
+
EXIT /B 1
)
-ENDLOCAL
-EXIT /B 0
+SET "ProjectName="
+FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p"
-:: #########################################################
-:: helpers
+IF NOT DEFINED ProjectName (
-:do_build
-IF DEFINED SIS_CMAKE_MAKE_COMMAND (
- PUSHd "%CMakeDir%"
- CALL %SIS_CMAKE_MAKE_COMMAND%
- SET build_status=!ERRORLEVEL!
- POPD
- EXIT /B !build_status!
-)
-IF EXIST "%CMakeDir%\Makefile" (
- PUSHd "%CMakeDir%"
- IF DEFINED MSYSTEM (
- mingw32-make.exe
- ) ELSE (
- make
- )
- SET build_status=!ERRORLEVEL!
- POPD
- EXIT /B !build_status!
+ ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2
+
+ EXIT /B 1
)
-cmake --build "%CMakeDir%" --config %BuildConfig%
-EXIT /B %ERRORLEVEL%
+ECHO Running all %ProjectName% unit-test programs
-:show_help
-IF EXIST "%ScriptDir%.sis\script_info_lines.txt" (
- type "%ScriptDir%.sis\script_info_lines.txt"
+FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*unit.*\.exe$`) DO (
+
+ ECHO .
+ ECHO executing %%f
+ %%f
+ IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
)
-ECHO.
-ECHO Runs all (matching) unit-test and/or component-test programs
-ECHO.
-ECHO %ScriptPath% [ ... flags/options ... ]
-ECHO.
-ECHO Flags/options:
-ECHO.
-ECHO behaviour:
-ECHO.
-ECHO -l
-ECHO --list-only
-ECHO lists the target programs but does not execute them
-ECHO.
-ECHO -M
-ECHO --no-make
-ECHO does not execute CMake and make before running tests
-ECHO.
-ECHO --unit-only
-ECHO runs only unit-test programs (test.unit.* / test_unit*)
-ECHO.
-ECHO --component-only
-ECHO runs only component-test programs (test.component.* / test_component*)
-ECHO.
-ECHO --verbosity ^
-ECHO specifies an explicit verbosity for the unit-test(s)
-ECHO.
-ECHO standard flags:
-ECHO.
-ECHO --help
-ECHO displays this help and terminates
-EXIT /B 0
-
-
-:: ############################# end of file ############################ ::
+
+ENDLOCAL
diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh
index 0d7e147d..56003827 100755
--- a/run_all_unit_tests.sh
+++ b/run_all_unit_tests.sh
@@ -1,38 +1,121 @@
#! /bin/bash
-ScriptPath=$0
-Dir=$(cd $(dirname "$ScriptPath"); pwd)
-Basename=$(basename "$ScriptPath")
+# ##########################################################
+# functions - 1
+
+sis_cmake_is_truey() {
+ case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
+
+ 1|ok|on|true|yes|y)
+
+ return 0
+ ;;
+ *)
+
+ return 1
+ ;;
+ esac
+}
+
+
+# ##########################################################
+# constants and variables
+
+Basename=$(basename "$0")
+Dir=$(cd "$(dirname "$0")" && pwd)
CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build}
-[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make
-MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}}
ProjectNameFile="$Dir/.sis/project_name.txt"
ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile")
+ScriptPath=$0
+AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}}
ListOnly=0
RunMake=1
-UnitOnly=0
-ComponentOnly=0
+SisUseColours=0
Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}}
# ##########################################################
# colours
+#
+# Enable when tput is available and either:
+# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if
+# empty/dumb), or
+# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of
+# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a
+# TTY is OK).
+
+SisClr_Blue=
+SisClr_Bold=
+SisClr_Green=
+SisClr_None=
+SisClr_Red=
+SisClr_Yellow=
+
+for arg in "$@"; do
+
+ case $arg in
+ --always-use-colors|--always-use-colours|-A)
+
+ AlwaysUseColours=1
+ ;;
+ esac
+done
+
+if command -v tput >/dev/null 2>&1; then
+
+ if [ $AlwaysUseColours -ne 0 ]; then
-if command -v tput > /dev/null; then
+ if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then
+
+ TERM=xterm-256color
+ fi
+
+ SisUseColours=1
+ elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
+
+ SisUseColours=1
+ fi
+fi
+
+if [ $SisUseColours -ne 0 ]; then
SisClr_Blue=${FG_BLUE:-$(tput setaf 4)}
- SisClr_Red=${FG_BLUE:-$(tput setaf 1)}
SisClr_Bold=${FD_BOLD:-$(tput bold)}
+ SisClr_Green=${FG_GREEN:-$(tput setaf 2)}
SisClr_None=${FD_NONE:-$(tput sgr0)}
-else
-
- SisClr_Blue=
- SisClr_Red=
- SisClr_Bold=
- SisClr_None=
+ SisClr_Red=${FG_RED:-$(tput setaf 1)}
+ SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)}
fi
+CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}"
+ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}"
+ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}"
+
+
+# ##########################################################
+# functions - 2
+
+sis_cmake_build() {
+
+ local config="${SIS_CMAKE_CONFIG:-Release}"
+ local args=(--build "$CMakeDir")
+ if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then
+
+ args+=(--config "$config")
+ fi
+ if [ "$#" -gt 0 ]; then
+
+ local t
+ for t in "$@"; do
+
+ args+=(--target "$t")
+ done
+ fi
+
+ cmake "${args[@]}"
+}
+
# ##########################################################
# command-line handling
@@ -40,6 +123,10 @@ fi
while [[ $# -gt 0 ]]; do
case $1 in
+ --always-use-colors|--always-use-colours|-A)
+
+ # AlwaysUseColours=1 - this is handled by the for loop above
+ ;;
--list-only|-l)
ListOnly=1
@@ -50,11 +137,7 @@ while [[ $# -gt 0 ]]; do
;;
--unit-only)
- UnitOnly=1
- ;;
- --component-only)
-
- ComponentOnly=1
+ # Benign: this script is already unit-only (aggregate / CI may pass it)
;;
--verbosity)
@@ -65,30 +148,32 @@ while [[ $# -gt 0 ]]; do
[ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt"
cat << EOF
-Runs all (matching) unit-test and/or component-test programs
+Runs all (matching) unit-test programs
-$ScriptPath [ ... flags/options ... ]
+${ScriptPath} [ ... flags/options ... ]
Flags/options:
behaviour:
+ -A
+ --always-use-colors
+ --always-use-colours
+ forces use of colours even when stdout is not a TTY
+
-l
--list-only
lists the target programs but does not execute them
-M
--no-make
- does not execute CMake and make before running tests
+ does not execute a build before running programs
--unit-only
- runs only unit-test programs (test.unit.* / test_unit*)
-
- --component-only
- runs only component-test programs (test.component.* / test_component*)
+ accepted for compatibility; this script always runs unit tests only
--verbosity
- specifies an explicit verbosity for the unit-test(s)
+ specifies an explicit verbosity, forwarded to each program
standard flags:
@@ -102,7 +187,7 @@ EOF
;;
*)
- >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage"
+ >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage"
exit 1
;;
@@ -111,50 +196,36 @@ EOF
shift
done
-if [ $UnitOnly -ne 0 ] && [ $ComponentOnly -ne 0 ]; then
-
- >&2 echo "$ScriptPath: --unit-only and --component-only are mutually exclusive"
-
- exit 1
-fi
-
# ##########################################################
# main()
status=0
-if [ $UnitOnly -ne 0 ]; then
-
- TestKindDescription='unit test'
-elif [ $ComponentOnly -ne 0 ]; then
-
- TestKindDescription='component test'
-else
-
- TestKindDescription='component and unit test'
-fi
-
if [ $RunMake -ne 0 ]; then
if [ $ListOnly -eq 0 ]; then
- echo "Executing build (via command \`$MakeCmd\`) and then running all ${ProjectName} ${TestKindDescription} programs"
+ echo
+ echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all unit-test programs"
- mkdir -p $CMakeDir || exit 1
+ mkdir -p "$CMakeDir" || exit 1
- cd $CMakeDir
+ if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- $MakeCmd
- status=$?
+ >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first"
- cd ->/dev/null
+ exit 1
+ fi
+
+ sis_cmake_build
+ status=$?
fi
else
- if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then
+ if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then
- >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step"
+ >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build"
exit 1
fi
@@ -164,54 +235,31 @@ if [ $status -eq 0 ]; then
if [ $ListOnly -ne 0 ]; then
- echo "Listing all ${ProjectName} ${TestKindDescription} programs"
+ echo
+ echo "Listing all ${ProjectNameClr} unit-test programs"
else
- echo "Running all ${ProjectName} ${TestKindDescription} programs"
+ echo
+ echo "Running all ${ProjectNameClr} unit-test programs"
fi
- if [ $UnitOnly -ne 0 ]; then
+ NumPrograms=0
- find_name_expr=( \( -name 'test_unit*' -o -name 'test.unit.*' \) )
- elif [ $ComponentOnly -ne 0 ]; then
+ while IFS= read -r -d '' f; do
- find_name_expr=( \( -name 'test_component*' -o -name 'test.component.*' \) )
- else
-
- find_name_expr=( \( -name 'test_unit*' -o -name 'test.unit.*' -o -name 'test_component*' -o -name 'test.component.*' \) )
- fi
-
- # Exclude build artefacts that can match suite name globs (CMake object
- # files under CMakeFiles/, etc.).
- TestPrograms=( $(find "$CMakeDir" -type f "${find_name_expr[@]}" \
- ! -path '*/CMakeFiles/*' \
- ! -name '*.a' \
- ! -name '*.d' \
- ! -name '*.lib' \
- ! -name '*.log' \
- ! -name '*.o' \
- ! -name '*.obj' \
- ! -name '*.pdb' \
- -exec test -x {} \; -print | sort) )
-
- echo "discovered ${#TestPrograms[@]} ${TestKindDescription} program(s)"
+ case "$f" in
+ *.pdb|*.ilk|*.log|*.obj|*.o)
+ continue
+ ;;
+ esac
- if [ ${#TestPrograms[@]} -eq 0 ]; then
-
- >&2 echo "$ScriptPath: no matching executable ${TestKindDescription} programs under '$CMakeDir' (execute bits missing after artifact download?)"
-
- if [ $ListOnly -eq 0 ]; then
-
- status=1
- fi
- fi
+ NumPrograms=$((NumPrograms + 1))
- for f in "${TestPrograms[@]}"
- do
+ fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}"
if [ $ListOnly -ne 0 ]; then
- echo "would execute $SisClr_Blue$SisClr_Bold$f$SisClr_None:"
+ echo "would execute ${fClr}:"
continue
fi
@@ -222,10 +270,10 @@ if [ $status -eq 0 ]; then
fi
if [ $Verbosity -ge 2 ]; then
- echo "executing $SisClr_Blue$SisClr_Bold$f$SisClr_None:"
+ echo "executing ${fClr}:"
fi
- if $f --verbosity=$Verbosity; then
+ if "$f" --verbosity="$Verbosity"; then
:
else
@@ -234,11 +282,20 @@ if [ $status -eq 0 ]; then
break 1
fi
- done
+ done < <(find "$CMakeDir" -type f \( -name 'test_unit*' -o -name 'test.unit.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z)
+
+ if [ $NumPrograms -eq 0 ]; then
+
+ >&2 echo "${ScriptPathClr}: no matching executable unit-test programs under '${CMakeDirClr}' (execute bits missing after artifact download?)"
+
+ if [ $ListOnly -eq 0 ]; then
+
+ exit 1
+ fi
+ fi
fi
exit $status
# ############################## end of file ############################# #
-
diff --git a/test/component/stlsoft/system/CMakeLists.txt b/test/component/stlsoft/system/CMakeLists.txt
index 071c04bc..452572ef 100644
--- a/test/component/stlsoft/system/CMakeLists.txt
+++ b/test/component/stlsoft/system/CMakeLists.txt
@@ -1,3 +1,4 @@
# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
add_subdirectory(environment_block)
add_subdirectory(environment_variable_exists)
+add_subdirectory(environment_variable_strtoll)
diff --git a/test/component/stlsoft/system/environment_variable_exists/entry.cpp b/test/component/stlsoft/system/environment_variable_exists/entry.cpp
index 93e4506a..6fbb2d09 100644
--- a/test/component/stlsoft/system/environment_variable_exists/entry.cpp
+++ b/test/component/stlsoft/system/environment_variable_exists/entry.cpp
@@ -4,7 +4,7 @@
* Purpose: Component-tests for `stlsoft::environment_variable_exists()`.
*
* Created: 28th September 2026
- * Updated: 30th September 2026
+ * Updated: 8th October 2026
*
* ////////////////////////////////////////////////////////////////////// */
@@ -218,21 +218,49 @@ static void TEST_environment_variable_exists_WITH_simple_string()
}
#ifndef _WIN32
-/* An empty value is a defined variable on UNIX. The Windows CRT treats an
- * empty assignment as erasure, so this case is UNIX-only.
+/* An empty value is a defined variable on UNIX: getenv() returns "".
+ * Existence is true for the C function and the C++ overloads. The Windows
+ * CRT treats an empty assignment as erasure, so this case is UNIX-only.
*/
static void TEST_environment_variable_exists_WHEN_EMPTY()
{
- environment_variable_scope const empty(VAR_NAME, "");
+ char const* const name = VAR_NAME;
- char const* const value = ::getenv(VAR_NAME);
+ {
+ environment_variable_scope const empty(VAR_NAME, "");
- XTESTS_REQUIRE(TEST_PTR_NE(NULL, value));
- TEST_MS_EQ("", value);
+ char const* const value = ::getenv(name);
- char const* const name = VAR_NAME;
+ XTESTS_REQUIRE(TEST_PTR_NE(NULL, value));
+ TEST_MS_EQ("", value);
- TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+ TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name));
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(std::string(name)));
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(stlsoft::simple_string(name)));
+ TEST_BOOLEAN_FALSE(stlsoft::stlsoft_C_environment_variable_exists_m("STLSOFT_CT_ENV_VAR_EXISTS_NO_SUCH"));
+ }
+
+ {
+ environment_variable_scope const present(VAR_NAME, "present");
+
+ TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name));
+
+ environment_variable_scope const emptied(VAR_NAME, "");
+
+ char const* const value = ::getenv(name);
+
+ XTESTS_REQUIRE(TEST_PTR_NE(NULL, value));
+ TEST_MS_EQ("", value);
+
+ TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name));
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+ }
+
+ environment_variable_scope const erased(VAR_NAME, ss_nullptr_k);
+
+ TEST_BOOLEAN_FALSE(stlsoft::stlsoft_C_environment_variable_exists_m(name));
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name));
}
#endif /* !_WIN32 */
diff --git a/test/component/stlsoft/system/environment_variable_strtoll/CMakeLists.txt b/test/component/stlsoft/system/environment_variable_strtoll/CMakeLists.txt
new file mode 100644
index 00000000..ecaa534c
--- /dev/null
+++ b/test/component/stlsoft/system/environment_variable_strtoll/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.stlsoft.system.environment_variable_strtoll entry.cpp)
diff --git a/test/component/stlsoft/system/environment_variable_strtoll/entry.cpp b/test/component/stlsoft/system/environment_variable_strtoll/entry.cpp
new file mode 100644
index 00000000..1cc60278
--- /dev/null
+++ b/test/component/stlsoft/system/environment_variable_strtoll/entry.cpp
@@ -0,0 +1,404 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: test/component/stlsoft/system/environment_variable_strtoll/entry.cpp
+ *
+ * Purpose: Component tests for
+ * `stlsoft_C_environment_variable_strtoll_m()`.
+ *
+ * Created: 28th September 2026
+ * Updated: 8th October 2026
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+/* /////////////////////////////////////
+ * test component header file include(s)
+ */
+
+#include
+
+
+/* /////////////////////////////////////
+ * general includes
+ */
+
+/* xTests header files */
+#include
+#include
+
+/* STLSoft header files */
+#include
+#include
+
+/* Standard C header files */
+#include
+#include
+#include
+#include
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+
+namespace {
+
+ static void TEST_environment_variable_strtoll_WHEN_ABSENT();
+ static void TEST_environment_variable_strtoll_WHEN_DECIMAL();
+ static void TEST_environment_variable_strtoll_WHEN_BASE_PREFIXES();
+ static void TEST_environment_variable_strtoll_WHEN_LEADING_WHITESPACE();
+ static void TEST_environment_variable_strtoll_WHEN_NOT_COMPLETE();
+ static void TEST_environment_variable_strtoll_WHEN_OUT_OF_RANGE();
+ static void TEST_environment_variable_strtoll_WHEN_LONG_VALUE();
+ static void TEST_environment_variable_strtoll_WHEN_ERASED();
+ static void TEST_environment_variable_strtoll_WHEN_VALUE_REPLACED();
+ static void TEST_environment_variable_strtoll_EXACT_NAME();
+ static void TEST_environment_variable_strtoll_CASE_SENSITIVITY();
+ static void TEST_environment_variable_strtoll_CPP_WRAPPER();
+#ifndef _WIN32
+ static void TEST_environment_variable_strtoll_WHEN_EMPTY();
+#endif /* !_WIN32 */
+} // 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.stlsoft.system.environment_variable_strtoll", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_ABSENT);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_DECIMAL);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_BASE_PREFIXES);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_LEADING_WHITESPACE);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_NOT_COMPLETE);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_OUT_OF_RANGE);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_LONG_VALUE);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_ERASED);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_VALUE_REPLACED);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_EXACT_NAME);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_CASE_SENSITIVITY);
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_CPP_WRAPPER);
+#ifndef _WIN32
+ XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_EMPTY);
+#endif /* !_WIN32 */
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * constants
+ */
+
+namespace {
+
+ using platformstl::environment_variable_scope;
+
+ stlsoft::ss_sint64_t const SENTINEL_ = 0x5a5a5a5a;
+
+ char const VAR_NAME[] = "STLSOFT_CT_ENV_VAR_STRTOLL";
+ char const VAR_NAME_EXTRA[] = "STLSOFT_CT_ENV_VAR_STRTOLL_EXTRA";
+ char const VAR_NAME_LOWER[] = "stlsoft_ct_env_var_strtoll_case";
+ char const VAR_NAME_PREFIX[] = "STLSOFT_CT_ENV_VAR_STRTOL";
+ char const VAR_NAME_UPPER[] = "STLSOFT_CT_ENV_VAR_STRTOLL_CASE";
+} // anonymous namespace
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * macros
+ */
+
+#define TEST_READ_PARSED_(name, expected) \
+ \
+ do \
+ { \
+ stlsoft::ss_sint64_t result_ = SENTINEL_; \
+ \
+ errno = EPERM; \
+ \
+ stlsoft::ss_truthy_t const parsed_ = \
+ stlsoft::stlsoft_C_environment_variable_strtoll_m( \
+ (name) \
+ , &result_ \
+ ); \
+ \
+ TEST_BOOLEAN_TRUE(parsed_); \
+ TEST_INT_EQ((expected), result_); \
+ TEST_INT_EQ(0, errno); \
+ } while (0)
+
+#define TEST_READ_REJECTED_(name, expected_errno) \
+ \
+ do \
+ { \
+ stlsoft::ss_sint64_t result_ = SENTINEL_; \
+ \
+ errno = EPERM; \
+ \
+ stlsoft::ss_truthy_t const parsed_ = \
+ stlsoft::stlsoft_C_environment_variable_strtoll_m( \
+ (name) \
+ , &result_ \
+ ); \
+ \
+ TEST_BOOLEAN_FALSE(parsed_); \
+ TEST_INT_EQ(0, result_); \
+ TEST_INT_EQ((expected_errno), errno); \
+ } while (0)
+
+#define TEST_PARSED_(value, expected) \
+ \
+ do \
+ { \
+ environment_variable_scope const scope_(VAR_NAME, (value)); \
+ \
+ TEST_READ_PARSED_(VAR_NAME, (expected)); \
+ } while (0)
+
+#define TEST_REJECTED_(value, expected_errno) \
+ \
+ do \
+ { \
+ environment_variable_scope const scope_(VAR_NAME, (value)); \
+ \
+ TEST_READ_REJECTED_(VAR_NAME, (expected_errno)); \
+ } while (0)
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+namespace {
+
+static void fill_n_(
+ char* buff
+, int n
+, char ch
+)
+{
+ int i;
+
+ for (i = 0; i != n; ++i)
+ {
+ buff[i] = ch;
+ }
+
+ buff[n] = '\0';
+}
+
+/* Absence is a failed lookup: the function returns 0, sets errno to EINVAL,
+ * and writes 0 through result.
+ */
+static void TEST_environment_variable_strtoll_WHEN_ABSENT()
+{
+ environment_variable_scope const absent(VAR_NAME, ss_nullptr_k);
+
+ TEST_READ_REJECTED_(VAR_NAME, EINVAL);
+}
+
+static void TEST_environment_variable_strtoll_WHEN_DECIMAL()
+{
+ TEST_PARSED_("0", 0);
+ TEST_PARSED_("00", 0);
+ TEST_PARSED_("+0", 0);
+ TEST_PARSED_("-0", 0);
+ TEST_PARSED_("1", 1);
+ TEST_PARSED_("42", 42);
+ TEST_PARSED_("+42", 42);
+ TEST_PARSED_("-42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_(" -42", -42);
+ TEST_PARSED_("-123456", -123456);
+
+ TEST_PARSED_("9223372036854775807", STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807));
+ TEST_PARSED_("-9223372036854775807", -STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807));
+ TEST_PARSED_("-9223372036854775808", -STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807) - 1);
+}
+
+/* Base 0 follows strtoll(): a leading 0 selects octal and a leading 0x
+ * selects hexadecimal. The whole value must be one subject sequence.
+ */
+static void TEST_environment_variable_strtoll_WHEN_BASE_PREFIXES()
+{
+ TEST_PARSED_("010", 8);
+ TEST_PARSED_("+010", 8);
+ TEST_PARSED_("-010", -8);
+ TEST_PARSED_("0x10", 16);
+ TEST_PARSED_("0X10", 16);
+ TEST_PARSED_("0xff", 255);
+ TEST_PARSED_("0xFf", 255);
+ TEST_PARSED_("+0x10", 16);
+ TEST_PARSED_("-0x10", -16);
+ TEST_PARSED_("0x0", 0);
+}
+
+/* Leading whitespace belongs to a strtoll() subject sequence. Trailing
+ * characters, including whitespace, do not.
+ */
+static void TEST_environment_variable_strtoll_WHEN_LEADING_WHITESPACE()
+{
+ TEST_PARSED_(" 42", 42);
+ TEST_PARSED_("\t42", 42);
+ TEST_PARSED_(" \t -42", -42);
+ TEST_PARSED_(" +0x10", 16);
+
+ TEST_REJECTED_("42 ", EINVAL);
+ TEST_REJECTED_(" 42 ", EINVAL);
+ TEST_REJECTED_("42\n", EINVAL);
+}
+
+static void TEST_environment_variable_strtoll_WHEN_NOT_COMPLETE()
+{
+ TEST_REJECTED_("abc", EINVAL);
+ TEST_REJECTED_(" ", EINVAL);
+ TEST_REJECTED_("+", EINVAL);
+ TEST_REJECTED_("-", EINVAL);
+ TEST_REJECTED_("++1", EINVAL);
+ TEST_REJECTED_("--1", EINVAL);
+ TEST_REJECTED_("0x", EINVAL);
+ TEST_REJECTED_("0xG", EINVAL);
+ TEST_REJECTED_("123abc", EINVAL);
+ TEST_REJECTED_("08", EINVAL);
+ TEST_REJECTED_("09", EINVAL);
+ TEST_REJECTED_("1.5", EINVAL);
+ TEST_REJECTED_("1e2", EINVAL);
+}
+
+static void TEST_environment_variable_strtoll_WHEN_OUT_OF_RANGE()
+{
+ /* Out-of-range values are rejected with ERANGE and result is set to 0,
+ * rather than the saturated limits that strtoll() returns.
+ */
+ TEST_REJECTED_("9223372036854775808", ERANGE);
+ TEST_REJECTED_("-9223372036854775809", ERANGE);
+}
+
+/* The complete environment-variable value is read before parsing, so a
+ * valid value remains valid when preceded by substantial whitespace.
+ */
+static void TEST_environment_variable_strtoll_WHEN_LONG_VALUE()
+{
+ char text[1024];
+
+ fill_n_(text, 1000, ' ');
+ text[1000] = '-';
+ text[1001] = '4';
+ text[1002] = '2';
+ text[1003] = '\0';
+ TEST_PARSED_(text, -42);
+
+ text[1003] = 'x';
+ text[1004] = '\0';
+ TEST_REJECTED_(text, EINVAL);
+}
+
+static void TEST_environment_variable_strtoll_WHEN_ERASED()
+{
+ environment_variable_scope const present(VAR_NAME, "5");
+
+ TEST_READ_PARSED_(VAR_NAME, 5);
+
+ environment_variable_scope const erased(VAR_NAME, ss_nullptr_k);
+
+ TEST_READ_REJECTED_(VAR_NAME, EINVAL);
+}
+
+static void TEST_environment_variable_strtoll_WHEN_VALUE_REPLACED()
+{
+ environment_variable_scope const first(VAR_NAME, "11");
+
+ TEST_READ_PARSED_(VAR_NAME, 11);
+
+ environment_variable_scope const second(VAR_NAME, "22");
+
+ TEST_READ_PARSED_(VAR_NAME, 22);
+}
+
+static void TEST_environment_variable_strtoll_EXACT_NAME()
+{
+ environment_variable_scope const extra_absent(VAR_NAME_EXTRA, ss_nullptr_k);
+ environment_variable_scope const prefix_absent(VAR_NAME_PREFIX, ss_nullptr_k);
+ environment_variable_scope const present(VAR_NAME, "42");
+
+ TEST_READ_PARSED_(VAR_NAME, 42);
+ TEST_READ_REJECTED_(VAR_NAME_EXTRA, EINVAL);
+ TEST_READ_REJECTED_(VAR_NAME_PREFIX, EINVAL);
+
+ environment_variable_scope const erased(VAR_NAME, ss_nullptr_k);
+ environment_variable_scope const extra(VAR_NAME_EXTRA, "7");
+
+ TEST_READ_REJECTED_(VAR_NAME, EINVAL);
+ TEST_READ_PARSED_(VAR_NAME_EXTRA, 7);
+}
+
+/* UNIX names are case-sensitive; the Windows CRT folds case.
+ */
+static void TEST_environment_variable_strtoll_CASE_SENSITIVITY()
+{
+ environment_variable_scope const lower(VAR_NAME_LOWER, ss_nullptr_k);
+ environment_variable_scope const upper(VAR_NAME_UPPER, "7");
+
+ TEST_READ_PARSED_(VAR_NAME_UPPER, 7);
+#ifdef _WIN32
+
+ TEST_READ_PARSED_(VAR_NAME_LOWER, 7);
+#else
+
+ TEST_READ_REJECTED_(VAR_NAME_LOWER, EINVAL);
+#endif
+}
+
+static void TEST_environment_variable_strtoll_CPP_WRAPPER()
+{
+ environment_variable_scope const value(VAR_NAME, "9223372036854775807");
+ stlsoft::ss_sint64_t result = SENTINEL_;
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_strtoll(VAR_NAME, &result));
+ TEST_INT_EQ(STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807), result);
+}
+
+#ifndef _WIN32
+
+/* An empty value is defined on UNIX, and it is not a number. The Windows
+ * CRT treats an empty assignment as erasure, so this case is UNIX-only.
+ */
+static void TEST_environment_variable_strtoll_WHEN_EMPTY()
+{
+ TEST_REJECTED_("", EINVAL);
+}
+#endif /* !_WIN32 */
+} // anonymous namespace
+
+#undef TEST_PARSED_
+#undef TEST_READ_PARSED_
+#undef TEST_READ_REJECTED_
+#undef TEST_REJECTED_
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/CMakeLists.txt b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/CMakeLists.txt
new file mode 100644
index 00000000..9107eda8
--- /dev/null
+++ b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/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.stlsoft.system.environment_variable_exists entry.cpp)
diff --git a/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/entry.cpp b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/entry.cpp
new file mode 100644
index 00000000..7f7bbca0
--- /dev/null
+++ b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/entry.cpp
@@ -0,0 +1,259 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: test.component.stlsoft.system.environment_variable_exists/entry.cpp
+ *
+ * Purpose: Component-tests for `stlsoft::environment_variable_exists()`.
+ *
+ * Created: 28th September 2026
+ * Updated: 28th September 2026
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+/* /////////////////////////////////////
+ * test component header file include(s)
+ */
+
+/* String-access shims must be visible before functions.hpp so the template
+ * overload can bind c_str_ptr() for these types.
+ */
+#include
+#include
+#include
+
+/* /////////////////////////////////////
+ * general includes
+ */
+
+/* xTests header files */
+#include
+#include
+
+/* STLSoft header files */
+#include
+#include
+
+/* Standard C++ header files */
+#include
+
+/* Standard C header files */
+#include
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+
+namespace {
+
+ static void TEST_environment_variable_exists_WHEN_ABSENT();
+ static void TEST_environment_variable_exists_WHEN_PRESENT();
+ static void TEST_environment_variable_exists_WHEN_ERASED();
+ static void TEST_environment_variable_exists_WHEN_VALUE_REPLACED();
+ static void TEST_environment_variable_exists_EXACT_NAME();
+ static void TEST_environment_variable_exists_WITH_std_string();
+ static void TEST_environment_variable_exists_WITH_simple_string();
+#ifndef _WIN32
+ static void TEST_environment_variable_exists_WHEN_EMPTY();
+#endif /* !_WIN32 */
+ static void TEST_environment_variable_exists_CASE_SENSITIVITY();
+} // 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.stlsoft.system.environment_variable_exists", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_ABSENT);
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_PRESENT);
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_ERASED);
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_VALUE_REPLACED);
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_EXACT_NAME);
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WITH_std_string);
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WITH_simple_string);
+#ifndef _WIN32
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_EMPTY);
+#endif /* !_WIN32 */
+ XTESTS_RUN_CASE(TEST_environment_variable_exists_CASE_SENSITIVITY);
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * constants
+ */
+
+namespace {
+
+ using platformstl::environment_variable_scope;
+
+ char const VAR_NAME[] = "STLSOFT_CT_ENV_VAR_EXISTS";
+ char const VAR_NAME_EXTRA[] = "STLSOFT_CT_ENV_VAR_EXISTS_EXTRA";
+ char const VAR_NAME_PREFIX[] = "STLSOFT_CT_ENV_VAR";
+ char const VAR_NAME_UPPER[] = "STLSOFT_CT_ENV_VAR_EXISTS_CASE";
+ char const VAR_NAME_LOWER[] = "stlsoft_ct_env_var_exists_case";
+} // anonymous namespace
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+namespace {
+
+static void TEST_environment_variable_exists_WHEN_ABSENT()
+{
+ environment_variable_scope const absent(VAR_NAME, ss_nullptr_k);
+
+ char const* const name = VAR_NAME;
+
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name));
+ TEST_BOOLEAN_FALSE(stlsoft::stlsoft_C_environment_variable_exists_m(name));
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists("STLSOFT_CT_ENV_VAR_EXISTS_NO_SUCH"));
+}
+
+static void TEST_environment_variable_exists_WHEN_PRESENT()
+{
+ environment_variable_scope const present(VAR_NAME, "present");
+
+ char const* const name = VAR_NAME;
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+ TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name));
+}
+
+static void TEST_environment_variable_exists_WHEN_ERASED()
+{
+ environment_variable_scope const present(VAR_NAME, "present");
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME));
+
+ environment_variable_scope const erased(VAR_NAME, ss_nullptr_k);
+
+ char const* const name = VAR_NAME;
+
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name));
+}
+
+static void TEST_environment_variable_exists_WHEN_VALUE_REPLACED()
+{
+ environment_variable_scope const first(VAR_NAME, "one");
+
+ char const* const name = VAR_NAME;
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+
+ environment_variable_scope const second(VAR_NAME, "two");
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+}
+
+static void TEST_environment_variable_exists_EXACT_NAME()
+{
+ environment_variable_scope const extra_absent(VAR_NAME_EXTRA, ss_nullptr_k);
+ environment_variable_scope const prefix_absent(VAR_NAME_PREFIX, ss_nullptr_k);
+ environment_variable_scope const present(VAR_NAME, "value");
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME));
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME_EXTRA));
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME_PREFIX));
+
+ environment_variable_scope const erased(VAR_NAME, ss_nullptr_k);
+ environment_variable_scope const extra(VAR_NAME_EXTRA, "extra");
+
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME));
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME_EXTRA));
+}
+
+static void TEST_environment_variable_exists_WITH_std_string()
+{
+ {
+ environment_variable_scope const absent(VAR_NAME, ss_nullptr_k);
+
+ std::string const name(VAR_NAME);
+
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name));
+ }
+
+ environment_variable_scope const present(VAR_NAME, "present");
+
+ std::string const name(VAR_NAME);
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+}
+
+static void TEST_environment_variable_exists_WITH_simple_string()
+{
+ {
+ environment_variable_scope const absent(VAR_NAME, ss_nullptr_k);
+
+ stlsoft::simple_string const name(VAR_NAME);
+
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name));
+ }
+
+ environment_variable_scope const present(VAR_NAME, "present");
+
+ stlsoft::simple_string const name(VAR_NAME);
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+}
+#ifndef _WIN32
+
+/* An empty value is a defined variable on UNIX. The Windows CRT treats an
+ * empty assignment as erasure, so this case is UNIX-only.
+ */
+static void TEST_environment_variable_exists_WHEN_EMPTY()
+{
+ environment_variable_scope const empty(VAR_NAME, "");
+
+ char const* const value = ::getenv(VAR_NAME);
+
+ XTESTS_REQUIRE(TEST_PTR_NE(NULL, value));
+ TEST_MS_EQ("", value);
+
+ char const* const name = VAR_NAME;
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name));
+}
+#endif /* !_WIN32 */
+
+/* UNIX names are case-sensitive; the Windows CRT folds case.
+ */
+static void TEST_environment_variable_exists_CASE_SENSITIVITY()
+{
+ environment_variable_scope const lower(VAR_NAME_LOWER, ss_nullptr_k);
+ environment_variable_scope const upper(VAR_NAME_UPPER, "1");
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME_UPPER));
+#ifdef _WIN32
+
+ TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME_LOWER));
+#else
+
+ TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME_LOWER));
+#endif
+}
+} // anonymous namespace
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/test/component/unixstl/filesystem/CMakeLists.txt b/test/component/unixstl/filesystem/CMakeLists.txt
index 7d8d9318..be579252 100644
--- a/test/component/unixstl/filesystem/CMakeLists.txt
+++ b/test/component/unixstl/filesystem/CMakeLists.txt
@@ -1,4 +1,5 @@
# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+add_subdirectory(filesystem_traits)
add_subdirectory(glob_sequence)
add_subdirectory(memory_mapped_file)
add_subdirectory(readdir_sequence)
diff --git a/test/component/unixstl/filesystem/filesystem_traits/CMakeLists.txt b/test/component/unixstl/filesystem/filesystem_traits/CMakeLists.txt
new file mode 100644
index 00000000..8460ebf3
--- /dev/null
+++ b/test/component/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
+add_subdirectory(opendir)
diff --git a/test/component/unixstl/filesystem/filesystem_traits/opendir/CMakeLists.txt b/test/component/unixstl/filesystem/filesystem_traits/opendir/CMakeLists.txt
new file mode 100644
index 00000000..204e5114
--- /dev/null
+++ b/test/component/unixstl/filesystem/filesystem_traits/opendir/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.filesystem.filesystem_traits.opendir entry.cpp)
diff --git a/test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp b/test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp
new file mode 100644
index 00000000..991257a2
--- /dev/null
+++ b/test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp
@@ -0,0 +1,449 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp)
+ *
+ * Purpose: Component-tests for `unixstl::filesystem_traits`, in respect of
+ * the directory-enumeration functions `open_dir()`, `read_dir()`,
+ * `rewind_dir()`, and `close_dir()`.
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+/* /////////////////////////////////////
+ * test component header file include(s)
+ */
+
+#include
+
+/* /////////////////////////////////////
+ * general includes
+ */
+
+#include
+
+/* xTests header files */
+#include
+#include
+#include
+#include
+
+/* Standard C++ header files */
+#include
+#include
+#include
+
+/* Standard C header files */
+#include
+#include
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+
+namespace {
+
+ static void TEST_open_dir_NONEXISTENT_directory();
+ static void TEST_read_dir_EMPTY_directory();
+ static void TEST_read_dir_NON_EMPTY_directory();
+ static void TEST_rewind_dir_AFTER_EXHAUSTION();
+ static void TEST_rewind_dir_AFTER_PARTIAL_READ();
+ static void TEST_rewind_dir_BEFORE_ANY_READ();
+ static void TEST_rewind_dir_EMPTY_directory();
+ static void TEST_rewind_dir_MULTIPLE_times();
+} // 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.filesystem.filesystem_traits.opendir", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_open_dir_NONEXISTENT_directory);
+ XTESTS_RUN_CASE(TEST_read_dir_EMPTY_directory);
+ XTESTS_RUN_CASE(TEST_read_dir_NON_EMPTY_directory);
+ XTESTS_RUN_CASE(TEST_rewind_dir_AFTER_EXHAUSTION);
+ XTESTS_RUN_CASE(TEST_rewind_dir_AFTER_PARTIAL_READ);
+ XTESTS_RUN_CASE(TEST_rewind_dir_BEFORE_ANY_READ);
+ XTESTS_RUN_CASE(TEST_rewind_dir_EMPTY_directory);
+ XTESTS_RUN_CASE(TEST_rewind_dir_MULTIPLE_times);
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * types
+ */
+
+namespace {
+
+ typedef unixstl::filesystem_traits fs_traits_t;
+ typedef platformstl::path path_t;
+ typedef std::vector names_t;
+ using ::xtests::cpp::util::temp_directory;
+ using ::xtests::cpp::util::temp_file;
+} // anonymous namespace
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * helper functions
+ */
+
+namespace {
+
+ /* Three temporary files, created in a given (temporary) directory,
+ * and deleted on destruction. Must be declared after, so destroyed
+ * before, the `temp_directory` in which they are created.
+ */
+ class three_files
+ {
+ public:
+ explicit
+ three_files(
+ char const* dir
+ )
+ : m_f0(temp_file::DeleteOnClose | temp_file::CloseOnOpen, dir)
+ , m_f1(temp_file::DeleteOnClose | temp_file::CloseOnOpen, dir)
+ , m_f2(temp_file::DeleteOnClose | temp_file::CloseOnOpen, dir)
+ {}
+
+ public:
+ /* The names (i.e. without directory) of the files, sorted */
+ names_t
+ names() const
+ {
+ names_t result;
+
+ result.push_back(leaf_name(m_f0.c_str()));
+ result.push_back(leaf_name(m_f1.c_str()));
+ result.push_back(leaf_name(m_f2.c_str()));
+
+ std::sort(result.begin(), result.end());
+
+ return result;
+ }
+
+ private:
+ static
+ std::string
+ leaf_name(
+ char const* path
+ )
+ {
+ char const* const sep = ::strrchr(path, '/');
+
+ return (NULL == sep) ? path : (sep + 1);
+ }
+
+ private:
+ temp_file m_f0;
+ temp_file m_f1;
+ temp_file m_f2;
+ };
+
+ /* Reads up to `max` entries from `h` into `names`; returns the number
+ * of entries read
+ */
+ static
+ size_t
+ read_entries(
+ DIR* h
+ , names_t& names
+ , size_t max
+ )
+ {
+ size_t n = 0;
+
+ for (struct dirent const* de; n < max && NULL != (de = fs_traits_t::read_dir(h)); ++n)
+ {
+ names.push_back(de->d_name);
+ }
+
+ return n;
+ }
+
+ /* Reads all remaining entries from `h` into `names`; returns the number
+ * of entries read
+ */
+ static
+ size_t
+ read_all_entries(
+ DIR* h
+ , names_t& names
+ )
+ {
+ return read_entries(h, names, ~size_t(0));
+ }
+
+ /* Removes the entries "." and ".." and sorts, so that sequences can be
+ * compared irrespective of the (unspecified) order of enumeration
+ */
+ static
+ names_t
+ normalise(
+ names_t names
+ )
+ {
+ names_t result;
+
+ for (names_t::const_iterator i = names.begin(); names.end() != i; ++i)
+ {
+ if ("." != *i && ".." != *i)
+ {
+ result.push_back(*i);
+ }
+ }
+
+ std::sort(result.begin(), result.end());
+
+ return result;
+ }
+
+} // anonymous namespace
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+namespace {
+
+static void TEST_open_dir_NONEXISTENT_directory()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+ path_t const path = path_t(td.c_str()) / "does-not-exist";
+
+ DIR* const h = fs_traits_t::open_dir(path.c_str());
+
+ TEST_BOOLEAN_TRUE(NULL == h);
+
+ if (NULL != h)
+ {
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_read_dir_EMPTY_directory()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ names_t names;
+
+ read_all_entries(h, names);
+
+ TEST_INT_EQ(0u, normalise(names).size());
+
+ TEST_BOOLEAN_TRUE(NULL == fs_traits_t::read_dir(h));
+
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_read_dir_NON_EMPTY_directory()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ three_files files(td.c_str());
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ names_t names;
+
+ read_all_entries(h, names);
+
+ TEST_BOOLEAN_TRUE(files.names() == normalise(names));
+
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_rewind_dir_BEFORE_ANY_READ()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ three_files files(td.c_str());
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ names_t names;
+
+ fs_traits_t::rewind_dir(h);
+
+ read_all_entries(h, names);
+
+ TEST_BOOLEAN_TRUE(files.names() == normalise(names));
+
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_rewind_dir_AFTER_PARTIAL_READ()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ three_files files(td.c_str());
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ names_t first_pass;
+ names_t second_pass;
+
+ // Read the first two entries (of "." + ".." + 3 files, in some order)
+
+ TEST_INT_EQ(2u, read_entries(h, first_pass, 2));
+
+ fs_traits_t::rewind_dir(h);
+
+ read_all_entries(h, second_pass);
+
+ // After the rewind, *all* entries are returned, including those
+ // that had been read before the rewind
+
+ TEST_INT_EQ(5u, second_pass.size());
+ TEST_BOOLEAN_TRUE(files.names() == normalise(second_pass));
+
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_rewind_dir_AFTER_EXHAUSTION()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ three_files files(td.c_str());
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ names_t first_pass;
+ names_t second_pass;
+
+ read_all_entries(h, first_pass);
+
+ TEST_BOOLEAN_TRUE(NULL == fs_traits_t::read_dir(h));
+
+ fs_traits_t::rewind_dir(h);
+
+ read_all_entries(h, second_pass);
+
+ TEST_BOOLEAN_TRUE(files.names() == normalise(first_pass));
+ TEST_BOOLEAN_TRUE(files.names() == normalise(second_pass));
+ TEST_INT_EQ(first_pass.size(), second_pass.size());
+
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_rewind_dir_EMPTY_directory()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ names_t names;
+
+ read_all_entries(h, names);
+
+ fs_traits_t::rewind_dir(h);
+
+ names_t names_after;
+
+ read_all_entries(h, names_after);
+
+ TEST_INT_EQ(0u, normalise(names_after).size());
+ TEST_INT_EQ(names.size(), names_after.size());
+
+ fs_traits_t::close_dir(h);
+ }
+}
+
+static void TEST_rewind_dir_MULTIPLE_times()
+{
+ temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose);
+
+ three_files files(td.c_str());
+
+ DIR* const h = fs_traits_t::open_dir(td.c_str());
+
+ if (NULL == h)
+ {
+ TEST_FAIL("failed to open directory");
+ }
+ else
+ {
+ for (int pass = 0; pass != 3; ++pass)
+ {
+ names_t names;
+
+ read_all_entries(h, names);
+
+ TEST_BOOLEAN_TRUE(files.names() == normalise(names));
+
+ fs_traits_t::rewind_dir(h);
+ }
+
+ fs_traits_t::close_dir(h);
+ }
+}
+} // anonymous namespace
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/test/scratch/unixstl/CMakeLists.txt b/test/scratch/unixstl/CMakeLists.txt
index beef2694..719e8486 100644
--- a/test/scratch/unixstl/CMakeLists.txt
+++ b/test/scratch/unixstl/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)
add_subdirectory(synch)
add_subdirectory(system)
diff --git a/test/scratch/unixstl/filesystem/CMakeLists.txt b/test/scratch/unixstl/filesystem/CMakeLists.txt
new file mode 100644
index 00000000..5fa1138e
--- /dev/null
+++ b/test/scratch/unixstl/filesystem/CMakeLists.txt
@@ -0,0 +1,2 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+add_subdirectory(rewind_dir)
diff --git a/test/scratch/unixstl/filesystem/rewind_dir/CMakeLists.txt b/test/scratch/unixstl/filesystem/rewind_dir/CMakeLists.txt
new file mode 100644
index 00000000..586b2901
--- /dev/null
+++ b/test/scratch/unixstl/filesystem/rewind_dir/CMakeLists.txt
@@ -0,0 +1,2 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+define_example_program(test.scratch.unixstl.filesystem.rewind_dir main.cpp)
diff --git a/test/scratch/unixstl/filesystem/rewind_dir/main.cpp b/test/scratch/unixstl/filesystem/rewind_dir/main.cpp
new file mode 100644
index 00000000..595999e0
--- /dev/null
+++ b/test/scratch/unixstl/filesystem/rewind_dir/main.cpp
@@ -0,0 +1,112 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: rewind_dir/main.cpp
+ *
+ * Purpose: Scratch test for `unixstl::filesystem_traits<>::rewind_dir()`,
+ * exploring its interaction with `open_dir()`, `read_dir()`, and
+ * `close_dir()`, on a directory given on the command-line (or the
+ * current directory, if none is given).
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+#include
+
+#include
+
+#include
+#include
+
+
+typedef unixstl::filesystem_traits fs_traits_t;
+
+
+namespace {
+
+ /* Reads up to `max` entries from `h`, printing each, and returns the
+ * number read
+ */
+ size_t
+ dump_entries(
+ DIR* h
+ , char const* label
+ , size_t max
+ )
+ {
+ size_t n = 0;
+
+ fprintf(stdout, "%s:\n", label);
+
+ for (struct dirent const* de; n < max && NULL != (de = fs_traits_t::read_dir(h)); ++n)
+ {
+ fprintf(stdout, "\t%s\n", de->d_name);
+ }
+
+ fprintf(stdout, "\t(%lu entries)\n", static_cast(n));
+
+ return n;
+ }
+} // anonymous namespace
+
+
+int main(int argc, char* argv[])
+{
+ char const* const program_name = platformstl::get_executable_name_from_path(argv[0]).ptr;
+ char const* dir = ".";
+
+ if (argc > 1)
+ {
+ dir = argv[1];
+ }
+
+ DIR* const h = fs_traits_t::open_dir(dir);
+
+ if (NULL == h)
+ {
+ fprintf(stderr, "%s: could not open directory '%s'\n", program_name, dir);
+
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ size_t const n1 = dump_entries(h, "first pass (full)", ~size_t(0));
+ size_t const n2 = dump_entries(h, "(no rewind; expect nothing)", ~size_t(0));
+
+ fs_traits_t::rewind_dir(h);
+
+ size_t const n3 = dump_entries(h, "second pass (after rewind)", ~size_t(0));
+
+ fs_traits_t::rewind_dir(h);
+
+ size_t const n4 = dump_entries(h, "third pass (after rewind; first two entries only)", 2);
+
+ fs_traits_t::rewind_dir(h);
+
+ size_t const n5 = dump_entries(h, "fourth pass (after rewind from partial read)", ~size_t(0));
+
+ fs_traits_t::close_dir(h);
+
+ fprintf(stdout, "\nsummary: n1=%lu; n2=%lu; n3=%lu; n4=%lu; n5=%lu\n"
+ , static_cast(n1)
+ , static_cast(n2)
+ , static_cast(n3)
+ , static_cast(n4)
+ , static_cast(n5)
+ );
+
+ if (0 != n2 || n1 != n3 || n1 != n5)
+ {
+ fprintf(stderr, "%s: unexpected entry counts\n", program_name);
+
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/test/unit/platformstl/CMakeLists.txt b/test/unit/platformstl/CMakeLists.txt
index 19138ddd..985b6996 100644
--- a/test/unit/platformstl/CMakeLists.txt
+++ b/test/unit/platformstl/CMakeLists.txt
@@ -1,2 +1,3 @@
# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+add_subdirectory(process)
add_subdirectory(system)
diff --git a/test/unit/platformstl/process/CMakeLists.txt b/test/unit/platformstl/process/CMakeLists.txt
new file mode 100644
index 00000000..992517e9
--- /dev/null
+++ b/test/unit/platformstl/process/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+add_subdirectory(functions)
+
diff --git a/test/unit/platformstl/process/functions/CMakeLists.txt b/test/unit/platformstl/process/functions/CMakeLists.txt
new file mode 100644
index 00000000..bd138b53
--- /dev/null
+++ b/test/unit/platformstl/process/functions/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+define_automated_test_program(test.unit.platformstl.process.functions entry.c)
+
diff --git a/test/unit/platformstl/process/functions/entry.c b/test/unit/platformstl/process/functions/entry.c
new file mode 100644
index 00000000..0133a8ab
--- /dev/null
+++ b/test/unit/platformstl/process/functions/entry.c
@@ -0,0 +1,106 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: functions/entry.c
+ *
+ * Purpose: Unit-tests for `platformstl_C_get_current_process_id()`.
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+/* /////////////////////////////////////
+ * test component header file include(s)
+ */
+
+#include
+
+/* /////////////////////////////////////
+ * general includes
+ */
+
+/* xTests header files */
+#include
+#include
+
+/* STLSoft header files */
+#include
+
+/* Standard C header files */
+#include
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+
+static void TEST_EQUALS_SELECTED_PLATFORM_FUNCTION(void);
+static void TEST_IS_POSITIVE(void);
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * 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.platformstl.process.functions", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_EQUALS_SELECTED_PLATFORM_FUNCTION);
+ XTESTS_RUN_CASE(TEST_IS_POSITIVE);
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+static void TEST_EQUALS_SELECTED_PLATFORM_FUNCTION(void)
+{
+ int expected;
+ int actual;
+
+#if defined(PLATFORMSTL_OS_IS_UNIX)
+
+ expected = unixstl_C_get_current_process_id();
+#elif defined(PLATFORMSTL_OS_IS_WINDOWS)
+
+ expected = winstl_C_get_current_process_id();
+#else
+
+# error Platform not discriminated
+#endif
+
+ actual = platformstl_C_get_current_process_id();
+
+ TEST_INT_EQ(expected, actual);
+}
+
+static void TEST_IS_POSITIVE(void)
+{
+ int const result = platformstl_C_get_current_process_id();
+
+ TEST_INT_GT(0, result);
+}
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/test/unit/unixstl/CMakeLists.txt b/test/unit/unixstl/CMakeLists.txt
index 5eb6b8bd..ba184416 100644
--- a/test/unit/unixstl/CMakeLists.txt
+++ b/test/unit/unixstl/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)
+add_subdirectory(process)
add_subdirectory(shims)
diff --git a/test/unit/unixstl/process/CMakeLists.txt b/test/unit/unixstl/process/CMakeLists.txt
new file mode 100644
index 00000000..992517e9
--- /dev/null
+++ b/test/unit/unixstl/process/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+add_subdirectory(functions)
+
diff --git a/test/unit/unixstl/process/functions/CMakeLists.txt b/test/unit/unixstl/process/functions/CMakeLists.txt
new file mode 100644
index 00000000..6884e0ba
--- /dev/null
+++ b/test/unit/unixstl/process/functions/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+define_automated_test_program(test.unit.unixstl.process.functions entry.c)
+
diff --git a/test/unit/unixstl/process/functions/entry.c b/test/unit/unixstl/process/functions/entry.c
new file mode 100644
index 00000000..e3b090c5
--- /dev/null
+++ b/test/unit/unixstl/process/functions/entry.c
@@ -0,0 +1,127 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: functions/entry.c
+ *
+ * Purpose: Unit-tests for `unixstl_C_get_current_process_id()`.
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+/* /////////////////////////////////////
+ * test component header file include(s)
+ */
+
+#include
+
+/* /////////////////////////////////////
+ * general includes
+ */
+
+/* xTests header files */
+#include
+#include
+
+/* STLSoft header files */
+#include
+
+/* Standard C header files */
+#include
+
+#if defined(_WIN32) && \
+ ( defined(STLSOFT_COMPILER_IS_MSVC) || \
+ defined(STLSOFT_COMPILER_IS_INTEL))
+# include
+#else
+# include
+# include
+#endif
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+
+static void TEST_EQUALS_NATIVE_PROCESS_ID(void);
+static void TEST_IS_POSITIVE(void);
+static void TEST_IS_STABLE(void);
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * 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.process.functions", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_EQUALS_NATIVE_PROCESS_ID);
+ XTESTS_RUN_CASE(TEST_IS_POSITIVE);
+ XTESTS_RUN_CASE(TEST_IS_STABLE);
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+static void TEST_EQUALS_NATIVE_PROCESS_ID(void)
+{
+ int expected;
+ int actual;
+
+#if 0
+#elif defined(_WIN32) && \
+ ( 0 ||\
+ defined(STLSOFT_COMPILER_IS_MSVC) ||\
+ defined(STLSOFT_COMPILER_IS_INTEL) ||\
+ 0)
+
+ expected = _getpid();
+#else
+
+ expected = STLSOFT_STATIC_CAST(int, getpid());
+#endif
+
+ actual = unixstl_C_get_current_process_id();
+
+ TEST_INT_EQ(expected, actual);
+}
+
+static void TEST_IS_POSITIVE(void)
+{
+ int const result = unixstl_C_get_current_process_id();
+
+ TEST_INT_GT(0, result);
+}
+
+static void TEST_IS_STABLE(void)
+{
+ int const first = unixstl_C_get_current_process_id();
+ int const second = unixstl_C_get_current_process_id();
+
+ TEST_INT_EQ(first, second);
+}
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+
diff --git a/test/unit/versions/entry.cpp b/test/unit/versions/entry.cpp
index b18bd6a3..4f1128d1 100644
--- a/test/unit/versions/entry.cpp
+++ b/test/unit/versions/entry.cpp
@@ -4,7 +4,7 @@
* Purpose: Unit-tests for versions
*
* Created: 23rd August 2025
- * Updated: 30th September 2026
+ * Updated: 10th October 2026
*
* ////////////////////////////////////////////////////////////////////// */
@@ -133,7 +133,7 @@ namespace {
static void TEST__STLSOFT_VER()
{
- TEST_INT_EQ(_STLSOFT_VER_1_11_1_RC7, _STLSOFT_VER);
+ TEST_INT_EQ(_STLSOFT_VER_1_11_1_RC8, _STLSOFT_VER);
}
#ifdef STLSOFT_HAS_ACE
@@ -152,7 +152,7 @@ static void TEST__ATLSTL_VER()
static void TEST__PLATFORMSTL_VER()
{
- TEST_INT_EQ(_PLATFORMSTL_VER_1_10_0_B01, _PLATFORMSTL_VER);
+ TEST_INT_EQ(_PLATFORMSTL_VER_1_10_0_B02, _PLATFORMSTL_VER);
}
#if 0
@@ -160,7 +160,7 @@ static void TEST__PLATFORMSTL_VER()
static void TEST__UNIXSTL_VER()
{
- TEST_INT_EQ(_UNIXSTL_VER_1_8_6_B06, _UNIXSTL_VER);
+ TEST_INT_EQ(_UNIXSTL_VER_1_8_6_B07, _UNIXSTL_VER);
}
#elif defined(PLATFORMSTL_OS_IS_WINDOWS)
@@ -171,7 +171,7 @@ static void TEST__COMSTL_VER()
static void TEST__WINSTL_VER()
{
- TEST_INT_EQ(_WINSTL_VER_1_13_0_B03, _WINSTL_VER);
+ TEST_INT_EQ(_WINSTL_VER_1_13_0_B04, _WINSTL_VER);
}
#endif
#ifdef STLSOFT_HAS_MFC
diff --git a/test/unit/winstl/CMakeLists.txt b/test/unit/winstl/CMakeLists.txt
index 668b342f..64aff324 100644
--- a/test/unit/winstl/CMakeLists.txt
+++ b/test/unit/winstl/CMakeLists.txt
@@ -4,5 +4,6 @@ add_subdirectory(conversion)
add_subdirectory(diagnostics)
add_subdirectory(filesystem)
add_subdirectory(memory)
+add_subdirectory(process)
add_subdirectory(system)
add_subdirectory(util)
diff --git a/test/unit/winstl/process/CMakeLists.txt b/test/unit/winstl/process/CMakeLists.txt
new file mode 100644
index 00000000..992517e9
--- /dev/null
+++ b/test/unit/winstl/process/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+add_subdirectory(functions)
+
diff --git a/test/unit/winstl/process/functions/CMakeLists.txt b/test/unit/winstl/process/functions/CMakeLists.txt
new file mode 100644
index 00000000..f7bbb016
--- /dev/null
+++ b/test/unit/winstl/process/functions/CMakeLists.txt
@@ -0,0 +1,3 @@
+# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
+define_automated_test_program(test.unit.winstl.process.functions entry.c)
+
diff --git a/test/unit/winstl/process/functions/entry.c b/test/unit/winstl/process/functions/entry.c
new file mode 100644
index 00000000..4f4cdb37
--- /dev/null
+++ b/test/unit/winstl/process/functions/entry.c
@@ -0,0 +1,116 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: functions/entry.c
+ *
+ * Purpose: Unit-tests for `winstl_C_get_current_process_id()`.
+ *
+ * Created: 8th October 2026
+ * Updated: 8th October 2026
+ *
+ * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * 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
+ */
+
+static void TEST_EQUALS_GETPID(void);
+static void TEST_EQUALS_CURRENT_PROCESS_ID(void);
+static void TEST_IS_POSITIVE(void);
+static void TEST_IS_STABLE(void);
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * 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.winstl.process.functions", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_EQUALS_GETPID);
+ XTESTS_RUN_CASE(TEST_EQUALS_CURRENT_PROCESS_ID);
+ XTESTS_RUN_CASE(TEST_IS_POSITIVE);
+ XTESTS_RUN_CASE(TEST_IS_STABLE);
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+static void TEST_EQUALS_GETPID(void)
+{
+ int const expected = _getpid();
+ int const actual = winstl_C_get_current_process_id();
+
+ TEST_INT_EQ(expected, actual);
+}
+
+static void TEST_EQUALS_CURRENT_PROCESS_ID(void)
+{
+ int const expected = STLSOFT_STATIC_CAST(int, GetCurrentProcessId());
+ int const actual = winstl_C_get_current_process_id();
+
+ TEST_INT_EQ(expected, actual);
+}
+
+static void TEST_IS_POSITIVE(void)
+{
+ int const result = winstl_C_get_current_process_id();
+
+ TEST_INT_GT(0, result);
+}
+
+static void TEST_IS_STABLE(void)
+{
+ int const first = winstl_C_get_current_process_id();
+ int const second = winstl_C_get_current_process_id();
+
+ TEST_INT_EQ(first, second);
+}
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+