This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/master by this push:
     new fc675993 #643 Log complete messages with embedded nul characters in 
console writers (#658)
fc675993 is described below

commit fc6759932937898121663542bdf63951cc45a36f
Author: Elliot <[email protected]>
AuthorDate: Mon May 18 22:32:16 2026 -0400

    #643 Log complete messages with embedded nul characters in console writers 
(#658)
---
 src/main/cpp/CMakeLists.txt                        |  1 +
 .../cpp/{systemerrwriter.cpp => consolewriter.cpp} | 68 +++++--------------
 src/main/cpp/systemerrwriter.cpp                   | 45 ++-----------
 src/main/cpp/systemoutwriter.cpp                   | 46 ++-----------
 src/main/include/log4cxx/helpers/systemerrwriter.h |  1 -
 src/main/include/log4cxx/helpers/systemoutwriter.h |  1 -
 .../include/log4cxx/private/consolewriter_priv.h   | 34 ++++++++++
 src/test/cpp/helpers/CMakeLists.txt                |  3 +-
 src/test/cpp/helpers/consolewritertestcase.cpp     | 77 ++++++++++++++++++++++
 9 files changed, 137 insertions(+), 139 deletions(-)

diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
index a672f9b0..92f04995 100644
--- a/src/main/cpp/CMakeLists.txt
+++ b/src/main/cpp/CMakeLists.txt
@@ -108,6 +108,7 @@ target_sources(log4cxx
   colorendpatternconverter.cpp
   configurator.cpp
   consoleappender.cpp
+  consolewriter.cpp
   cyclicbuffer.cpp
   date.cpp
   dateformat.cpp
diff --git a/src/main/cpp/systemerrwriter.cpp b/src/main/cpp/consolewriter.cpp
similarity index 55%
copy from src/main/cpp/systemerrwriter.cpp
copy to src/main/cpp/consolewriter.cpp
index 869c38d5..52118d90 100644
--- a/src/main/cpp/systemerrwriter.cpp
+++ b/src/main/cpp/consolewriter.cpp
@@ -15,82 +15,44 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
-#include <log4cxx/helpers/systemerrwriter.h>
+#include <log4cxx/private/consolewriter_priv.h>
 #include <log4cxx/helpers/transcoder.h>
-#include <stdio.h>
 #if !defined(LOG4CXX)
        #define LOG4CXX 1
 #endif
 #include <log4cxx/private/log4cxx_private.h>
 
 using namespace LOG4CXX_NS;
-using namespace LOG4CXX_NS::helpers;
 
-IMPLEMENT_LOG4CXX_OBJECT(SystemErrWriter)
-
-SystemErrWriter::SystemErrWriter()
-{
-}
-
-SystemErrWriter::~SystemErrWriter()
-{
-}
-
-void SystemErrWriter::close( LOG4CXX_CLOSE_WRITER_FORMAL_PARAMETERS )
-{
-}
-
-void SystemErrWriter::flush( LOG4CXX_FLUSH_WRITER_FORMAL_PARAMETERS )
-{
-       fflush(stderr);
-}
-
-void SystemErrWriter::write( LOG4CXX_WRITE_WRITER_FORMAL_PARAMETERS )
-{
-#if LOG4CXX_WCHAR_T_API
-       if (isWide())
-       {
-               LOG4CXX_ENCODE_WCHAR(msg, str);
-               fputws(msg.c_str(), stderr);
-               return;
-       }
-
-#endif
-       LOG4CXX_ENCODE_CHAR(msg, str);
-       fputs(msg.c_str(), stderr);
-}
-
-bool SystemErrWriter::isWide()
+static bool isConsoleWide(FILE *file)
 {
 #if LOG4CXX_FORCE_WIDE_CONSOLE
        return true;
 #elif LOG4CXX_FORCE_BYTE_CONSOLE || !LOG4CXX_HAS_FWIDE
        return false;
 #else
-       return fwide(stderr, 0) > 0;
+       return fwide(file, 0) > 0;
 #endif
 }
 
-#if LOG4CXX_ABI_VERSION <= 15
-void SystemErrWriter::write(const LogString& str)
+size_t helpers::writeToConsole(const LogString& str, FILE *file)
 {
 #if LOG4CXX_WCHAR_T_API
-
-       if (isWide())
+       if (isConsoleWide(file))
        {
                LOG4CXX_ENCODE_WCHAR(msg, str);
-               fputws(msg.c_str(), stderr);
-               return;
+               int status = fputws(msg.c_str(), file);
+               return status == EOF ? 0 : msg.size();
        }
-
 #endif
+
        LOG4CXX_ENCODE_CHAR(msg, str);
-       fputs(msg.c_str(), stderr);
-}
 
-void SystemErrWriter::flush()
-{
-       fflush(stderr);
+       //
+       // We can't use fputs, fprintf, or even a `%.*s` specifier
+       // as the message may contain embedded null bytes, which would cause the
+       // message to be prematurely truncated.
+       //
+       msg.append("\n");
+       return fwrite(msg.data(), 1, msg.size(), file);
 }
-#endif
\ No newline at end of file
diff --git a/src/main/cpp/systemerrwriter.cpp b/src/main/cpp/systemerrwriter.cpp
index 869c38d5..7ca4a21a 100644
--- a/src/main/cpp/systemerrwriter.cpp
+++ b/src/main/cpp/systemerrwriter.cpp
@@ -15,14 +15,9 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
 #include <log4cxx/helpers/systemerrwriter.h>
-#include <log4cxx/helpers/transcoder.h>
 #include <stdio.h>
-#if !defined(LOG4CXX)
-       #define LOG4CXX 1
-#endif
-#include <log4cxx/private/log4cxx_private.h>
+#include <log4cxx/private/consolewriter_priv.h>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::helpers;
@@ -48,49 +43,17 @@ void SystemErrWriter::flush( 
LOG4CXX_FLUSH_WRITER_FORMAL_PARAMETERS )
 
 void SystemErrWriter::write( LOG4CXX_WRITE_WRITER_FORMAL_PARAMETERS )
 {
-#if LOG4CXX_WCHAR_T_API
-       if (isWide())
-       {
-               LOG4CXX_ENCODE_WCHAR(msg, str);
-               fputws(msg.c_str(), stderr);
-               return;
-       }
-
-#endif
-       LOG4CXX_ENCODE_CHAR(msg, str);
-       fputs(msg.c_str(), stderr);
-}
-
-bool SystemErrWriter::isWide()
-{
-#if LOG4CXX_FORCE_WIDE_CONSOLE
-       return true;
-#elif LOG4CXX_FORCE_BYTE_CONSOLE || !LOG4CXX_HAS_FWIDE
-       return false;
-#else
-       return fwide(stderr, 0) > 0;
-#endif
+       helpers::writeToConsole(str, stderr);
 }
 
 #if LOG4CXX_ABI_VERSION <= 15
 void SystemErrWriter::write(const LogString& str)
 {
-#if LOG4CXX_WCHAR_T_API
-
-       if (isWide())
-       {
-               LOG4CXX_ENCODE_WCHAR(msg, str);
-               fputws(msg.c_str(), stderr);
-               return;
-       }
-
-#endif
-       LOG4CXX_ENCODE_CHAR(msg, str);
-       fputs(msg.c_str(), stderr);
+       helpers::writeToConsole(str, stderr);
 }
 
 void SystemErrWriter::flush()
 {
        fflush(stderr);
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/main/cpp/systemoutwriter.cpp b/src/main/cpp/systemoutwriter.cpp
index 343ea649..dea990b5 100644
--- a/src/main/cpp/systemoutwriter.cpp
+++ b/src/main/cpp/systemoutwriter.cpp
@@ -15,14 +15,8 @@
  * limitations under the License.
  */
 
-#include <log4cxx/logstring.h>
 #include <log4cxx/helpers/systemoutwriter.h>
-#include <log4cxx/helpers/transcoder.h>
-#include <stdio.h>
-#if !defined(LOG4CXX)
-       #define LOG4CXX 1
-#endif
-#include <log4cxx/private/log4cxx_private.h>
+#include <log4cxx/private/consolewriter_priv.h>
 
 using namespace LOG4CXX_NS;
 using namespace LOG4CXX_NS::helpers;
@@ -48,49 +42,17 @@ void SystemOutWriter::flush( 
LOG4CXX_FLUSH_WRITER_FORMAL_PARAMETERS )
 
 void SystemOutWriter::write( LOG4CXX_WRITE_WRITER_FORMAL_PARAMETERS )
 {
-#if LOG4CXX_WCHAR_T_API
-       if (isWide())
-       {
-               LOG4CXX_ENCODE_WCHAR(msg, str);
-               fputws(msg.c_str(), stdout);
-               return;
-       }
-
-#endif
-       LOG4CXX_ENCODE_CHAR(msg, str);
-       fputs(msg.c_str(), stdout);
-}
-
-bool SystemOutWriter::isWide()
-{
-#if LOG4CXX_FORCE_WIDE_CONSOLE
-       return true;
-#elif LOG4CXX_FORCE_BYTE_CONSOLE || !LOG4CXX_HAS_FWIDE
-       return false;
-#else
-       return fwide(stdout, 0) > 0;
-#endif
+       helpers::writeToConsole(str, stdout);
 }
 
 #if LOG4CXX_ABI_VERSION <= 15
 void SystemOutWriter::write(const LogString& str)
 {
-#if LOG4CXX_WCHAR_T_API
-
-       if (isWide())
-       {
-               LOG4CXX_ENCODE_WCHAR(msg, str);
-               fputws(msg.c_str(), stdout);
-               return;
-       }
-
-#endif
-       LOG4CXX_ENCODE_CHAR(msg, str);
-       fputs(msg.c_str(), stdout);
+       helpers::writeToConsole(str, stdout);
 }
 
 void SystemOutWriter::flush()
 {
        fflush(stdout);
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/main/include/log4cxx/helpers/systemerrwriter.h 
b/src/main/include/log4cxx/helpers/systemerrwriter.h
index 33c702a9..1107ecb3 100644
--- a/src/main/include/log4cxx/helpers/systemerrwriter.h
+++ b/src/main/include/log4cxx/helpers/systemerrwriter.h
@@ -54,7 +54,6 @@ class LOG4CXX_EXPORT SystemErrWriter : public Writer
        private:
                SystemErrWriter(const SystemErrWriter&);
                SystemErrWriter& operator=(const SystemErrWriter&);
-               static bool isWide();
 };
 
 } // namespace helpers
diff --git a/src/main/include/log4cxx/helpers/systemoutwriter.h 
b/src/main/include/log4cxx/helpers/systemoutwriter.h
index 00b81375..6e4f9e06 100644
--- a/src/main/include/log4cxx/helpers/systemoutwriter.h
+++ b/src/main/include/log4cxx/helpers/systemoutwriter.h
@@ -54,7 +54,6 @@ class LOG4CXX_EXPORT SystemOutWriter : public Writer
        private:
                SystemOutWriter(const SystemOutWriter&);
                SystemOutWriter& operator=(const SystemOutWriter&);
-               static bool isWide();
 };
 
 } // namespace helpers
diff --git a/src/main/include/log4cxx/private/consolewriter_priv.h 
b/src/main/include/log4cxx/private/consolewriter_priv.h
new file mode 100644
index 00000000..89ca1244
--- /dev/null
+++ b/src/main/include/log4cxx/private/consolewriter_priv.h
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef LOG4CXX_CONSOLEWRITER_PRIVATE_H
+#define LOG4CXX_CONSOLEWRITER_PRIVATE_H
+
+#include <log4cxx/logstring.h>
+#include <stdio.h>
+
+namespace LOG4CXX_NS
+{
+namespace helpers
+{
+
+LOG4CXX_EXPORT size_t writeToConsole(const LogString& str, FILE *file);
+
+} // namespace helpers
+
+} // namespace log4cxx
+
+#endif /* LOG4CXX_CONSOLEWRITER_PRIVATE_H */
diff --git a/src/test/cpp/helpers/CMakeLists.txt 
b/src/test/cpp/helpers/CMakeLists.txt
index fe07d973..97ce7454 100644
--- a/src/test/cpp/helpers/CMakeLists.txt
+++ b/src/test/cpp/helpers/CMakeLists.txt
@@ -15,12 +15,13 @@
 # limitations under the License.
 #
 
-set(HELPER_TESTS 
+set(HELPER_TESTS
     absolutetimedateformattestcase
     cacheddateformattestcase
     casttestcase
     charsetdecodertestcase
     charsetencodertestcase
+    consolewritertestcase
     cyclicbuffertestcase
     datetimedateformattestcase
     filewatchdogtest
diff --git a/src/test/cpp/helpers/consolewritertestcase.cpp 
b/src/test/cpp/helpers/consolewritertestcase.cpp
new file mode 100644
index 00000000..e44e23ff
--- /dev/null
+++ b/src/test/cpp/helpers/consolewritertestcase.cpp
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../logunit.h"
+
+#include <log4cxx/private/consolewriter_priv.h>
+#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/transcoder.h>
+#include <apr_file_io.h>
+
+using namespace log4cxx;
+using namespace log4cxx::helpers;
+
+
+LOGUNIT_CLASS(ConsoleWriterTest)
+{
+       LOGUNIT_TEST_SUITE(ConsoleWriterTest);
+       LOGUNIT_TEST(testWriteEmbeddedNullCharacters);
+       LOGUNIT_TEST_SUITE_END();
+
+public:
+       /**
+        * Tests writing to an unknown host.
+        */
+       void testWriteEmbeddedNullCharacters()
+       {
+               Pool p;
+               const char* tmpdir = NULL;
+               apr_status_t stat = apr_temp_dir_get(&tmpdir, p.getAPRPool());
+               LOGUNIT_ASSERT_EQUAL(APR_SUCCESS, stat);
+
+               std::string path = tmpdir;
+               path += "/log4cxx.test.log";
+
+               remove(path.c_str());
+
+               std::string message{"Hello\0World!", 12};
+
+               {
+                       FILE *fp = fopen(path.c_str(), "wb");
+                       LOG4CXX_DECODE_CHAR(lsMessage, message);
+                       size_t written = helpers::writeToConsole(lsMessage, fp);
+                       LOGUNIT_ASSERT_EQUAL(message.size() + 1, written);
+                       fclose(fp);
+               }
+
+               std::string content;
+
+               {
+                       FILE *fp = fopen(path.c_str(), "rb");
+                       content.resize(1024);
+                       size_t count = fread((void*)content.data(), 1, 
content.size(), fp);
+                       content.resize(count);
+                       fclose(fp);
+               }
+
+               LOGUNIT_ASSERT_EQUAL(content, message + "\n");
+       }
+
+};
+
+
+LOGUNIT_TEST_SUITE_REGISTRATION(ConsoleWriterTest);

Reply via email to