diff --git a/include/platformstl/filesystem/FILE_stream.hpp b/include/platformstl/filesystem/FILE_stream.hpp
index d30fd1b2..5710ce5d 100644
--- a/include/platformstl/filesystem/FILE_stream.hpp
+++ b/include/platformstl/filesystem/FILE_stream.hpp
@@ -4,11 +4,11 @@
* Purpose: Facade for the standard C Streams API.
*
* Created: 31st May 2009
- * Updated: 20th March 2025
+ * Updated: 23rd September 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) 2009-2019, Matthew Wilson and Synesis Software
* All rights reserved.
*
@@ -53,8 +53,8 @@
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_MAJOR 2
# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_MINOR 1
-# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_REVISION 5
-# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_EDIT 28
+# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_REVISION 6
+# define PLATFORMSTL_VER_PLATFORMSTL_FILESYSTEM_HPP_FILE_STREAM_EDIT 29
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
@@ -270,12 +270,31 @@ class FILE_stream_base
return write_text_(ps, cch);
}
+ /// [DEPRECATED] Writes \c cb bytes from the memory block pointed to by
+ /// \c pv to the underlying file stream.
+ ///
+ /// \deprecated Use write_binary() instead
+ ///
+ /// \exception X Thrown in \c cb bytes cannot be written to the
+ /// underlying stream
+ STLSOFT_DEPRECATED_("write(void const*, size_t) is deprecated and will be removed from a future version of STLSoft; use write_binary() instead")
+ class_type& write(void const* pv, size_type cb)
+ {
+ return write_binary(pv, cb);
+ }
+
/// Writes \c cb bytes from the memory block pointed to by \c pv to the
/// underlying file stream.
///
+ /// \note Every byte is written, including embedded NULs. Character
+ /// overloads of write() stop at the first NUL. Newline
+ /// translation follows the stream's open mode, so open with
+ /// "wb" when the stored bytes must match the memory
+ /// block.
+ ///
/// \exception X Thrown in \c cb bytes cannot be written to the
/// underlying stream
- class_type& write(void const* pv, size_type cb)
+ class_type& write_binary(void const* pv, size_type cb)
{
return write_bytes_(pv, cb);
}
diff --git a/test/component/platformstl/CMakeLists.txt b/test/component/platformstl/CMakeLists.txt
index ead898a4..9fc02d09 100644
--- a/test/component/platformstl/CMakeLists.txt
+++ b/test/component/platformstl/CMakeLists.txt
@@ -1,3 +1,4 @@
# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten
add_subdirectory(diagnostics)
+add_subdirectory(filesystem)
add_subdirectory(system)
diff --git a/test/component/platformstl/filesystem/CMakeLists.txt b/test/component/platformstl/filesystem/CMakeLists.txt
new file mode 100644
index 00000000..477a88a9
--- /dev/null
+++ b/test/component/platformstl/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(test.component.platformstl.filesystem.file_lines)
diff --git a/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/CMakeLists.txt b/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/CMakeLists.txt
new file mode 100644
index 00000000..bb50f424
--- /dev/null
+++ b/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/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.platformstl.filesystem.file_lines entry.cpp)
diff --git a/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp b/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp
new file mode 100644
index 00000000..9628b986
--- /dev/null
+++ b/test/component/platformstl/filesystem/test.component.platformstl.filesystem.file_lines/entry.cpp
@@ -0,0 +1,283 @@
+/* /////////////////////////////////////////////////////////////////////////
+ * File: test.component.platformstl.filesystem.file_lines/entry.cpp
+ *
+ * Purpose: Component-tests for `platformstl::file_lines`.
+ *
+ * Created: 23rd September 2026
+ * Updated: 23rd September 2026
+ *
+ * ////////////////////////////////////////////////////////////////////// */
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+
+/* /////////////////////////////////////
+ * test component header file include(s)
+ */
+
+#include
+
+/* /////////////////////////////////////
+ * general includes
+ */
+
+/* xTests header files */
+#include
+#include
+#include
+
+/* STLSoft header files */
+#include
+#include
+
+/* Standard C header files */
+#include
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+
+namespace {
+
+ void TEST_EMPTY_FILE();
+ void TEST_FILE_WITH_ONE_LINE_UNTERMINATED();
+ void TEST_FILE_WITH_ONE_LINE_TERMINATED();
+ void TEST_FILE_WITH_THREE_LINES();
+ void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CRLF();
+ void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CRLF();
+ void TEST_FILE_WITH_CONSECUTIVE_CRLF_GIVES_EMPTY_LINE();
+ void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CR();
+ void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CR();
+ void TEST_FILE_WITH_CONSECUTIVE_CR_GIVES_EMPTY_LINE();
+ void TEST_FILE_CONTAINING_NUL_CHARACTER_THROWS();
+} // 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.platformstl.filesystem.file_lines", verbosity))
+ {
+ XTESTS_RUN_CASE(TEST_EMPTY_FILE);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_UNTERMINATED);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_TERMINATED);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_THREE_LINES);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CRLF);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CRLF);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_CONSECUTIVE_CRLF_GIVES_EMPTY_LINE);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CR);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CR);
+ XTESTS_RUN_CASE(TEST_FILE_WITH_CONSECUTIVE_CR_GIVES_EMPTY_LINE);
+ XTESTS_RUN_CASE_THAT_THROWS(TEST_FILE_CONTAINING_NUL_CHARACTER_THROWS, platformstl::invalid_file_type_exception);
+
+ XTESTS_PRINT_RESULTS();
+
+ XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode);
+ }
+
+ return retCode;
+}
+
+
+/* /////////////////////////////////////////////////////////////////////////
+ * test function implementations
+ */
+
+namespace {
+
+ using ::xtests::cpp::util::temp_file;
+
+
+void TEST_EMPTY_FILE()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ platformstl::file_lines lines(f.c_str());
+
+ TEST_INT_EQ(0u, lines.size());
+}
+
+void TEST_FILE_WITH_ONE_LINE_UNTERMINATED()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "w");
+
+ stm.write("abc");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(1u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+}
+
+void TEST_FILE_WITH_ONE_LINE_TERMINATED()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "w");
+
+ stm.write_line("abc");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(1u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+}
+
+void TEST_FILE_WITH_THREE_LINES()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "w");
+
+ stm.write_line("abc");
+ stm.write("\nghi");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(3u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+ TEST_MS_EQ("", lines[1]);
+ TEST_MS_EQ("ghi", lines[2]);
+}
+
+void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CRLF()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write("abc\r\n");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(1u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+}
+
+void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CRLF()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write("abc\r\ndef\r\nghi\r\n");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(3u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+ TEST_MS_EQ("def", lines[1]);
+ TEST_MS_EQ("ghi", lines[2]);
+}
+
+void TEST_FILE_WITH_CONSECUTIVE_CRLF_GIVES_EMPTY_LINE()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write("abc\r\n\r\nghi");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(3u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+ TEST_MS_EQ("", lines[1]);
+ TEST_MS_EQ("ghi", lines[2]);
+}
+
+void TEST_FILE_WITH_ONE_LINE_TERMINATED_BY_CR()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write("abc\r");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(1u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+}
+
+void TEST_FILE_WITH_THREE_LINES_SEPARATED_BY_CR()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write("abc\rdef\rghi\r");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(3u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+ TEST_MS_EQ("def", lines[1]);
+ TEST_MS_EQ("ghi", lines[2]);
+}
+
+void TEST_FILE_WITH_CONSECUTIVE_CR_GIVES_EMPTY_LINE()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write("abc\r\rghi");
+ }
+
+ platformstl::file_lines lines(f.c_str());
+
+ REQUIRE(TEST_INT_EQ(3u, lines.size()));
+ TEST_MS_EQ("abc", lines[0]);
+ TEST_MS_EQ("", lines[1]);
+ TEST_MS_EQ("ghi", lines[2]);
+}
+
+void TEST_FILE_CONTAINING_NUL_CHARACTER_THROWS()
+{
+ temp_file f(temp_file::DeleteOnClose | temp_file::EmptyOnOpen | temp_file::CloseOnOpen);
+
+ {
+ char const contents[] = { 'a', 'b', 'c', '\0', 'd', 'e', 'f' };
+ platformstl::FILE_stream stm(f.c_str(), "wb");
+
+ stm.write_binary(contents, sizeof(contents));
+ }
+
+ platformstl::file_lines lines(f.c_str());
+}
+} // anonymous namespace
+
+
+/* ///////////////////////////// end of file //////////////////////////// */
+