Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add entry message wide string support #412

Merged
merged 3 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion include/stumpless/entry.h
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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.
*
Expand Down
16 changes: 7 additions & 9 deletions src/config/have_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,23 @@ 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,
0,
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;
Expand All @@ -120,19 +119,18 @@ 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,
needed_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;
Expand Down
4 changes: 3 additions & 1 deletion src/config/no_wcsrtombs_s.c
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -18,6 +18,7 @@

#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <wchar.h>
#include "private/config/locale/wrapper.h"
#include "private/config/no_wcsrtombs_s.h"
Expand All @@ -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 );
Expand Down
34 changes: 34 additions & 0 deletions src/entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am going through the commits. Could you tell me about this line ?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wide string conversion functions return a size that includes the NULL terminator, but the entry message_length member shouldn't include the NULL terminator in it. Stumpless currently assumes that this string is NULL-terminated though, and specifically the stumpless_get_entry_message function does when copying out the message to return. If the NULL terminator is left in on this length, the get entry function will try to read an extra byte at the end because it expects a NULL terminator to be there, which is invalid (and causes valgrind errors).

It's a confusing convention, and the next major version (v3.0) will drop the NULL terminators completely for this reason, but for now this line is necessary to keep everything consistent.


} 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,
Expand Down
1 change: 1 addition & 0 deletions src/windows/stumpless.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
130 changes: 127 additions & 3 deletions test/function/entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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 );

Expand Down
3 changes: 3 additions & 0 deletions tools/check_headers/c_standard_library.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@
"va_end": "stdarg.h"
"va_list": "stdarg.h"
"va_start": "stdarg.h"
"wchar_t":
- "stddef.h"
- "wchar.h"
1 change: 0 additions & 1 deletion tools/check_headers/standard_library.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions tools/check_headers/stumpless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading