diff --git a/include/stumpless/entry.h b/include/stumpless/entry.h index f2c89a76c..9812c16fa 100644 --- a/include/stumpless/entry.h +++ b/include/stumpless/entry.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 */ /* - * Copyright 2018-2023 Joel E. Anderson + * Copyright 2018-2024 Joel E. Anderson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1309,6 +1309,39 @@ struct stumpless_entry * stumpless_set_entry_message_str( struct stumpless_entry *entry, const char *message ); +/** + * Sets the message of a given entry. + * + * **Thread Safety: MT-Safe** + * This function is thread safe. A mutex is used to coordinate changes to the + * entry while it is being modified. + * + * **Async Signal Safety: AS-Unsafe lock heap** + * This function is not safe to call from signal handlers due to the use of a + * non-reentrant lock to coordinate changes and the use of memory management + * functions to create the new message and free the old one. + * + * **Async Cancel Safety: AC-Unsafe lock heap** + * This function is not safe to call from threads that may be asynchronously + * cancelled, due to the use of a lock that could be left locked as well as + * memory management functions. + * + * @since release v2.2.0 + * + * @param entry The entry to modify. + * + * @param message The new message to set on the entry. If this is NULL, then it + * will be blank in the entry (no characters). This must be a valid UTF-16 string + * in shortest form. + * + * @return The modified entry if no error is encountered. If an error is + * encountered, then NULL is returned and an error code is set appropriately. + */ +STUMPLESS_PUBLIC_FUNCTION +struct stumpless_entry * +stumpless_set_entry_message_str_w( struct stumpless_entry *entry, + const wchar_t *message ); + /** * Sets the msgid for an entry. * diff --git a/src/config/have_windows.c b/src/config/have_windows.c index 1dbe5e51f..5e5dfbd9f 100644 --- a/src/config/have_windows.c +++ b/src/config/have_windows.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 /* - * Copyright 2018-2022 Joel E. Anderson + * Copyright 2018-2024 Joel E. Anderson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,14 +91,13 @@ windows_copy_cstring_to_lpwstr( LPCSTR str, int *copy_length ) { } char * -windows_copy_wstring_to_cstring( const wchar_t *str, int *copy_size ) { +windows_copy_wstring_to_cstring( const wchar_t *str, int *copy_size ){ int needed_size; LPSTR str_copy; int conversion_result; needed_size = WideCharToMultiByte( CP_UTF8, - WC_ERR_INVALID_CHARS | - WC_NO_BEST_FIT_CHARS, + WC_ERR_INVALID_CHARS, str, -1, NULL, @@ -106,9 +105,9 @@ windows_copy_wstring_to_cstring( const wchar_t *str, int *copy_size ) { NULL, NULL ); - if( needed_size == 0 ) { + if( needed_size == 0 ){ raise_wide_conversion_failure( - GetLastError( ), + GetLastError(), L10N_WINDOWS_WIDE_TO_MB_CONVERSION_ERROR_CODE_TYPE ); return NULL; @@ -120,8 +119,7 @@ windows_copy_wstring_to_cstring( const wchar_t *str, int *copy_size ) { } conversion_result = WideCharToMultiByte( CP_UTF8, - WC_ERR_INVALID_CHARS | - WC_NO_BEST_FIT_CHARS, + WC_ERR_INVALID_CHARS, str, -1, str_copy, @@ -129,10 +127,10 @@ windows_copy_wstring_to_cstring( const wchar_t *str, int *copy_size ) { NULL, NULL ); - if( conversion_result == 0 ) { + if( conversion_result == 0 ){ free_mem( str_copy ); raise_wide_conversion_failure( - GetLastError( ), + GetLastError(), L10N_WINDOWS_WIDE_TO_MB_CONVERSION_ERROR_CODE_TYPE ); return NULL; diff --git a/src/config/no_wcsrtombs_s.c b/src/config/no_wcsrtombs_s.c index 79b394ed2..642f71ae3 100644 --- a/src/config/no_wcsrtombs_s.c +++ b/src/config/no_wcsrtombs_s.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 /* - * Copyright 2022 Joel E. Anderson + * Copyright 2022-2024 Joel E. Anderson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ #include #include +#include #include #include "private/config/locale/wrapper.h" #include "private/config/no_wcsrtombs_s.h" @@ -32,6 +33,7 @@ no_wcsrtombs_s_copy_wstring_to_cstring( const wchar_t *str, int *copy_size ) { mbstate_t state; size_t conversion_result; + memset( &state, 0, sizeof( state ) ); conversion_result = wcsrtombs( NULL, &str, 0, &state ); if( conversion_result == -1 ) { raise_wide_conversion_failure( errno, L10N_ERRNO_ERROR_CODE_TYPE ); diff --git a/src/entry.c b/src/entry.c index b2d5b76ee..2f8dcfc94 100644 --- a/src/entry.c +++ b/src/entry.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 /* - * Copyright 2018-2022 Joel E. Anderson + * Copyright 2018-2024 Joel E. Anderson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ #include "private/config/wrapper/getpid.h" #include "private/config/wrapper/thread_safety.h" #include "private/config/wrapper/wel.h" +#include "private/config/wrapper/wstring.h" #include "private/deprecate.h" #include "private/element.h" #include "private/entry.h" @@ -872,6 +873,39 @@ stumpless_set_entry_message_str( struct stumpless_entry *entry, return entry; } +struct stumpless_entry * +stumpless_set_entry_message_str_w( struct stumpless_entry *entry, + const wchar_t *message ){ + char *new_message; + int new_message_size; + const char *old_message; + + VALIDATE_ARG_NOT_NULL( entry ); + + if( message ){ + new_message = config_copy_wstring_to_cstring( message, &new_message_size ); + if( !new_message ){ + return NULL; + } + new_message_size--; // leave off the NULL character + + } else { + new_message = NULL; + new_message_size = 0; + } + + lock_entry( entry ); + old_message = entry->message; + entry->message = new_message; + entry->message_length = new_message_size; + unlock_entry( entry ); + + free_mem( old_message ); + clear_error(); + + return entry; +} + struct stumpless_entry * stumpless_set_entry_param_by_index( struct stumpless_entry *entry, size_t element_index, diff --git a/src/windows/stumpless.def b/src/windows/stumpless.def index bbfebecdf..be4e64079 100644 --- a/src/windows/stumpless.def +++ b/src/windows/stumpless.def @@ -243,3 +243,4 @@ EXPORTS stumpless_close_chain_only @226 stumpless_get_chain_length @227 stumpless_new_chain @228 + stumpless_set_entry_message_str_w @229 diff --git a/test/function/entry.cpp b/test/function/entry.cpp index 9692038d0..03d7d3820 100644 --- a/test/function/entry.cpp +++ b/test/function/entry.cpp @@ -2318,6 +2318,129 @@ namespace { stumpless_free_all( ); } + TEST( SetMessageWideStrTest, AsciiMessage ) { + struct stumpless_entry *entry; + const wchar_t *wide_ascii_message = L"ASCII Message"; + const char *ascii_message = "ASCII Message"; + const struct stumpless_entry *result; + const char *new_message; + + entry = create_empty_entry(); + ASSERT_NOT_NULL( entry ); + + result = stumpless_set_entry_message_str_w( entry, wide_ascii_message ); + EXPECT_EQ( entry, result ); + EXPECT_NO_ERROR; + + new_message = stumpless_get_entry_message( entry ); + EXPECT_NOT_NULL( new_message ); + EXPECT_NO_ERROR; + EXPECT_STREQ( ascii_message, new_message ); + + free( ( void * ) new_message ); + stumpless_destroy_entry_and_contents( entry ); + stumpless_free_all(); + } + + TEST( SetMessageWideStrTest, LongAsciiMessage ) { + struct stumpless_entry *entry; + const char *long_message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.Proin porta, sem quis imperdiet aliquam, lectus odio consequat elit, at ullamcorper felis urna vel leo. Ut vitae risus cursus, tempor risus sed, elementum risus. Vestibulum porta egestas libero et lacinia."; + const wchar_t *wide_long_message = L"Lorem ipsum dolor sit amet, consectetur adipiscing elit.Proin porta, sem quis imperdiet aliquam, lectus odio consequat elit, at ullamcorper felis urna vel leo. Ut vitae risus cursus, tempor risus sed, elementum risus. Vestibulum porta egestas libero et lacinia."; + const struct stumpless_entry *result; + const char *new_message; + + entry = create_empty_entry(); + ASSERT_NOT_NULL( entry ); + + result = stumpless_set_entry_message_str_w( entry, wide_long_message ); + EXPECT_EQ( entry, result ); + EXPECT_NO_ERROR; + + new_message = stumpless_get_entry_message( entry ); + EXPECT_NOT_NULL( new_message ); + EXPECT_NO_ERROR; + EXPECT_STREQ( long_message, new_message ); + free( ( void * ) new_message ); + stumpless_destroy_entry_and_contents( entry ); + stumpless_free_all(); + } + + TEST( SetMessageWideStrTest, MallocFailureOnMessage ) { + void * (*set_malloc_result)(size_t); + struct stumpless_entry *entry; + const wchar_t *new_message = L"nice and long to make sure it beats the first"; + const struct stumpless_entry *result; + + entry = create_empty_entry( ); + ASSERT_NOT_NULL( entry ); + + set_malloc_result = stumpless_set_malloc( MALLOC_FAIL_ON_SIZE( 46 ) ); + ASSERT_NOT_NULL( set_malloc_result ); + + result = stumpless_set_entry_message_str_w( entry, new_message ); + EXPECT_ERROR_ID_EQ( STUMPLESS_MEMORY_ALLOCATION_FAILURE ); + EXPECT_NULL( result ); + + set_malloc_result = stumpless_set_malloc( malloc ); + EXPECT_TRUE( set_malloc_result == malloc ); + + stumpless_destroy_entry_and_contents( entry ); + stumpless_free_all(); + } + + TEST( SetMessageWideStrTest, NullEntry ) { + const struct stumpless_entry *result; + + result = stumpless_set_entry_message_str_w( NULL, L"test-message" ); + EXPECT_ERROR_ID_EQ( STUMPLESS_ARGUMENT_EMPTY ); + EXPECT_NULL( result ); + + stumpless_free_all(); + } + + TEST( SetMessageWideStrTest, NullMessage ) { + struct stumpless_entry *entry; + const struct stumpless_entry *result; + + entry = create_empty_entry(); + EXPECT_NO_ERROR; + EXPECT_NOT_NULL( entry ); + + result = stumpless_set_entry_message_str_w( entry, NULL ); + EXPECT_NO_ERROR; + EXPECT_EQ( entry, result ); + + EXPECT_NULL( entry->message ); + EXPECT_EQ( 0, entry->message_length ); + + stumpless_destroy_entry_and_contents( entry ); + stumpless_free_all(); + } + + TEST( SetMessageWideStrTest, WideMessage ) { + struct stumpless_entry *entry; + const struct stumpless_entry *result; + const wchar_t *wide_message= L"message"; + const char *utf8_message = "message"; + const char *new_message; + + entry = create_empty_entry(); + ASSERT_NOT_NULL( entry ); + + result = stumpless_set_entry_message_str_w( entry, wide_message ); + EXPECT_EQ( entry, result ); + EXPECT_NO_ERROR; + + new_message = stumpless_get_entry_message( entry ); + EXPECT_NOT_NULL( new_message ); + EXPECT_NO_ERROR; + EXPECT_STREQ( new_message, utf8_message ); + + free( ( void * ) new_message ); + stumpless_destroy_entry_and_contents( entry ); + stumpless_free_all(); + } + TEST( SetParam, NullEntry ) { struct stumpless_param *param; const struct stumpless_entry *result; @@ -2387,6 +2510,7 @@ namespace { const char *app_name = "test-app-name"; const char *msgid = "test-msgid"; const char *message = "test-message"; + const char *param_name = "very-long-name-abcdefghijklmnopqrstuvwxyz"; entry = stumpless_new_entry( STUMPLESS_FACILITY_USER, STUMPLESS_SEVERITY_INFO, @@ -2395,9 +2519,9 @@ namespace { message ); result = stumpless_add_new_param_to_entry( entry, - "new-element-name", - "very-long-name-abcdefghijklmnopqrstuvwxyz", - "test-value" ); + "new-element-name", + param_name, + "test-value" ); EXPECT_NULL( result ); EXPECT_ERROR_ID_EQ( STUMPLESS_ARGUMENT_TOO_BIG ); diff --git a/tools/check_headers/c_standard_library.yml b/tools/check_headers/c_standard_library.yml index f41b3c3f1..99ecc88b2 100644 --- a/tools/check_headers/c_standard_library.yml +++ b/tools/check_headers/c_standard_library.yml @@ -17,3 +17,6 @@ "va_end": "stdarg.h" "va_list": "stdarg.h" "va_start": "stdarg.h" +"wchar_t": + - "stddef.h" + - "wchar.h" diff --git a/tools/check_headers/standard_library.yml b/tools/check_headers/standard_library.yml index ef6ec6929..a24f58ccc 100644 --- a/tools/check_headers/standard_library.yml +++ b/tools/check_headers/standard_library.yml @@ -212,7 +212,6 @@ "WC_NO_BEST_FIT_CHARS": - "windows.h" - "private/windows_wrapper.h" -"wchar_t": "stddef.h" "wcslen": "wchar.h" "wcsrtombs": "wchar.h" "wcstombs": "stdlib.h" diff --git a/tools/check_headers/stumpless.yml b/tools/check_headers/stumpless.yml index 59e23449a..1b750e5d5 100644 --- a/tools/check_headers/stumpless.yml +++ b/tools/check_headers/stumpless.yml @@ -358,6 +358,7 @@ "stumpless_set_entry_msgid": "stumpless/entry.h" "stumpless_set_entry_message": "stumpless/entry.h" "stumpless_set_entry_message_str": "stumpless/entry.h" +"stumpless_set_entry_message_str_w" : "stumpless/entry.h" "stumpless_set_entry_priority": "stumpless/entry.h" "stumpless_set_entry_prival": "stumpless/entry.h" "stumpless_set_entry_severity": "stumpless/entry.h"