gnucash future: Multiple changes pushed
John Ralls
jralls at code.gnucash.org
Sat Jul 18 16:53:10 EDT 2026
Updated via https://github.com/Gnucash/gnucash/commit/0e53aff8 (commit)
via https://github.com/Gnucash/gnucash/commit/85646ea0 (commit)
via https://github.com/Gnucash/gnucash/commit/770fd5af (commit)
from https://github.com/Gnucash/gnucash/commit/458b2ea4 (commit)
commit 0e53aff804ada073bfaae88f46cb7e737d52e345
Merge: 458b2ea4c4 85646ea094
Author: John Ralls <jralls at ceridwen.us>
Date: Sat Jul 18 13:49:37 2026 -0700
Merge Brent McBride's 'gnc-glib-utils-cpp' into future.
commit 85646ea0946fefd451bd4890f6b761990bcbb23d
Author: Brent McBride <mcbridebt at hotmail.com>
Date: Tue Jul 14 22:49:26 2026 -0700
Modernize gnc-string-utils to C++ and convert its test to Googletest
Convert the string utilities from GLib to boost::locale: safe_utf8_collate
compares with a boost::locale::collator, gnc_locale_from_utf8/to_utf8 use the
boost::locale info facet for the charset (short-circuiting UTF-8 locales), and
utf8_strstr normalizes with boost::locale::normalize.
These functions are reached from low-level engine code such as
xaccAccountOrder -- including from the Python bindings and unit tests -- that
runs before gnucash-core-app calls gnc_init_boost_locale. gnc_get_boost_locale
therefore now lazily creates a facet-equipped locale on first use, while still
letting gnc_init_boost_locale install message catalogs afterwards.
The test is converted to Googletest and pins LC_ALL=C so the conversion path
and its failure handling are exercised deterministically.
diff --git a/libgnucash/core-utils/gnc-locale-utils.cpp b/libgnucash/core-utils/gnc-locale-utils.cpp
index fe99ffaf37..9d57203cb6 100644
--- a/libgnucash/core-utils/gnc-locale-utils.cpp
+++ b/libgnucash/core-utils/gnc-locale-utils.cpp
@@ -1,6 +1,7 @@
/********************************************************************\
* gnc-locale-utils.cpp -- provide a default locale for C++ *
* Copyright (C) 2019 John Ralls <jralls at ceridwen.us *
+ * Copyright (C) 2026 Brent McBride <mcbridebt at hotmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -71,39 +72,54 @@ gnc_get_locale()
static std::locale boost_cached;
-static bool tried_boost_already = false;
+static bool boost_locale_created = false;
+static bool boost_messages_loaded = false;
+
+/* Build a boost::locale from the environment. Message catalogs are loaded
+ * only when a path is supplied; the collation, character-set and other
+ * facets are always present so that gnc_get_boost_locale() is usable even
+ * before translations have been set up. */
+static std::locale
+create_boost_locale (const std::string& messages_path)
+{
+ try
+ {
+ boost::locale::generator gen;
+ if (!messages_path.empty())
+ gen.add_messages_path(messages_path);
+ gen.add_messages_domain(PROJECT_NAME);
+ return gen ("");
+ }
+ catch (const std::runtime_error& err)
+ {
+ const char* locale = setlocale(LC_ALL, "");
+
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "Failed to create C++ default locale from "
+ "%s because %s. Using the 'C' locale for C++.",
+ locale, err.what());
+ return std::locale::classic();
+ }
+}
void
gnc_init_boost_locale (const std::string& messages_path)
{
- if (!tried_boost_already)
- {
- tried_boost_already = true;
+ /* Once message catalogs are loaded there is nothing more to do; a locale
+ * that was created lazily (without catalogs) is still replaced here. */
+ if (boost_messages_loaded)
+ return;
- try
- {
- boost::locale::generator gen;
- if (!messages_path.empty())
- gen.add_messages_path(messages_path);
- else
- g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
- "Attempt to initialize boost_locale without a message_path. "
- "If message catalogs are not installed in the system's default locations "
- "user interface strings will not be translated.");
- gen.add_messages_domain(PROJECT_NAME);
- boost_cached = gen ("");
- }
- catch (const std::runtime_error& err)
- {
- const char* locale = setlocale(LC_ALL, "");
+ if (messages_path.empty())
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
+ "Attempt to initialize boost_locale without a message_path. "
+ "If message catalogs are not installed in the system's default locations "
+ "user interface strings will not be translated.");
- g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
- "Failed to create C++ default locale from"
- "%s because %s. Using the 'C' locale for C++.",
- locale, err.what());
- boost_cached = std::locale::classic();
- }
- }
+ boost_cached = create_boost_locale (messages_path);
+ boost_locale_created = true;
+ if (!messages_path.empty())
+ boost_messages_loaded = true;
}
@@ -111,6 +127,16 @@ gnc_init_boost_locale (const std::string& messages_path)
const std::locale&
gnc_get_boost_locale()
{
+ if (!boost_locale_created)
+ {
+ /* A consumer needs boost::locale facets before the application
+ * initialized them -- e.g. the Python bindings, a unit test, or
+ * engine code such as xaccAccountOrder(). Create a usable locale
+ * now; message catalogs, which require a path, are installed later
+ * by gnc_init_boost_locale(). */
+ boost_cached = create_boost_locale ("");
+ boost_locale_created = true;
+ }
return boost_cached;
}
diff --git a/libgnucash/core-utils/gnc-string-utils.cpp b/libgnucash/core-utils/gnc-string-utils.cpp
index d287485a77..7be5850caa 100644
--- a/libgnucash/core-utils/gnc-string-utils.cpp
+++ b/libgnucash/core-utils/gnc-string-utils.cpp
@@ -1,6 +1,7 @@
/********************************************************************\
- * gnc-glib-utils.c -- utility functions based on glib functions *
+ * gnc-string-utils.cpp -- string and list utility functions *
* Copyright (C) 2006 David Hampton <hampton at employees.org> *
+ * Copyright (C) 2026 Brent McBride <mcbridebt at hotmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -22,12 +23,16 @@
\********************************************************************/
#include <config.h>
-#include <errno.h>
-#include <stdio.h>
-#include <signal.h>
-#include <string.h>
-#include <stdbool.h>
+#include <cstring>
+#include <string_view>
+
+#include <boost/locale/collator.hpp>
+#include <boost/locale/conversion.hpp>
+#include <boost/locale/encoding.hpp>
+#include <boost/locale/info.hpp>
+
+#include "gnc-locale-utils.hpp"
#include "gnc-string-utils.h"
#ifdef G_OS_WIN32
@@ -38,12 +43,16 @@ int
safe_utf8_collate (const char * da, const char * db)
{
if (da && !(*da))
- da = NULL;
+ da = nullptr;
if (db && !(*db))
- db = NULL;
+ db = nullptr;
if (da && db)
- return g_utf8_collate(da, db);
+ {
+ auto const& coll{std::use_facet<boost::locale::collator<char>>(
+ gnc_get_boost_locale())};
+ return coll.compare(boost::locale::collate_level::quaternary, da, db);
+ }
if (da)
return 1;
if (db)
@@ -126,20 +135,18 @@ gnc_utf8_validate(const gchar *str,
const gchar **end)
{
- const gchar *p;
-
- g_return_val_if_fail (str != NULL, FALSE);
+ g_return_val_if_fail (str != nullptr, false);
if (end)
*end = str;
- p = str;
+ const gchar *p = str;
while ((max_len < 0 || (p - str) < max_len) && *p)
{
int i, mask = 0, len;
gunichar result;
- unsigned char c = (unsigned char) * p;
+ unsigned char c = static_cast<unsigned char>(*p);
UTF8_COMPUTE (c, mask, len);
@@ -156,7 +163,7 @@ gnc_utf8_validate(const gchar *str,
if (UTF8_LENGTH (result) != len) /* Check for overlong UTF-8 */
break;
- if (result == (gunichar) - 1)
+ if (result == static_cast<gunichar>(-1))
break;
if (!UNICODE_VALID (result))
@@ -173,32 +180,31 @@ gnc_utf8_validate(const gchar *str,
*/
if (max_len >= 0 &&
p != (str + max_len))
- return FALSE;
+ return false;
else if (max_len < 0 &&
*p != '\0')
- return FALSE;
+ return false;
else
- return TRUE;
+ return true;
}
void
gnc_utf8_strip_invalid (gchar *str)
{
gchar *end;
- gint len;
g_return_if_fail(str);
- if (gnc_utf8_validate(str, -1, (const gchar **)&end))
+ if (gnc_utf8_validate(str, -1, const_cast<const gchar **>(&end)))
return;
g_warning("Invalid utf8 string: %s", str);
do
{
- len = strlen(end);
+ int len = strlen(end);
memmove(end, end + 1, len); /* shuffle the remainder one byte */
}
- while (!gnc_utf8_validate(str, -1, (const gchar **)&end));
+ while (!gnc_utf8_validate(str, -1, const_cast<const gchar **>(&end)));
}
gchar *
@@ -212,14 +218,13 @@ gnc_utf8_strip_invalid_strdup(const gchar* str)
void
gnc_utf8_strip_invalid_and_controls (gchar *str)
{
- gchar *c = NULL;
- const gchar *controls = "\b\f\n\r\t\v";
- g_return_if_fail (str != NULL && strlen (str) > 0);
+ const char *controls = "\b\f\n\r\t\v";
+ g_return_if_fail (str != nullptr && strlen (str) > 0);
gnc_utf8_strip_invalid (str); /* First fix the UTF-8 */
- for(c = str + strlen (str) - 1; c != str; --c)
+ for (gchar *c = str + strlen (str) - 1; c != str; --c)
{
- gboolean line_control = ((unsigned char)(*c) < 0x20);
- if (line_control || strchr(controls, *c) != NULL)
+ bool line_control = (static_cast<unsigned char>(*c) < 0x20);
+ if (line_control || strchr(controls, *c) != nullptr)
*c = ' '; /*replace controls with a single space. */
}
}
@@ -227,44 +232,54 @@ gnc_utf8_strip_invalid_and_controls (gchar *str)
gchar *
gnc_locale_from_utf8(const gchar* str)
{
- gchar * locale_str;
- gsize bytes_written = 0;
- GError * err = NULL;
-
- /* Convert from UTF-8 to the encoding used in the current locale. */
- locale_str = g_locale_from_utf8(str, -1, NULL, &bytes_written, &err);
- if (err)
+ g_return_val_if_fail (str != nullptr, nullptr);
+
+ // Convert from UTF-8 to the encoding used in the current locale.
+ auto const& info{std::use_facet<boost::locale::info> (
+ gnc_get_boost_locale ())};
+ if (info.utf8 ())
+ return g_strdup (str);
+ try
{
- g_warning("g_locale_from_utf8 failed: %s", err->message);
- g_error_free(err);
+ auto locale_str = boost::locale::conv::from_utf<char> (
+ str, info.encoding (), boost::locale::conv::stop);
+ return g_strdup (locale_str.c_str ());
+ }
+ catch (const std::exception& err)
+ {
+ g_warning ("gnc_locale_from_utf8 failed: %s", err.what ());
+ return nullptr;
}
-
- return locale_str;
}
gchar *
gnc_locale_to_utf8(const gchar* str)
{
- gchar * utf8_str;
- gsize bytes_written = 0;
- GError * err = NULL;
-
- /* Convert to UTF-8 from the encoding used in the current locale. */
- utf8_str = g_locale_to_utf8(str, -1, NULL, &bytes_written, &err);
- if (err)
+ g_return_val_if_fail (str != nullptr, nullptr);
+
+ // Convert to UTF-8 from the encoding used in the current locale.
+ auto const& info{std::use_facet<boost::locale::info> (
+ gnc_get_boost_locale ())};
+ if (info.utf8 ())
+ return g_strdup (str);
+ try
{
- g_warning("g_locale_to_utf8 failed: %s", err->message);
- g_error_free(err);
+ auto utf8_str = boost::locale::conv::to_utf<char> (
+ str, info.encoding (), boost::locale::conv::stop);
+ return g_strdup (utf8_str.c_str ());
+ }
+ catch (const std::exception& err)
+ {
+ g_warning ("gnc_locale_to_utf8 failed: %s", err.what ());
+ return nullptr;
}
-
- return utf8_str;
}
GList*
gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data)
{
- GList *rtn = NULL;
- for (; list != NULL; list = list->next)
+ GList *rtn = nullptr;
+ for (; list != nullptr; list = list->next)
{
rtn = g_list_prepend (rtn, (*fn)(list->data, user_data));
}
@@ -274,58 +289,54 @@ gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data)
void
gnc_g_list_cut(GList **list, GList *cut_point)
{
- if (list == NULL || *list == NULL)
+ if (list == nullptr || *list == nullptr)
return;
// if it's the first element.
- if (cut_point->prev == NULL)
+ if (cut_point->prev == nullptr)
{
- *list = NULL;
+ *list = nullptr;
return;
}
- cut_point->prev->next = NULL;
- cut_point->prev = NULL;
+ cut_point->prev->next = nullptr;
+ cut_point->prev = nullptr;
}
static bool
utf8_strstr(char **needle, char *haystack)
{
- char *tmp = g_utf8_normalize (*needle, -1, G_NORMALIZE_NFC);
- if (haystack && *haystack)
- {
- char *place = strstr(haystack, tmp);
- if (place)
- {
- g_free (tmp);
- return false;
- }
- }
- *needle = tmp; //so that haystack is already normalized
+ auto tmp{boost::locale::normalize (*needle, boost::locale::norm_nfc,
+ gnc_get_boost_locale ())};
+ if (haystack && *haystack &&
+ std::string_view{haystack}.find (tmp) != std::string_view::npos)
+ return false;
+
+ *needle = g_strdup (tmp.c_str ()); //so that haystack is already normalized
return true;
}
static gchar *
-gnc_g_list_stringjoin_internal (GList *list_of_strings, const gchar *sep, bool testdups)
+stringjoin_internal (GList *list_of_strings, const gchar *sep, bool testdups)
{
- gint seplen = sep ? strlen(sep) : 0;
+ gint seplen = sep ? strlen (sep) : 0;
gint length = -seplen;
gchar *retval, *p;
for (GList *n = list_of_strings; n; n = n->next)
{
- gchar *str = n->data;
+ gchar *str = static_cast<gchar*>(n->data);
if (str && *str)
length += strlen (str) + seplen;
}
if (length <= 0)
- return NULL;
+ return nullptr;
- p = retval = (gchar*) g_malloc0 (length * sizeof (gchar) + 1);
+ p = retval = static_cast<gchar*>(g_malloc0 (length * sizeof (gchar) + 1));
for (GList *n = list_of_strings; n; n = n->next)
{
- gchar *str = n->data;
+ gchar *str = static_cast<gchar*>(n->data);
if (!str || !str[0])
continue;
if (!testdups || utf8_strstr (&str, retval))
@@ -344,19 +355,20 @@ gnc_g_list_stringjoin_internal (GList *list_of_strings, const gchar *sep, bool t
gchar *
gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep)
{
- return gnc_g_list_stringjoin_internal (list_of_strings, sep, false);
+ return stringjoin_internal (list_of_strings, sep, false);
}
gchar *
gnc_g_list_stringjoin_nodups (GList *list_of_strings, const gchar *sep)
{
- return gnc_g_list_stringjoin_internal (list_of_strings, sep, true);
+ return stringjoin_internal (list_of_strings, sep, true);
}
gint
gnc_list_length_cmp (const GList *list, size_t len)
{
- for (GList *lst = (GList*) list;; lst = g_list_next (lst), len--)
+ for (GList *lst = const_cast<GList*>(list);;
+ lst = g_list_next (lst), len--)
{
if (!lst) return (len ? -1 : 0);
if (!len) return 1;
diff --git a/libgnucash/core-utils/gnc-string-utils.h b/libgnucash/core-utils/gnc-string-utils.h
index b0281bdbaf..6b9cc2c323 100644
--- a/libgnucash/core-utils/gnc-string-utils.h
+++ b/libgnucash/core-utils/gnc-string-utils.h
@@ -24,10 +24,9 @@
/** @addtogroup GLib
@{ */
-/** @addtogroup Helpers GLib Helpers
+/** @addtogroup Helpers String Helpers
- The API in this file is designed to provide support functions that
- wrap the base glib functions and make them easier to use.
+ The API in this file provides string and list utility functions.
@{ */
/** @file gnc-string-utils.h
diff --git a/libgnucash/core-utils/test/CMakeLists.txt b/libgnucash/core-utils/test/CMakeLists.txt
index 0416017fba..b5c92ffdf2 100644
--- a/libgnucash/core-utils/test/CMakeLists.txt
+++ b/libgnucash/core-utils/test/CMakeLists.txt
@@ -15,7 +15,15 @@ macro(add_core_utils_test _TARGET _SOURCE_FILES)
gnc_add_test(${_TARGET} "${_SOURCE_FILES}" CORE_UTILS_TEST_INCLUDE_DIRS CORE_UTILS_TEST_LIBS)
endmacro()
-add_core_utils_test(test-gnc-string-utils gtest-gnc-string-utils.cpp)
+set(gtest_string_utils_INCLUDES
+ ${CMAKE_BINARY_DIR}/common # for config.h
+ ${MODULEPATH}
+ ${CMAKE_SOURCE_DIR}/common/test-core)
+
+set(gtest_string_utils_LIBS gnc-core-utils test-core gtest)
+
+gnc_add_test(test-gnc-string-utils gtest-gnc-string-utils.cpp
+ gtest_string_utils_INCLUDES gtest_string_utils_LIBS)
add_core_utils_test(test-resolve-file-path test-resolve-file-path.c)
add_core_utils_test(test-userdata-dir test-userdata-dir.c)
if (NOT MAC_INTEGRATION AND NOT WIN32)
diff --git a/libgnucash/core-utils/test/gtest-gnc-string-utils.cpp b/libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
index d0eb1815ee..d74a4e3f75 100644
--- a/libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
+++ b/libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
@@ -1,6 +1,7 @@
/********************************************************************
- * testmain.c: GLib g_test test execution file. *
- * Copyright 2011 John Ralls <jralls at ceridwen.us> *
+ * gtest-gnc-string-utils.cpp: Unit tests for gnc-string-utils. *
+ * Copyright 2011 John Ralls <jralls at ceridwen.us> *
+ * Copyright 2026 Brent McBride <mcbridebt at hotmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -22,150 +23,312 @@
#include <config.h>
-#include <string.h>
+#include <cstring>
#include <glib.h>
#include <gnc-string-utils.h>
+#include <gnc-locale-utils.hpp>
#include <unittest-support.h>
+#include <gtest/gtest.h>
+
+/* safe_utf8_collate compares with boost::locale::collator via
+ * gnc_get_boost_locale(), which returns a usable locale only after
+ * gnc_init_boost_locale() has run (done in main() for the application).
+ * Initialize it once for the whole test run.
+ *
+ * Pin the encoding to US-ASCII (LC_ALL=C) so that gnc_locale_from_utf8 and
+ * gnc_locale_to_utf8 exercise their conversion (and failure) path rather
+ * than the UTF-8 no-op short-circuit, deterministically on any host. */
+class BoostLocaleEnvironment : public ::testing::Environment
+{
+public:
+ void SetUp () override
+ {
+ g_setenv ("LC_ALL", "C", TRUE);
+ gnc_init_boost_locale ("");
+ }
+};
+
+static auto* const boost_locale_env =
+ ::testing::AddGlobalTestEnvironment (new BoostLocaleEnvironment);
+
+TEST(GncGlibUtils, safe_utf8_collate)
+{
+ EXPECT_EQ (0, safe_utf8_collate ("abc", "abc"));
+ EXPECT_LT (safe_utf8_collate ("abc", "abd"), 0);
+ EXPECT_GT (safe_utf8_collate ("abd", "abc"), 0);
+
+ /* Empty strings are treated the same as nullptr. */
+ EXPECT_EQ (0, safe_utf8_collate ("", ""));
+ EXPECT_EQ (0, safe_utf8_collate (nullptr, nullptr));
+ EXPECT_EQ (0, safe_utf8_collate ("", nullptr));
+ EXPECT_EQ (0, safe_utf8_collate (nullptr, ""));
+
+ /* When only one side is empty/nullptr the non-empty side sorts later. */
+ EXPECT_EQ (1, safe_utf8_collate ("abc", nullptr));
+ EXPECT_EQ (1, safe_utf8_collate ("abc", ""));
+ EXPECT_EQ (-1, safe_utf8_collate (nullptr, "abc"));
+ EXPECT_EQ (-1, safe_utf8_collate ("", "abc"));
+}
+
+TEST(GncGlibUtils, gnc_utf8_validate)
+{
+ const gchar *valid = "Hello, world";
+ const gchar *end = nullptr;
+
+ EXPECT_TRUE (gnc_utf8_validate (valid, -1, &end));
+ EXPECT_EQ (valid + strlen (valid), end);
+
+ /* max_len path: validate only a prefix of the string. */
+ EXPECT_TRUE (gnc_utf8_validate (valid, 5, &end));
+ EXPECT_EQ (valid + 5, end);
+
+ /* Invalid input: end is left pointing at the first bad byte. */
+ const gchar *invalid = "abc\xb2\xf3xyz";
+ EXPECT_FALSE (gnc_utf8_validate (invalid, -1, &end));
+ EXPECT_EQ (invalid + 3, end);
+
+ /* A multi-byte character (here "é" == 0xc3 0xa9) validates when whole,
+ * but fails when max_len splits it, with end left at the split point. */
+ const gchar *multibyte = "a\xc3\xa9";
+ EXPECT_TRUE (gnc_utf8_validate (multibyte, 3, &end));
+ EXPECT_EQ (multibyte + 3, end);
+ EXPECT_FALSE (gnc_utf8_validate (multibyte, 2, &end));
+ EXPECT_EQ (multibyte + 1, end);
+
+ /* A well-formed 6-byte lead byte (0xFC) followed by an invalid
+ * continuation byte decodes to (gunichar)-1 while UTF8_LENGTH still
+ * matches, exercising the result == -1 rejection rather than the
+ * overlong-length check. */
+ const gchar *bad_seq = "\xFC\x20";
+ EXPECT_FALSE (gnc_utf8_validate (bad_seq, -1, &end));
+ EXPECT_EQ (bad_seq, end);
+}
+
+TEST(GncGlibUtils, gnc_utf8_strip_invalid_strdup)
+{
+ /* Valid input yields an equal, independently-allocated copy. */
+ gchar *result = gnc_utf8_strip_invalid_strdup ("valid string");
+ EXPECT_STREQ ("valid string", result);
+ g_free (result);
+}
static void
-test_gnc_utf8_strip_invalid_and_controls (gconstpointer data)
+check_strip_invalid_and_controls (const gchar *input)
{
- gchar *str = g_strdup (data);
+ gchar *str = g_strdup (input);
const gchar *controls = "\b\f\n\r\t\v\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\xa\xb\xc\xd\xe\xf\x10\x11\x12\x13\x14\x15\x16"
"\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
- char *msg1 = g_strdup_printf ("Invalid utf8 string: %s",
- (const gchar*)data);
- const GLogLevelFlags level = G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL;
- TestErrorStruct check = {level, NULL, msg1, 0};
-
- guint handler = g_log_set_handler (NULL, level,
+ gchar *msg = g_strdup_printf ("Invalid utf8 string: %s", input);
+ const GLogLevelFlags level = static_cast<GLogLevelFlags>
+ (G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL);
+ TestErrorStruct check = {level, nullptr, msg, 0};
+ guint handler = g_log_set_handler (nullptr, level,
(GLogFunc)test_null_handler, &check);
- g_test_log_set_fatal_handler((GTestLogFatalFunc)test_checked_handler,
- &check);
gnc_utf8_strip_invalid_and_controls (str);
- g_assert_true (g_utf8_validate(str, -1, NULL) == TRUE);
- g_assert_true (strpbrk(str, controls) == NULL);
- g_assert_true (g_utf8_strlen(str, -1) > 0);
- g_log_remove_handler (NULL, handler);
+ EXPECT_TRUE (g_utf8_validate (str, -1, nullptr));
+ EXPECT_EQ (nullptr, strpbrk (str, controls));
+ EXPECT_GT (g_utf8_strlen (str, -1), 0);
+
+ g_log_remove_handler (nullptr, handler);
g_free (str);
- g_free (msg1);
+ g_free (msg);
}
-static void
-test_g_list_stringjoin (gconstpointer data)
+TEST(GncGlibUtils, gnc_utf8_strip_invalid_and_controls)
{
- GList *test = NULL;
- gchar *ret;
+ check_strip_invalid_and_controls
+ ("ΠγÏήγοÏη καÏΠαλεÏÎ¿Ï Ïήδηξε ÏÎ¬Î½Ï Î±ÏÏ Ïην \xb2\xf3Ïγή ÏκÏλο.");
+ check_strip_invalid_and_controls
+ ("ΠγÏήγοÏη καÏΠαλεÏοÏ\bÏήδηξε\nÏÎ¬Î½Ï Î±ÏÏ\tÏην αÏγή ÏκÏλο.");
+}
+
+TEST(GncGlibUtils, gnc_locale_utf8_roundtrip)
+{
+ /* Plain ASCII round-trips through any locale encoding. */
+ const gchar *ascii = "Plain ASCII text 12345";
+ gchar *locale_str = gnc_locale_from_utf8 (ascii);
+ ASSERT_NE (nullptr, locale_str);
+ gchar *utf8_str = gnc_locale_to_utf8 (locale_str);
+ ASSERT_NE (nullptr, utf8_str);
+ EXPECT_STREQ (ascii, utf8_str);
+ g_free (locale_str);
+ g_free (utf8_str);
+}
- ret = gnc_g_list_stringjoin (NULL, NULL);
- g_assert_true (ret == NULL);
+TEST(GncGlibUtils, gnc_locale_conversion_failure)
+{
+ /* Input that cannot be converted makes boost::locale::conv throw with
+ * the stop method; the functions catch it, warn, and return nullptr.
+ * 0xff is invalid UTF-8 and non-ASCII, so both directions fail in any
+ * locale encoding. */
+ const gchar *invalid = "\xff\xfe";
+ const GLogLevelFlags level = static_cast<GLogLevelFlags>
+ (G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL);
+ TestErrorStruct check = {level, nullptr, nullptr, 0};
+ guint handler = g_log_set_handler (nullptr, level,
+ (GLogFunc)test_null_handler, &check);
- ret = gnc_g_list_stringjoin (NULL, ":");
- g_assert_true (ret == NULL);
+ EXPECT_EQ (nullptr, gnc_locale_from_utf8 (invalid));
+ EXPECT_EQ (nullptr, gnc_locale_to_utf8 (invalid));
- test = g_list_prepend (test, "one");
+ g_log_remove_handler (nullptr, handler);
+}
- ret = gnc_g_list_stringjoin (test, NULL);
- g_assert_cmpstr (ret, ==, "one");
+static gpointer
+add_offset_map_fn (gpointer data, gpointer user_data)
+{
+ return GINT_TO_POINTER (GPOINTER_TO_INT (data) +
+ GPOINTER_TO_INT (user_data));
+}
+
+TEST(GncGlibUtils, gnc_g_list_map)
+{
+ GList *list = nullptr;
+ list = g_list_append (list, GINT_TO_POINTER (1));
+ list = g_list_append (list, GINT_TO_POINTER (2));
+ list = g_list_append (list, GINT_TO_POINTER (3));
+
+ GList *mapped = gnc_g_list_map (list, add_offset_map_fn,
+ GINT_TO_POINTER (10));
+ ASSERT_EQ (3u, g_list_length (mapped));
+ EXPECT_EQ (11, GPOINTER_TO_INT (g_list_nth_data (mapped, 0)));
+ EXPECT_EQ (12, GPOINTER_TO_INT (g_list_nth_data (mapped, 1)));
+ EXPECT_EQ (13, GPOINTER_TO_INT (g_list_nth_data (mapped, 2)));
+
+ g_list_free (list);
+ g_list_free (mapped);
+}
+
+TEST(GncGlibUtils, gnc_g_list_cut)
+{
+ /* A null or empty list is left untouched. */
+ GList *empty = nullptr;
+ gnc_g_list_cut (&empty, nullptr);
+ EXPECT_EQ (nullptr, empty);
+
+ GList *list = nullptr;
+ list = g_list_append (list, GINT_TO_POINTER (1));
+ list = g_list_append (list, GINT_TO_POINTER (2));
+ list = g_list_append (list, GINT_TO_POINTER (3));
+ list = g_list_append (list, GINT_TO_POINTER (4));
+
+ /* Cut at the third element: head keeps [1, 2], tail becomes [3, 4]. */
+ GList *tail = g_list_nth (list, 2);
+ gnc_g_list_cut (&list, tail);
+
+ ASSERT_EQ (2u, g_list_length (list));
+ EXPECT_EQ (1, GPOINTER_TO_INT (g_list_nth_data (list, 0)));
+ EXPECT_EQ (2, GPOINTER_TO_INT (g_list_nth_data (list, 1)));
+
+ ASSERT_EQ (2u, g_list_length (tail));
+ EXPECT_EQ (3, GPOINTER_TO_INT (g_list_nth_data (tail, 0)));
+ EXPECT_EQ (4, GPOINTER_TO_INT (g_list_nth_data (tail, 1)));
+
+ g_list_free (list);
+ g_list_free (tail);
+
+ /* Cutting at the first element clears the caller's list pointer. */
+ GList *head = g_list_append (nullptr, GINT_TO_POINTER (1));
+ GList *single = head;
+ gnc_g_list_cut (&single, head);
+ EXPECT_EQ (nullptr, single);
+ g_list_free (head);
+}
+
+TEST(GncGlibUtils, gnc_g_list_stringjoin)
+{
+ GList *test = nullptr;
+ gchar *ret;
+
+ EXPECT_EQ (nullptr, gnc_g_list_stringjoin (nullptr, nullptr));
+ EXPECT_EQ (nullptr, gnc_g_list_stringjoin (nullptr, ":"));
+
+ test = g_list_prepend (test, (gpointer)"one");
+
+ ret = gnc_g_list_stringjoin (test, nullptr);
+ EXPECT_STREQ ("one", ret);
g_free (ret);
ret = gnc_g_list_stringjoin (test, "");
- g_assert_cmpstr (ret, ==, "one");
+ EXPECT_STREQ ("one", ret);
g_free (ret);
ret = gnc_g_list_stringjoin (test, ":");
- g_assert_cmpstr (ret, ==, "one");
+ EXPECT_STREQ ("one", ret);
g_free (ret);
- /* The following inserts a NULL between "two" and "one". As a
+ /* The following inserts a nullptr between "two" and "one". As a
result, the stringjoin effectively skips a step, i.e. it does
not insert separator repeatedly between NULL strings */
- test = g_list_prepend (test, NULL);
+ test = g_list_prepend (test, nullptr);
- test = g_list_prepend (test, "two");
+ test = g_list_prepend (test, (gpointer)"two");
- ret = gnc_g_list_stringjoin (test, NULL);
- g_assert_cmpstr (ret, ==, "twoone");
+ ret = gnc_g_list_stringjoin (test, nullptr);
+ EXPECT_STREQ ("twoone", ret);
g_free (ret);
ret = gnc_g_list_stringjoin (test, "");
- g_assert_cmpstr (ret, ==, "twoone");
+ EXPECT_STREQ ("twoone", ret);
g_free (ret);
ret = gnc_g_list_stringjoin (test, ":");
- g_assert_cmpstr (ret, ==, "two:one");
+ EXPECT_STREQ ("two:one", ret);
g_free (ret);
- test = g_list_prepend (test, "three");
+ test = g_list_prepend (test, (gpointer)"three");
- ret = gnc_g_list_stringjoin (test, NULL);
- g_assert_cmpstr (ret, ==, "threetwoone");
+ ret = gnc_g_list_stringjoin (test, nullptr);
+ EXPECT_STREQ ("threetwoone", ret);
g_free (ret);
ret = gnc_g_list_stringjoin (test, "");
- g_assert_cmpstr (ret, ==, "threetwoone");
+ EXPECT_STREQ ("threetwoone", ret);
g_free (ret);
ret = gnc_g_list_stringjoin (test, ":");
- g_assert_cmpstr (ret, ==, "three:two:one");
+ EXPECT_STREQ ("three:two:one", ret);
g_free (ret);
g_list_free (test);
}
-static void
-test_g_list_stringjoin_nodups (gconstpointer data)
+TEST(GncGlibUtils, gnc_g_list_stringjoin_nodups)
{
- GList *test = NULL;
+ GList *test = nullptr;
gchar *ret;
- test = g_list_prepend (test, "one");
- test = g_list_prepend (test, "two");
- test = g_list_prepend (test, "two");
- test = g_list_prepend (test, "three");
- test = g_list_prepend (test, "one:two");
- test = g_list_prepend (test, "four");
+ test = g_list_prepend (test, (gpointer)"one");
+ test = g_list_prepend (test, (gpointer)"two");
+ test = g_list_prepend (test, (gpointer)"two");
+ test = g_list_prepend (test, (gpointer)"three");
+ test = g_list_prepend (test, (gpointer)"one:two");
+ test = g_list_prepend (test, (gpointer)"four");
test = g_list_reverse (test);
ret = gnc_g_list_stringjoin_nodups (test, ":");
- g_assert_cmpstr (ret, ==, "one:two:three:four");
+ EXPECT_STREQ ("one:two:three:four", ret);
g_free (ret);
+ g_list_free (test);
}
-static void
-test_gnc_list_length (gconstpointer data)
+TEST(GncGlibUtils, gnc_list_length_cmp)
{
- GList *lst = NULL;
+ GList *lst = nullptr;
- g_assert_true (gnc_list_length_cmp (lst, 0) == 0);
- g_assert_true (gnc_list_length_cmp (lst, 1) == -1);
+ EXPECT_EQ (0, gnc_list_length_cmp (lst, 0));
+ EXPECT_EQ (-1, gnc_list_length_cmp (lst, 1));
- lst = g_list_prepend (lst, (gpointer)1);
- g_assert_true (gnc_list_length_cmp (lst, 0) == 1);
- g_assert_true (gnc_list_length_cmp (lst, 1) == 0);
- g_assert_true (gnc_list_length_cmp (lst, 2) == -1);
+ lst = g_list_prepend (lst, GINT_TO_POINTER (1));
+ EXPECT_EQ (1, gnc_list_length_cmp (lst, 0));
+ EXPECT_EQ (0, gnc_list_length_cmp (lst, 1));
+ EXPECT_EQ (-1, gnc_list_length_cmp (lst, 2));
- lst = g_list_prepend (lst, (gpointer)2);
- g_assert_true (gnc_list_length_cmp (lst, 1) == 1);
- g_assert_true (gnc_list_length_cmp (lst, 2) == 0);
- g_assert_true (gnc_list_length_cmp (lst, 3) == -1);
+ lst = g_list_prepend (lst, GINT_TO_POINTER (2));
+ EXPECT_EQ (1, gnc_list_length_cmp (lst, 1));
+ EXPECT_EQ (0, gnc_list_length_cmp (lst, 2));
+ EXPECT_EQ (-1, gnc_list_length_cmp (lst, 3));
g_list_free (lst);
}
-
-
-int
-main (int argc, char *argv[])
-{
- const gchar *invalid_utf8 = "ΠγÏήγοÏη καÏΠαλεÏÎ¿Ï Ïήδηξε ÏÎ¬Î½Ï Î±ÏÏ Ïην \xb2\xf3Ïγή ÏκÏλο.";
- const gchar *controls = "ΠγÏήγοÏη καÏΠαλεÏοÏ\bÏήδηξε\nÏÎ¬Î½Ï Î±ÏÏ\tÏην αÏγή ÏκÏλο.";
- g_test_init (&argc, &argv, NULL); // initialize test program
- g_test_add_data_func ("/core-utils/gnc_utf8_strip_invalid_and_controls invalid utf8", (gconstpointer)invalid_utf8, test_gnc_utf8_strip_invalid_and_controls);
- g_test_add_data_func ("/core-utils/gnc_utf8_strip_invalid_and_controls control chars", (gconstpointer)controls, test_gnc_utf8_strip_invalid_and_controls);
- g_test_add_data_func ("/core-utils/gnc_g_list_stringjoin", NULL, test_g_list_stringjoin);
- g_test_add_data_func ("/core-utils/gnc_g_list_stringjoin_nodups", NULL, test_g_list_stringjoin_nodups);
- g_test_add_data_func ("/core-utils/gnc_list_length", NULL, test_gnc_list_length);
-
- return g_test_run();
-}
commit 770fd5af633e1fd15684cf33ab0beaebfe7355b4
Author: Brent McBride <mcbridebt at hotmail.com>
Date: Tue Jul 14 21:37:34 2026 -0700
Rename gnc-glib-utils.{c,h} and its test to gnc-string-utils for C++ migration
diff --git a/bindings/core-utils.i b/bindings/core-utils.i
index 064d60707a..893ea651f5 100644
--- a/bindings/core-utils.i
+++ b/bindings/core-utils.i
@@ -22,7 +22,7 @@
%{
#include <config.h>
#include <gnc-environment.h>
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include <gnc-prefs.h>
#include <gnc-path.h>
#include <gnc-filepath-utils.h>
diff --git a/bindings/guile/glib-guile.c b/bindings/guile/glib-guile.c
index 849c069ab3..53df5d393a 100644
--- a/bindings/guile/glib-guile.c
+++ b/bindings/guile/glib-guile.c
@@ -45,7 +45,7 @@
#include <libguile.h>
#include "swig-runtime.h"
#include "guile-mappings.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-guile-utils.h"
#include "glib-guile.h"
diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index 10094d2376..0cd2c26f2a 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -36,7 +36,7 @@
#include "gnc-file.h"
#include "gnc-features.h"
#include "gnc-filepath-utils.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gui-query.h"
#include "gnc-hooks.h"
#include "gnc-keyring.h"
diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index 1f321b6c5a..72e928471b 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -69,7 +69,7 @@
#include "gnc-state.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "gnc-uri.hpp"
#include "gnc-version.h"
#include "gnc-warnings.h"
diff --git a/gnucash/gnome-utils/gnc-tree-view-account.c b/gnucash/gnome-utils/gnc-tree-view-account.c
index 586472a68e..a43ea1d1fa 100644
--- a/gnucash/gnome-utils/gnc-tree-view-account.c
+++ b/gnucash/gnome-utils/gnc-tree-view-account.c
@@ -39,7 +39,7 @@
#include "gnc-commodity.h"
#include "gnc-component-manager.h"
#include "gnc-engine.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gobject-utils.h"
#include "gnc-prefs.h"
#include "gnc-hooks.h"
diff --git a/gnucash/gnome-utils/gnc-tree-view-commodity.c b/gnucash/gnome-utils/gnc-tree-view-commodity.c
index 9fd3bb4a93..bf242cef29 100644
--- a/gnucash/gnome-utils/gnc-tree-view-commodity.c
+++ b/gnucash/gnome-utils/gnc-tree-view-commodity.c
@@ -35,7 +35,7 @@
#include "gnc-commodity.h"
#include "gnc-component-manager.h"
#include "gnc-engine.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gnome-utils.h"
#include "gnc-icons.h"
#include "gnc-ui-util.h"
diff --git a/gnucash/gnome-utils/gnc-tree-view-owner.c b/gnucash/gnome-utils/gnc-tree-view-owner.c
index d1b1762845..16d6f2e122 100644
--- a/gnucash/gnome-utils/gnc-tree-view-owner.c
+++ b/gnucash/gnome-utils/gnc-tree-view-owner.c
@@ -37,7 +37,7 @@
#include "gnc-commodity.h"
#include "gnc-component-manager.h"
#include "gnc-engine.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gobject-utils.h"
#include "gnc-hooks.h"
#include "gnc-session.h"
diff --git a/gnucash/gnome-utils/gnc-tree-view-price.c b/gnucash/gnome-utils/gnc-tree-view-price.c
index 96e22ea77f..f51c10dc4a 100644
--- a/gnucash/gnome-utils/gnc-tree-view-price.c
+++ b/gnucash/gnome-utils/gnc-tree-view-price.c
@@ -35,7 +35,7 @@
#include "gnc-pricedb.h"
#include "gnc-component-manager.h"
#include "gnc-engine.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gnome-utils.h"
#include "gnc-icons.h"
#include "gnc-ui-util.h"
diff --git a/gnucash/gnome-utils/gnc-tree-view.c b/gnucash/gnome-utils/gnc-tree-view.c
index 38af5ec221..b71742affc 100644
--- a/gnucash/gnome-utils/gnc-tree-view.c
+++ b/gnucash/gnome-utils/gnc-tree-view.c
@@ -40,7 +40,7 @@
#include "gnc-tree-view.h"
#include "gnc-engine.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gnome-utils.h"
#include "gnc-gobject-utils.h"
#include "gnc-cell-renderer-label.h"
diff --git a/gnucash/gnome/dialog-imap-editor.c b/gnucash/gnome/dialog-imap-editor.c
index c20d5a7b4d..669d6c822e 100644
--- a/gnucash/gnome/dialog-imap-editor.c
+++ b/gnucash/gnome/dialog-imap-editor.c
@@ -33,7 +33,7 @@
#include "gnc-ui.h"
#include "gnc-ui-util.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "Account.h"
#define DIALOG_IMAP_CM_CLASS "dialog-imap-edit"
diff --git a/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c
index 67f9279f64..d62d4aee16 100644
--- a/gnucash/gnome/dialog-invoice.c
+++ b/gnucash/gnome/dialog-invoice.c
@@ -50,7 +50,7 @@
#include "gncOwner.h"
#include "gncInvoice.h"
#include "gncInvoiceP.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "gncEntryLedger.h"
diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c
index 51dcf9e1f0..e622ab9719 100644
--- a/gnucash/gnome/dialog-payment.c
+++ b/gnucash/gnome/dialog-payment.c
@@ -32,7 +32,7 @@
#include "gnc-ui.h"
#include "gnc-gui-query.h"
#include "gnc-ui-util.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "qof.h"
#include "gnc-date.h"
#include "gnc-date-edit.h"
diff --git a/gnucash/gnome/dialog-price-edit-db.cpp b/gnucash/gnome/dialog-price-edit-db.cpp
index 2308e36e30..8885aa488b 100644
--- a/gnucash/gnome/dialog-price-edit-db.cpp
+++ b/gnucash/gnome/dialog-price-edit-db.cpp
@@ -48,7 +48,7 @@
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "gnc-warnings.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db"
diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c
index 1cae867e51..b5c8932883 100644
--- a/gnucash/gnome/dialog-sx-editor.c
+++ b/gnucash/gnome/dialog-sx-editor.c
@@ -64,7 +64,7 @@
#include "gnc-ui-util.h"
#include "gnucash-sheet.h"
#include "gnc-session.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "gnc-split-reg.h"
diff --git a/gnucash/gnome/dialog-sx-since-last-run.c b/gnucash/gnome/dialog-sx-since-last-run.c
index fc1972553b..6c07774556 100644
--- a/gnucash/gnome/dialog-sx-since-last-run.c
+++ b/gnucash/gnome/dialog-sx-since-last-run.c
@@ -43,7 +43,7 @@
#include "gnc-prefs.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "Query.h"
#include "qof.h"
#include "gnc-ledger-display.h"
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.cpp b/gnucash/gnome/gnc-plugin-page-account-tree.cpp
index 4a7fb6a468..5f2ca6b696 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.cpp
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.cpp
@@ -73,7 +73,7 @@
#include "window-main-summarybar.h"
#include "dialog-object-references.h"
#include "dialog-find-account.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_GUI;
diff --git a/gnucash/gnome/gnc-plugin-page-register-filter.cpp b/gnucash/gnome/gnc-plugin-page-register-filter.cpp
index 1a1dea4161..50df6ab09e 100644
--- a/gnucash/gnome/gnc-plugin-page-register-filter.cpp
+++ b/gnucash/gnome/gnc-plugin-page-register-filter.cpp
@@ -37,7 +37,7 @@
#include "dialog-utils.h"
#include "gnc-date.h"
#include "gnc-date-edit.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-ui.h"
#include "gnc-state.h"
#include "gnc-period-select.h"
diff --git a/gnucash/gnome/gnc-plugin-page-register.cpp b/gnucash/gnome/gnc-plugin-page-register.cpp
index 89b52dcc49..3c459ad359 100644
--- a/gnucash/gnome/gnc-plugin-page-register.cpp
+++ b/gnucash/gnome/gnc-plugin-page-register.cpp
@@ -65,7 +65,7 @@
#include "gnc-engine.h"
#include "gnc-event.h"
#include "gnc-features.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gnome-utils.h"
#include "gnc-gobject-utils.h"
#include "gnc-gui-query.h"
diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index 68c2ace85c..71dc73c92f 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -47,7 +47,7 @@
#include <sys/stat.h>
#include <errno.h>
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "gfec.h"
#include "dialog-custom-report.h"
#include "dialog-utils.h"
diff --git a/gnucash/gnome/gnc-plugin-page-sx-list.cpp b/gnucash/gnome/gnc-plugin-page-sx-list.cpp
index 2a0dd17fd3..ad28429cfc 100644
--- a/gnucash/gnome/gnc-plugin-page-sx-list.cpp
+++ b/gnucash/gnome/gnc-plugin-page-sx-list.cpp
@@ -60,7 +60,7 @@
#include "gnc-dense-cal.h"
#include "gnc-engine.h"
#include "gnc-event.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-icons.h"
#include "gnc-main-window.h"
#include "gnc-plugin.h"
diff --git a/gnucash/import-export/aqb/assistant-ab-initial.c b/gnucash/import-export/aqb/assistant-ab-initial.c
index 5a2dba7f30..effb1aae47 100644
--- a/gnucash/import-export/aqb/assistant-ab-initial.c
+++ b/gnucash/import-export/aqb/assistant-ab-initial.c
@@ -58,7 +58,7 @@
#include "gnc-ab-kvp.h"
#include "gnc-ab-utils.h"
#include "gnc-component-manager.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "gnc-session.h"
diff --git a/gnucash/import-export/aqb/gnc-ab-utils.c b/gnucash/import-export/aqb/gnc-ab-utils.c
index 2c4defcc92..de616c747a 100644
--- a/gnucash/import-export/aqb/gnc-ab-utils.c
+++ b/gnucash/import-export/aqb/gnc-ab-utils.c
@@ -43,7 +43,7 @@
#include "Transaction.h"
#include "dialog-ab-trans.h"
#include "gnc-ab-kvp.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-gwen-gui.h"
#include "gnc-prefs.h"
#include "gnc-ui.h"
diff --git a/gnucash/import-export/bi-import/dialog-bi-import.c b/gnucash/import-export/bi-import/dialog-bi-import.c
index f29743b933..f17df19205 100644
--- a/gnucash/import-export/bi-import/dialog-bi-import.c
+++ b/gnucash/import-export/bi-import/dialog-bi-import.c
@@ -36,7 +36,7 @@
#include <glib.h>
#include <glib/gstdio.h>
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-date.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
diff --git a/gnucash/import-export/csv-imp/csv-account-import.c b/gnucash/import-export/csv-imp/csv-account-import.c
index f575fee7dd..6f9fbc6680 100644
--- a/gnucash/import-export/csv-imp/csv-account-import.c
+++ b/gnucash/import-export/csv-imp/csv-account-import.c
@@ -30,7 +30,7 @@
#include <glib/gi18n.h>
#include <glib/gstdio.h>
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-ui-util.h"
#include <regex.h>
#include "Account.h"
diff --git a/gnucash/import-export/customer-import/dialog-customer-import.c b/gnucash/import-export/customer-import/dialog-customer-import.c
index 2991be1756..3f61c0966b 100644
--- a/gnucash/import-export/customer-import/dialog-customer-import.c
+++ b/gnucash/import-export/customer-import/dialog-customer-import.c
@@ -34,7 +34,7 @@
#include <glib.h>
#include <glib/gstdio.h>
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "gnc-gui-query.h"
diff --git a/gnucash/import-export/import-main-matcher.cpp b/gnucash/import-export/import-main-matcher.cpp
index 3a5e7c2bcd..8331b7fda1 100644
--- a/gnucash/import-export/import-main-matcher.cpp
+++ b/gnucash/import-export/import-main-matcher.cpp
@@ -49,7 +49,7 @@
#include "Account.hpp"
#include "dialog-transfer.h"
#include "dialog-utils.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "gnc-engine.h"
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.cpp b/gnucash/import-export/ofx/gnc-ofx-import.cpp
index 8e4d218917..d4cf3e9846 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.cpp
+++ b/gnucash/import-export/ofx/gnc-ofx-import.cpp
@@ -46,7 +46,7 @@
#include "gnc-file.h"
#include "gnc-engine.h"
#include "gnc-ui-util.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-prefs.h"
#include "gnc-ui.h"
#include "gnc-window.h"
diff --git a/gnucash/register/ledger-core/gnc-ledger-display.c b/gnucash/register/ledger-core/gnc-ledger-display.c
index ae632ef782..8b8554c216 100644
--- a/gnucash/register/ledger-core/gnc-ledger-display.c
+++ b/gnucash/register/ledger-core/gnc-ledger-display.c
@@ -37,7 +37,7 @@
#include "gnc-ledger-display.h"
#include "gnc-prefs.h"
#include "gnc-ui-util.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include "split-register-control.h"
#include "split-register-model.h"
diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c
index 34ed7d5a1f..203d46772e 100644
--- a/gnucash/register/ledger-core/split-register-model.c
+++ b/gnucash/register/ledger-core/split-register-model.c
@@ -32,7 +32,7 @@
#include "gnc-prefs.h"
#include "gnc-ui.h"
#include "gnc-uri-utils.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-filepath-utils.h"
#include "gnc-warnings.h"
#include "doclinkcell.h"
diff --git a/gnucash/register/register-gnome/combocell-gnome.c b/gnucash/register/register-gnome/combocell-gnome.c
index 05b942c069..525a857c15 100644
--- a/gnucash/register/register-gnome/combocell-gnome.c
+++ b/gnucash/register/register-gnome/combocell-gnome.c
@@ -47,7 +47,7 @@
#include "gnucash-sheetP.h"
#include "table-allgui.h"
#include "Account.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#define GNC_PREF_AUTO_RAISE_LISTS "auto-raise-lists"
diff --git a/gnucash/register/register-gnome/completioncell-gnome.c b/gnucash/register/register-gnome/completioncell-gnome.c
index 76e47ad6e7..74022a3726 100644
--- a/gnucash/register/register-gnome/completioncell-gnome.c
+++ b/gnucash/register/register-gnome/completioncell-gnome.c
@@ -43,7 +43,7 @@
#include "gnucash-sheet.h"
#include "gnucash-sheetP.h"
#include "table-allgui.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include <gnc-unicode.h>
typedef struct _PopBox
diff --git a/libgnucash/app-utils/gnc-sx-instance-model.c b/libgnucash/app-utils/gnc-sx-instance-model.c
index b742c8dc31..dbccc92d1a 100644
--- a/libgnucash/app-utils/gnc-sx-instance-model.c
+++ b/libgnucash/app-utils/gnc-sx-instance-model.c
@@ -46,7 +46,7 @@
#include "gnc-date.h"
#include "gnc-event.h"
#include "gnc-exp-parser.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-sx-instance-model.h"
#include "gnc-ui-util.h"
#include "qof.h"
diff --git a/libgnucash/core-utils/CMakeLists.txt b/libgnucash/core-utils/CMakeLists.txt
index 21ce5acc08..6bbf5de4c1 100644
--- a/libgnucash/core-utils/CMakeLists.txt
+++ b/libgnucash/core-utils/CMakeLists.txt
@@ -10,7 +10,7 @@ set (core_utils_SOURCES
gnc-environment.c
gnc-filepath-utils.cpp
gnc-gkeyfile-utils.c
- gnc-glib-utils.c
+ gnc-string-utils.cpp
gnc-unicode.cpp
gnc-locale-utils.c
gnc-locale-utils.cpp
@@ -28,7 +28,7 @@ set(core_utils_noinst_HEADERS
gnc-environment.h
gnc-filepath-utils.h
gnc-gkeyfile-utils.h
- gnc-glib-utils.h
+ gnc-string-utils.h
gnc-unicode.h
gnc-locale-utils.h
gnc-locale-utils.hpp
diff --git a/libgnucash/core-utils/gnc-glib-utils.c b/libgnucash/core-utils/gnc-string-utils.cpp
similarity index 99%
rename from libgnucash/core-utils/gnc-glib-utils.c
rename to libgnucash/core-utils/gnc-string-utils.cpp
index 0b55982625..d287485a77 100644
--- a/libgnucash/core-utils/gnc-glib-utils.c
+++ b/libgnucash/core-utils/gnc-string-utils.cpp
@@ -28,7 +28,7 @@
#include <string.h>
#include <stdbool.h>
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#ifdef G_OS_WIN32
#include <windows.h>
diff --git a/libgnucash/core-utils/gnc-glib-utils.h b/libgnucash/core-utils/gnc-string-utils.h
similarity index 96%
rename from libgnucash/core-utils/gnc-glib-utils.h
rename to libgnucash/core-utils/gnc-string-utils.h
index a2770aaaf8..b0281bdbaf 100644
--- a/libgnucash/core-utils/gnc-glib-utils.h
+++ b/libgnucash/core-utils/gnc-string-utils.h
@@ -1,6 +1,7 @@
/********************************************************************\
- * gnc-glib-utils.c -- utility functions based on glib functions *
+ * gnc-string-utils.h -- string and list utility functions *
* Copyright (C) 2006 David Hampton <hampton at employees.org> *
+ * Copyright (C) 2026 Brent McBride <mcbridebt at hotmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@@ -29,13 +30,13 @@
wrap the base glib functions and make them easier to use.
@{ */
-/** @file gnc-glib-utils.h
- * @brief GLib helper routines
+/** @file gnc-string-utils.h
+ * @brief String and list utility routines
* @author Copyright (C) 2006 David Hampton <hampton at employees.org>
*/
-#ifndef GNC_GLIB_UTILS_H
-#define GNC_GLIB_UTILS_H
+#ifndef GNC_STRING_UTILS_H
+#define GNC_STRING_UTILS_H
#include <glib.h>
@@ -216,6 +217,6 @@ gint gnc_list_length_cmp (const GList *list, size_t len);
} /* extern "C" */
#endif
-#endif /* GNC_GLIB_UTILS_H */
+#endif /* GNC_STRING_UTILS_H */
/** @} */
/** @} */
diff --git a/libgnucash/core-utils/test/CMakeLists.txt b/libgnucash/core-utils/test/CMakeLists.txt
index 1e5ad913a1..0416017fba 100644
--- a/libgnucash/core-utils/test/CMakeLists.txt
+++ b/libgnucash/core-utils/test/CMakeLists.txt
@@ -15,7 +15,7 @@ macro(add_core_utils_test _TARGET _SOURCE_FILES)
gnc_add_test(${_TARGET} "${_SOURCE_FILES}" CORE_UTILS_TEST_INCLUDE_DIRS CORE_UTILS_TEST_LIBS)
endmacro()
-add_core_utils_test(test-gnc-glib-utils test-gnc-glib-utils.c)
+add_core_utils_test(test-gnc-string-utils gtest-gnc-string-utils.cpp)
add_core_utils_test(test-resolve-file-path test-resolve-file-path.c)
add_core_utils_test(test-userdata-dir test-userdata-dir.c)
if (NOT MAC_INTEGRATION AND NOT WIN32)
@@ -67,5 +67,5 @@ gnc_add_test(test-gnc-unicode
gtest_icu_locale_LIBS)
set_dist_list(test_core_utils_DIST CMakeLists.txt
- test-gnc-glib-utils.c test-resolve-file-path.c test-userdata-dir.c
+ gtest-gnc-string-utils.cpp test-resolve-file-path.c test-userdata-dir.c
test-userdata-dir-invalid-home.c gtest-gnc-unicode.cpp gtest-path-utilities.cpp)
diff --git a/libgnucash/core-utils/test/test-gnc-glib-utils.c b/libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
similarity index 99%
rename from libgnucash/core-utils/test/test-gnc-glib-utils.c
rename to libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
index 2f87d80f96..d0eb1815ee 100644
--- a/libgnucash/core-utils/test/test-gnc-glib-utils.c
+++ b/libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
@@ -24,7 +24,7 @@
#include <config.h>
#include <string.h>
#include <glib.h>
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#include <unittest-support.h>
static void
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 6bb094a49f..df2f8c794c 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -39,7 +39,7 @@
#include "Transaction.h"
#include "TransactionP.hpp"
#include "gnc-event.h"
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "gnc-lot.h"
#include "gnc-pricedb.h"
#include "qofevent.h"
diff --git a/libgnucash/engine/Recurrence.cpp b/libgnucash/engine/Recurrence.cpp
index 47f5439cf8..c063500a32 100644
--- a/libgnucash/engine/Recurrence.cpp
+++ b/libgnucash/engine/Recurrence.cpp
@@ -31,7 +31,7 @@
#include "gnc-date.h"
#include "Account.h"
#include <stdint.h>
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
#define LOG_MOD "gnc.engine.recurrence"
static QofLogModule log_module = LOG_MOD;
diff --git a/libgnucash/engine/gncIDSearch.c b/libgnucash/engine/gncIDSearch.c
index 921708d1d4..0146ebb758 100644
--- a/libgnucash/engine/gncIDSearch.c
+++ b/libgnucash/engine/gncIDSearch.c
@@ -22,7 +22,7 @@
**********************************************************************/
#include "gncIDSearch.h"
-#include <gnc-glib-utils.h>
+#include <gnc-string-utils.h>
typedef enum
{ UNDEFINED,
diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp
index fc52032ba1..c8f2a35442 100644
--- a/libgnucash/engine/test/utest-Account.cpp
+++ b/libgnucash/engine/test/utest-Account.cpp
@@ -28,7 +28,7 @@
#include <gnc-event.h>
#include <gnc-date.h>
/* Add specific headers for this class */
-#include "gnc-glib-utils.h"
+#include "gnc-string-utils.h"
#include "../Account.h"
#include "../AccountP.hpp"
#include "../Split.h"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d2acbd170f..a0e5d7f20f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -617,11 +617,11 @@ libgnucash/core-utils/binreloc.c
libgnucash/core-utils/gnc-environment.c
libgnucash/core-utils/gnc-filepath-utils.cpp
libgnucash/core-utils/gnc-gkeyfile-utils.c
-libgnucash/core-utils/gnc-glib-utils.c
libgnucash/core-utils/gnc-locale-utils.c
libgnucash/core-utils/gnc-locale-utils.cpp
libgnucash/core-utils/gnc-path.c
libgnucash/core-utils/gnc-prefs.cpp
+libgnucash/core-utils/gnc-string-utils.cpp
libgnucash/core-utils/gnc-unicode.cpp
libgnucash/core-utils/gnc-version.c
libgnucash/engine/Account.cpp
Summary of changes:
bindings/core-utils.i | 2 +-
bindings/guile/glib-guile.c | 2 +-
gnucash/gnome-utils/gnc-file.c | 2 +-
gnucash/gnome-utils/gnc-main-window.cpp | 2 +-
gnucash/gnome-utils/gnc-tree-view-account.c | 2 +-
gnucash/gnome-utils/gnc-tree-view-commodity.c | 2 +-
gnucash/gnome-utils/gnc-tree-view-owner.c | 2 +-
gnucash/gnome-utils/gnc-tree-view-price.c | 2 +-
gnucash/gnome-utils/gnc-tree-view.c | 2 +-
gnucash/gnome/dialog-imap-editor.c | 2 +-
gnucash/gnome/dialog-invoice.c | 2 +-
gnucash/gnome/dialog-payment.c | 2 +-
gnucash/gnome/dialog-price-edit-db.cpp | 2 +-
gnucash/gnome/dialog-sx-editor.c | 2 +-
gnucash/gnome/dialog-sx-since-last-run.c | 2 +-
gnucash/gnome/gnc-plugin-page-account-tree.cpp | 2 +-
gnucash/gnome/gnc-plugin-page-register-filter.cpp | 2 +-
gnucash/gnome/gnc-plugin-page-register.cpp | 2 +-
gnucash/gnome/gnc-plugin-page-report.cpp | 2 +-
gnucash/gnome/gnc-plugin-page-sx-list.cpp | 2 +-
gnucash/import-export/aqb/assistant-ab-initial.c | 2 +-
gnucash/import-export/aqb/gnc-ab-utils.c | 2 +-
gnucash/import-export/bi-import/dialog-bi-import.c | 2 +-
gnucash/import-export/csv-imp/csv-account-import.c | 2 +-
.../customer-import/dialog-customer-import.c | 2 +-
gnucash/import-export/import-main-matcher.cpp | 2 +-
gnucash/import-export/ofx/gnc-ofx-import.cpp | 2 +-
gnucash/register/ledger-core/gnc-ledger-display.c | 2 +-
.../register/ledger-core/split-register-model.c | 2 +-
gnucash/register/register-gnome/combocell-gnome.c | 2 +-
.../register/register-gnome/completioncell-gnome.c | 2 +-
libgnucash/app-utils/gnc-sx-instance-model.c | 2 +-
libgnucash/core-utils/CMakeLists.txt | 4 +-
libgnucash/core-utils/gnc-locale-utils.cpp | 80 +++--
.../{gnc-glib-utils.c => gnc-string-utils.cpp} | 168 ++++++-----
.../{gnc-glib-utils.h => gnc-string-utils.h} | 18 +-
libgnucash/core-utils/test/CMakeLists.txt | 12 +-
.../core-utils/test/gtest-gnc-string-utils.cpp | 334 +++++++++++++++++++++
libgnucash/core-utils/test/test-gnc-glib-utils.c | 171 -----------
libgnucash/engine/Account.cpp | 2 +-
libgnucash/engine/Recurrence.cpp | 2 +-
libgnucash/engine/gncIDSearch.c | 2 +-
libgnucash/engine/test/utest-Account.cpp | 2 +-
po/POTFILES.in | 2 +-
44 files changed, 535 insertions(+), 326 deletions(-)
rename libgnucash/core-utils/{gnc-glib-utils.c => gnc-string-utils.cpp} (66%)
rename libgnucash/core-utils/{gnc-glib-utils.h => gnc-string-utils.h} (94%)
create mode 100644 libgnucash/core-utils/test/gtest-gnc-string-utils.cpp
delete mode 100644 libgnucash/core-utils/test/test-gnc-glib-utils.c
More information about the gnucash-changes
mailing list