mirror of
https://github.com/Gnucash/gnucash.git
synced 2026-08-27 13:47:35 -05:00
Merge Brent McBride's 'gnc-glib-utils-cpp' into future.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/********************************************************************\
|
||||
* gnc-locale-utils.cpp -- provide a default locale for C++ *
|
||||
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us *
|
||||
* Copyright (C) 2026 Brent McBride <mcbridebt@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;
|
||||
}
|
||||
|
||||
|
||||
+88
-76
@@ -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@employees.org> *
|
||||
* Copyright (C) 2026 Brent McBride <mcbridebt@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,13 +23,17 @@
|
||||
\********************************************************************/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "gnc-glib-utils.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
|
||||
#include <windows.h>
|
||||
@@ -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;
|
||||
g_return_val_if_fail (str != nullptr, nullptr);
|
||||
|
||||
/* 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)
|
||||
// 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;
|
||||
g_return_val_if_fail (str != nullptr, nullptr);
|
||||
|
||||
/* 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)
|
||||
// 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;
|
||||
@@ -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@employees.org> *
|
||||
* Copyright (C) 2026 Brent McBride <mcbridebt@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 *
|
||||
@@ -23,19 +24,18 @@
|
||||
|
||||
/** @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-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@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 +216,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 */
|
||||
/** @} */
|
||||
/** @} */
|
||||
@@ -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-glib-utils test-gnc-glib-utils.c)
|
||||
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)
|
||||
@@ -67,5 +75,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)
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
/********************************************************************
|
||||
* gtest-gnc-string-utils.cpp: Unit tests for gnc-string-utils. *
|
||||
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
|
||||
* Copyright 2026 Brent McBride <mcbridebt@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 *
|
||||
* 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
|
||||
|
||||
#include <config.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
|
||||
check_strip_invalid_and_controls (const gchar *input)
|
||||
{
|
||||
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";
|
||||
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);
|
||||
|
||||
gnc_utf8_strip_invalid_and_controls (str);
|
||||
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 (msg);
|
||||
}
|
||||
|
||||
TEST(GncGlibUtils, gnc_utf8_strip_invalid_and_controls)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
EXPECT_EQ (nullptr, gnc_locale_from_utf8 (invalid));
|
||||
EXPECT_EQ (nullptr, gnc_locale_to_utf8 (invalid));
|
||||
|
||||
g_log_remove_handler (nullptr, handler);
|
||||
}
|
||||
|
||||
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, "");
|
||||
EXPECT_STREQ ("one", ret);
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, ":");
|
||||
EXPECT_STREQ ("one", ret);
|
||||
g_free (ret);
|
||||
|
||||
/* 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, nullptr);
|
||||
|
||||
test = g_list_prepend (test, (gpointer)"two");
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, nullptr);
|
||||
EXPECT_STREQ ("twoone", ret);
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, "");
|
||||
EXPECT_STREQ ("twoone", ret);
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, ":");
|
||||
EXPECT_STREQ ("two:one", ret);
|
||||
g_free (ret);
|
||||
|
||||
test = g_list_prepend (test, (gpointer)"three");
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, nullptr);
|
||||
EXPECT_STREQ ("threetwoone", ret);
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, "");
|
||||
EXPECT_STREQ ("threetwoone", ret);
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, ":");
|
||||
EXPECT_STREQ ("three:two:one", ret);
|
||||
g_free (ret);
|
||||
|
||||
g_list_free (test);
|
||||
}
|
||||
|
||||
TEST(GncGlibUtils, gnc_g_list_stringjoin_nodups)
|
||||
{
|
||||
GList *test = nullptr;
|
||||
gchar *ret;
|
||||
|
||||
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, ":");
|
||||
EXPECT_STREQ ("one:two:three:four", ret);
|
||||
g_free (ret);
|
||||
g_list_free (test);
|
||||
}
|
||||
|
||||
TEST(GncGlibUtils, gnc_list_length_cmp)
|
||||
{
|
||||
GList *lst = nullptr;
|
||||
|
||||
EXPECT_EQ (0, gnc_list_length_cmp (lst, 0));
|
||||
EXPECT_EQ (-1, gnc_list_length_cmp (lst, 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, 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);
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/********************************************************************
|
||||
* testmain.c: GLib g_test test execution file. *
|
||||
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
|
||||
* *
|
||||
* 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include <gnc-glib-utils.h>
|
||||
#include <unittest-support.h>
|
||||
|
||||
static void
|
||||
test_gnc_utf8_strip_invalid_and_controls (gconstpointer data)
|
||||
{
|
||||
gchar *str = g_strdup (data);
|
||||
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,
|
||||
(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);
|
||||
g_free (str);
|
||||
g_free (msg1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_list_stringjoin (gconstpointer data)
|
||||
{
|
||||
GList *test = NULL;
|
||||
gchar *ret;
|
||||
|
||||
ret = gnc_g_list_stringjoin (NULL, NULL);
|
||||
g_assert_true (ret == NULL);
|
||||
|
||||
ret = gnc_g_list_stringjoin (NULL, ":");
|
||||
g_assert_true (ret == NULL);
|
||||
|
||||
test = g_list_prepend (test, "one");
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, NULL);
|
||||
g_assert_cmpstr (ret, ==, "one");
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, "");
|
||||
g_assert_cmpstr (ret, ==, "one");
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, ":");
|
||||
g_assert_cmpstr (ret, ==, "one");
|
||||
g_free (ret);
|
||||
|
||||
/* The following inserts a NULL 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, "two");
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, NULL);
|
||||
g_assert_cmpstr (ret, ==, "twoone");
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, "");
|
||||
g_assert_cmpstr (ret, ==, "twoone");
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, ":");
|
||||
g_assert_cmpstr (ret, ==, "two:one");
|
||||
g_free (ret);
|
||||
|
||||
test = g_list_prepend (test, "three");
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, NULL);
|
||||
g_assert_cmpstr (ret, ==, "threetwoone");
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, "");
|
||||
g_assert_cmpstr (ret, ==, "threetwoone");
|
||||
g_free (ret);
|
||||
|
||||
ret = gnc_g_list_stringjoin (test, ":");
|
||||
g_assert_cmpstr (ret, ==, "three:two:one");
|
||||
g_free (ret);
|
||||
|
||||
g_list_free (test);
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_list_stringjoin_nodups (gconstpointer data)
|
||||
{
|
||||
GList *test = NULL;
|
||||
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_reverse (test);
|
||||
ret = gnc_g_list_stringjoin_nodups (test, ":");
|
||||
g_assert_cmpstr (ret, ==, "one:two:three:four");
|
||||
g_free (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
test_gnc_list_length (gconstpointer data)
|
||||
{
|
||||
GList *lst = NULL;
|
||||
|
||||
g_assert_true (gnc_list_length_cmp (lst, 0) == 0);
|
||||
g_assert_true (gnc_list_length_cmp (lst, 1) == -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, (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);
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
**********************************************************************/
|
||||
|
||||
#include "gncIDSearch.h"
|
||||
#include <gnc-glib-utils.h>
|
||||
#include <gnc-string-utils.h>
|
||||
|
||||
typedef enum
|
||||
{ UNDEFINED,
|
||||
|
||||
@@ -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"
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user