From dc47d251f5993f5cb9b43f6825480d6c52aa1123 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Tue, 10 Oct 2023 10:53:41 -0700 Subject: [PATCH] Fix richio vprint stdlib checks do not allow dereferencing the first element of a vector when there are no elements in the vector (regardless of whether we have allocated memory for them). This whole function is rather over-engineered, setting up multiple allocations and branches depending of the string size. This commit reduces the function to the actions needed (get the string size, print it into the output) --- common/richio.cpp | 32 +++----------- qa/tests/common/CMakeLists.txt | 1 + qa/tests/common/test_richio.cpp | 77 +++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 qa/tests/common/test_richio.cpp diff --git a/common/richio.cpp b/common/richio.cpp index a82444b0d8..3768ce3276 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -48,35 +48,17 @@ static int vprint( std::string* result, const char* format, va_list ap ) { - char msg[512]; - // This function can call vsnprintf twice. - // But internally, vsnprintf retrieves arguments from the va_list identified by arg as if - // va_arg was used on it, and thus the state of the va_list is likely to be altered by the call. - // see: www.cplusplus.com/reference/cstdio/vsnprintf - // we make a copy of va_list ap for the second call, if happens va_list tmp; va_copy( tmp, ap ); + size_t len = vsnprintf( nullptr, 0, format, tmp ); + va_end( tmp ); - size_t len = vsnprintf( msg, sizeof(msg), format, ap ); + // Resize the output to hold the required data + size_t size = result->size(); + result->resize( size + len ); - if( len < sizeof(msg) ) // the output fit into msg - { - result->append( msg, msg + len ); - } - else - { - // output was too big, so now incur the expense of allocating - // a buf for holding suffient characters. - - std::vector buf; - buf.reserve( len+1 ); // reserve(), not resize() which writes. +1 for trailing nul. - - len = vsnprintf( &buf[0], len+1, format, tmp ); - - result->append( &buf[0], &buf[0] + len ); - } - - va_end( tmp ); // Release the temporary va_list, initialised from ap + // Now do the actual printing + len = vsnprintf( result->data() + size, len + 1, format, ap ); return len; } diff --git a/qa/tests/common/CMakeLists.txt b/qa/tests/common/CMakeLists.txt index cadafbd0ed..438106a7a2 100644 --- a/qa/tests/common/CMakeLists.txt +++ b/qa/tests/common/CMakeLists.txt @@ -43,6 +43,7 @@ set( QA_COMMON_SRCS test_kiid.cpp test_property.cpp test_refdes_utils.cpp + test_richio.cpp test_text_attributes.cpp test_title_block.cpp test_types.cpp diff --git a/qa/tests/common/test_richio.cpp b/qa/tests/common/test_richio.cpp new file mode 100644 index 0000000000..b8b1cd9e22 --- /dev/null +++ b/qa/tests/common/test_richio.cpp @@ -0,0 +1,77 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file + * Test suite for general string functions + */ + +#include + +// Code under test +#include + +/** + * Declare the test suite + */ +BOOST_AUTO_TEST_SUITE( RichIO ) + +/** + * Test the #vprint method. + */ +BOOST_AUTO_TEST_CASE( VPrint ) +{ + std::string output; + + // Test 1: Basic strings and numbers + StrPrintf( &output, "Hello %s! ", "World" ); + StrPrintf( &output, "Number: %d, ", 42 ); + StrPrintf( &output, "Float: %.2f, ", 3.14 ); + StrPrintf( &output, "Char: %c. ", 'A' ); + BOOST_CHECK_EQUAL( output, std::string( "Hello World! Number: 42, Float: 3.14, Char: A. " ) ); + output.clear(); + + // Test 2: Large string + std::string longString( 500, 'A' ); + StrPrintf( &output, "%s", longString.c_str() ); + BOOST_CHECK_EQUAL( output, longString ); + output.clear(); + + // Test 3: Edge case with zero characters + StrPrintf( &output, "" ); + BOOST_ASSERT( output.empty() ); + + // Test 4: Mixing small and large strings + StrPrintf( &output, "Small, " ); + StrPrintf( &output, "%s, ", longString.c_str() ); + StrPrintf( &output, "End." ); + BOOST_CHECK_EQUAL( output, std::string( "Small, " + longString + ", End." ) ); + output.clear(); + + // Test 5: Formatting with various data types + StrPrintf( &output, "%d %s %c %.2f", 123, "Hello", 'X', 9.876 ); + BOOST_CHECK_EQUAL( output, std::string( "123 Hello X 9.88" ) ); + output.clear(); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file