gnucash stable: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Tue Oct 21 09:31:14 EDT 2025


Updated	 via  https://github.com/Gnucash/gnucash/commit/00149932 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5dad327f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c8348fa4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a3eefc8b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fcf2fc5f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5c5b32ab (commit)
	from  https://github.com/Gnucash/gnucash/commit/dc9b37ae (commit)



commit 001499322ecb13580fd11fb411cfac2b8fce5191
Merge: dc9b37aeff 5dad327f47
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Oct 21 21:30:07 2025 +0800

    Merge Chris Lam 'some-char-leaks' #2140 into stable


commit 5dad327f4711c02b5ef4389945447755707eb57f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Oct 14 23:58:05 2025 +0800

    [qoflog.cpp] plug a gchar* leak

diff --git a/libgnucash/engine/qoflog.cpp b/libgnucash/engine/qoflog.cpp
index 0da26d73fd..a70074ab4d 100644
--- a/libgnucash/engine/qoflog.cpp
+++ b/libgnucash/engine/qoflog.cpp
@@ -269,6 +269,12 @@ qof_log_shutdown (void)
         fout = nullptr;
     }
 
+    if (qof_logger_format)
+    {
+        g_free (qof_logger_format);
+        qof_logger_format = nullptr;
+    }
+
     if (function_buffer)
     {
         g_free(function_buffer);

commit c8348fa4cfdd2ed0e3d3ef9f45a730df43e71b64
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Oct 14 22:51:59 2025 +0800

    [translog.cpp] use c++ for better cleanup

diff --git a/libgnucash/engine/TransLog.cpp b/libgnucash/engine/TransLog.cpp
index 2145d22c8a..c66b6b7ce9 100644
--- a/libgnucash/engine/TransLog.cpp
+++ b/libgnucash/engine/TransLog.cpp
@@ -28,7 +28,8 @@
 #include <errno.h>
 #include <glib.h>
 #include <glib/gstdio.h>
-#include <string.h>
+#include <string>
+#include <fstream>
 
 #include "Account.h"
 #include "Transaction.h"
@@ -85,9 +86,9 @@ static QofLogModule log_module = "gnc.translog";
 
 
 static int gen_logs = 1;
-static FILE * trans_log = nullptr; /**< current log file handle */
-static char * trans_log_name = nullptr; /**< current log file name */
-static char * log_base_name = nullptr;
+static std::ofstream trans_log_stream; /**< current log file handle */
+static std::string trans_log_name; /**< current log file name */
+static std::string log_base_name;
 
 /********************************************************************\
 \********************************************************************/
@@ -107,7 +108,7 @@ void xaccLogEnable  (void)
 void
 xaccReopenLog (void)
 {
-    if (trans_log)
+    if (trans_log_stream.is_open())
     {
         xaccCloseLog();
         xaccOpenLog();
@@ -120,10 +121,9 @@ xaccLogSetBaseName (const char *basepath)
 {
     if (!basepath) return;
 
-    g_free (log_base_name);
-    log_base_name = g_strdup (basepath);
+    log_base_name = basepath;
 
-    if (trans_log)
+    if (trans_log_stream.is_open())
     {
         xaccCloseLog();
         xaccOpenLog();
@@ -141,13 +141,12 @@ gboolean
 xaccFileIsCurrentLog (const gchar *name)
 {
     gchar *base;
-    gint result;
 
-    if (!name || !trans_log_name)
+    if (!name || trans_log_name.empty())
         return FALSE;
 
     base = g_path_get_basename(name);
-    result = (strcmp(base, trans_log_name) == 0);
+    bool result = trans_log_name.compare(base) == 0;
     g_free(base);
     return result;
 }
@@ -166,17 +165,18 @@ xaccOpenLog (void)
 	 PINFO ("Attempt to open disabled transaction log");
 	 return;
     }
-    if (trans_log) return;
+    if (trans_log_stream.is_open()) return;
 
-    if (!log_base_name) log_base_name = g_strdup ("translog");
+    if (log_base_name.empty())
+        log_base_name = "translog";
 
     /* tag each filename with a timestamp */
     timestamp = gnc_date_timestamp ();
 
-    filename = g_strconcat (log_base_name, ".", timestamp, ".log", nullptr);
+    filename = g_strconcat (log_base_name.c_str(), ".", timestamp, ".log", nullptr);
 
-    trans_log = g_fopen (filename, "a");
-    if (!trans_log)
+    trans_log_stream.open(filename, std::ios::app);
+    if (!trans_log_stream.is_open())
     {
         int norr = errno;
         printf ("Error: xaccOpenLog(): cannot open journal\n"
@@ -188,20 +188,20 @@ xaccOpenLog (void)
     }
 
     /* Save the log file name */
-    if (trans_log_name)
-        g_free (trans_log_name);
-    trans_log_name = g_path_get_basename(filename);
+    auto tmpstr = g_path_get_basename(filename);
+    trans_log_name = tmpstr;
+    g_free (tmpstr);
 
     g_free (filename);
     g_free (timestamp);
 
     /*  Note: this must match src/import-export/log-replay/gnc-log-replay.c */
-    fprintf (trans_log, "mod\ttrans_guid\tsplit_guid\ttime_now\t"
-             "date_entered\tdate_posted\t"
-             "acc_guid\tacc_name\tnum\tdescription\t"
-             "notes\tmemo\taction\treconciled\t"
-             "amount\tvalue\tdate_reconciled\n");
-    fprintf (trans_log, "-----------------\n");
+    trans_log_stream << "mod\ttrans_guid\tsplit_guid\ttime_now\t"
+                     << "date_entered\tdate_posted\t"
+                     << "acc_guid\tacc_name\tnum\tdescription\t"
+                     << "notes\tmemo\taction\treconciled\t"
+                     << "amount\tvalue\tdate_reconciled\n"
+                     << "-----------------\n";
 }
 
 /********************************************************************\
@@ -210,10 +210,9 @@ xaccOpenLog (void)
 void
 xaccCloseLog (void)
 {
-    if (!trans_log) return;
-    fflush (trans_log);
-    fclose (trans_log);
-    trans_log = nullptr;
+    if (!trans_log_stream.is_open()) return;
+    trans_log_stream.flush();
+    trans_log_stream.close();
 }
 
 /********************************************************************\
@@ -233,14 +232,14 @@ xaccTransWriteLog (Transaction *trans, char flag)
          PINFO ("Attempt to write disabled transaction log");
 	 return;
     }
-    if (!trans_log) return;
+    if (!trans_log_stream.is_open()) return;
 
     gnc_time64_to_iso8601_buff (gnc_time(nullptr), dnow);
     gnc_time64_to_iso8601_buff (trans->date_entered, dent);
     gnc_time64_to_iso8601_buff (trans->date_posted, dpost);
     guid_to_string_buff (xaccTransGetGUID(trans), trans_guid_str);
     trans_notes = xaccTransGetNotes(trans);
-    fprintf (trans_log, "===== START\n");
+    trans_log_stream << "===== START\n";
 
     for (node = trans->splits; node; node = node->next)
     {
@@ -266,37 +265,26 @@ xaccTransWriteLog (Transaction *trans, char flag)
         amt = xaccSplitGetAmount (split);
         val = xaccSplitGetValue (split);
 
-        /* use tab-separated fields */
-        fprintf (trans_log,
-                 "%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
-                 "%s\t%s\t%s\t%s\t%c\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT "\t%s\n",
-                 flag,
-                 trans_guid_str, split_guid_str,  /* trans+split make up unique id */
-                 /* Note that the next three strings always exist,
-                 		* so we don't need to test them. */
-                 dnow,
-                 dent,
-                 dpost,
-                 acc_guid_str,
-                 accname ? accname : "",
-                 trans->num ? trans->num : "",
-                 trans->description ? trans->description : "",
-                 trans_notes ? trans_notes : "",
-                 split->memo ? split->memo : "",
-                 split->action ? split->action : "",
-                 split->reconciled,
-                 gnc_numeric_num(amt),
-                 gnc_numeric_denom(amt),
-                 gnc_numeric_num(val),
-                 gnc_numeric_denom(val),
-                 /* The next string always exists. No need to test it. */
-                 drecn);
+        trans_log_stream << flag << '\t'
+                         << trans_guid_str << '\t'
+                         << split_guid_str << '\t'
+                         << dnow << '\t'
+                         << dent << '\t'
+                         << dpost << '\t'
+                         << acc_guid_str << '\t'
+                         << (accname ? accname : "") << '\t'
+                         << (trans->num ? trans->num : "") << '\t'
+                         << (trans->description ? trans->description : "") << '\t'
+                         << (trans_notes ? trans_notes : "") << '\t'
+                         << (split->memo ? split->memo : "") << '\t'
+                         << (split->action ? split->action : "") << '\t'
+                         << split->reconciled << '\t'
+                         << gnc_numeric_num(amt) << '/' << gnc_numeric_denom(amt) << '\t'
+                         << gnc_numeric_num(val) << '/' << gnc_numeric_denom(val) << '\t'
+                         << drecn << '\n';
     }
 
-    fprintf (trans_log, "===== END\n");
-
-    /* get data out to the disk */
-    fflush (trans_log);
+    trans_log_stream << "===== END" << std::endl;
 }
 
 /************************ END OF ************************************\

commit a3eefc8b12f34b516e7acc9da0789dd879b69f00
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Oct 14 20:05:48 2025 +0800

    [import-main-matcher.cpp] plug GtkWidget* and static char* leaks
    
    static char* strbuf would never be completely freed upon exit. store a
    std::string->std::string map within the gui object, return the
    c_str(). also plug GtkWidget* leak.

diff --git a/gnucash/import-export/import-main-matcher.cpp b/gnucash/import-export/import-main-matcher.cpp
index c45d5dbd04..49a2036d0f 100644
--- a/gnucash/import-export/import-main-matcher.cpp
+++ b/gnucash/import-export/import-main-matcher.cpp
@@ -41,6 +41,8 @@
 #include <memory>
 #include <algorithm>
 #include <vector>
+#include <unordered_map>
+#include <string>
 
 #include "import-main-matcher.h"
 
@@ -64,6 +66,8 @@
 #define GNC_PREFS_GROUP "dialogs.import.generic.transaction-list"
 #define IMPORT_MAIN_MATCHER_CM_CLASS "transaction-matcher-dialog"
 
+using StrStrMap = std::unordered_map<std::string,std::string>;
+
 struct _main_matcher_info
 {
     GtkWidget *main_widget;
@@ -96,6 +100,7 @@ struct _main_matcher_info
     GHashTable *memo_hash;
 
     GList *new_strings;
+    StrStrMap colormap;
 };
 
 enum downloaded_cols
@@ -242,6 +247,7 @@ gnc_gen_trans_list_delete (GNCImportMainMatcher *info)
     g_hash_table_destroy (info->desc_hash);
     g_hash_table_destroy (info->notes_hash);
     g_hash_table_destroy (info->memo_hash);
+    info->colormap.~StrStrMap();
 
     g_list_free_full (info->new_strings, (GDestroyNotify)g_free);
 
@@ -1737,6 +1743,7 @@ gnc_gen_trans_common_setup (GNCImportMainMatcher *info,
     info->memo_hash = g_hash_table_new (g_str_hash, g_str_equal);
     info->new_strings = NULL;
     info->transaction_processed_cb = NULL;
+    new (&info->colormap) StrStrMap();
 
     /* Connect the signals */
     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, info);
@@ -1861,18 +1868,22 @@ gnc_gen_trans_list_run (GNCImportMainMatcher *info)
 }
 
 static const gchar*
-get_required_color (const gchar *class_name)
+get_required_color (StrStrMap& cache, const gchar *class_name)
 {
-    GdkRGBA color;
-    GtkWidget *label = gtk_label_new ("Color");
-    GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(label));
-    gtk_style_context_add_class (context, class_name);
-    gnc_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &color);
-    static gchar *strbuf = NULL;
-    if (strbuf)
-        g_free (strbuf);
-    strbuf = gdk_rgba_to_string (&color);
-    return strbuf;
+    auto& rv = cache[class_name];
+    if (rv.empty())
+    {
+        GdkRGBA color;
+        GtkWidget *label = gtk_label_new ("Color");
+        GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(label));
+        gtk_style_context_add_class (context, class_name);
+        gnc_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &color);
+        gchar* col_str = gdk_rgba_to_string (&color);
+        rv = col_str;
+        g_free (col_str);
+        gtk_widget_destroy (label);
+    }
+    return rv.c_str();
 }
 
 static void
@@ -2010,7 +2021,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
         if (gnc_import_TransInfo_is_balanced (info))
         {
             ro_text = _("New, already balanced");
-            color = get_required_color (int_not_required_class);
+            color = get_required_color (gui->colormap, int_not_required_class);
         }
         else
         {
@@ -2025,7 +2036,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
                     GNCPrintAmountInfo pinfo = gnc_commodity_print_info (
                         xaccAccountGetCommodity (dest_acc), true);
                     imbalance = g_strdup (xaccPrintAmount (bal_amt, pinfo));
-                    color = get_required_color (int_not_required_class);
+                    color = get_required_color (gui->colormap, int_not_required_class);
                     if (gnc_import_TransInfo_get_destacc_selected_manually (info))
                     {
                         text =
@@ -2049,7 +2060,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
                         xaccTransGetCurrency (gnc_import_TransInfo_get_trans (info)), true);
                     gnc_numeric bal_val = gnc_import_TransInfo_get_dest_value (info);
                     imbalance = g_strdup (xaccPrintAmount (bal_val, pinfo));
-                    color = get_required_color (int_required_class);
+                    color = get_required_color (gui->colormap, int_required_class);
                     text =
                     /* Translators: %s is the amount to be transferred. */
                     g_strdup_printf (_("New, UNBALANCED (need price to transfer %s to acct %s)!"),
@@ -2065,7 +2076,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
                     xaccTransGetCurrency (gnc_import_TransInfo_get_trans (info)), true);
                 gnc_numeric bal_val = gnc_import_TransInfo_get_dest_value (info);
                 imbalance = g_strdup (xaccPrintAmount (bal_val, pinfo));
-                color = get_required_color (int_prob_required_class);
+                color = get_required_color (gui->colormap, int_prob_required_class);
                 text =
                     /* Translators: %s is the amount to be transferred. */
                     g_strdup_printf (_("New, UNBALANCED (need acct to transfer %s)!"),
@@ -2083,7 +2094,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
             if (sel_match)
             {
                 gchar *full_names = get_peer_acct_names (sel_match->split);
-                color = get_required_color (int_not_required_class);
+                color = get_required_color (gui->colormap, int_not_required_class);
                 if (gnc_import_TransInfo_get_match_selected_manually (info))
                 {
                     text = g_strdup_printf (_("Reconcile (manual) match to %s"),
@@ -2099,7 +2110,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
             }
             else
             {
-                color = get_required_color (int_required_class);
+                color = get_required_color (gui->colormap, int_required_class);
                 ro_text = _("Match missing!");
                 show_pixbuf = false;
                 remove_child_row (model, iter);
@@ -2112,7 +2123,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
             if (sel_match)
             {
                 gchar *full_names = get_peer_acct_names (sel_match->split);
-                color = get_required_color (int_not_required_class);
+                color = get_required_color (gui->colormap, int_not_required_class);
                 if (gnc_import_TransInfo_get_match_selected_manually (info))
                 {
                     text = g_strdup_printf (_("Update and reconcile (manual) match to %s"),
@@ -2128,7 +2139,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
             }
             else
             {
-                color = get_required_color (int_required_class);
+                color = get_required_color (gui->colormap, int_required_class);
                 ro_text = _("Match missing!");
                 show_pixbuf = false;
                 remove_child_row (model, iter);
@@ -2136,7 +2147,7 @@ refresh_model_row (GNCImportMainMatcher *gui,
         }
         break;
     case GNCImport_SKIP:
-        color = get_required_color (int_required_class);
+        color = get_required_color (gui->colormap, int_required_class);
         ro_text = _("Do not import (no action selected)");
         show_pixbuf = false;
         remove_child_row (model, iter);

commit fcf2fc5f85a8d6de992d277478415f8f29d37bc5
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Oct 14 19:43:49 2025 +0800

    [gnc-prefs.cpp] plug gchar* leak
    
    namespace_regexp was never freed upon shutdown

diff --git a/libgnucash/core-utils/gnc-prefs.cpp b/libgnucash/core-utils/gnc-prefs.cpp
index f3d8503454..e94e5b7f7c 100644
--- a/libgnucash/core-utils/gnc-prefs.cpp
+++ b/libgnucash/core-utils/gnc-prefs.cpp
@@ -21,6 +21,8 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org
  */
 
+#include <string>
+
 #include <stdlib.h>
 #include <glib.h>
 #include <config.h>
@@ -28,7 +30,7 @@
 #include "gnc-prefs-p.h"
 #include "gnc-version.h"
 
-static gchar *namespace_regexp    = NULL;
+static std::string namespace_regexp;
 static gboolean is_debugging      = FALSE;
 static gboolean extras_enabled    = FALSE;
 static gboolean use_compression   = TRUE; // This is also the default in the prefs backend
@@ -46,17 +48,14 @@ PrefsBackend *prefsbackend = NULL;
 const gchar *
 gnc_prefs_get_namespace_regexp(void)
 {
-    return namespace_regexp;
+    return namespace_regexp.c_str();
 }
 
 void
 gnc_prefs_set_namespace_regexp(const gchar *str)
 {
-    if (namespace_regexp)
-        g_free(namespace_regexp);
-
     if (str)
-        namespace_regexp = g_strdup(str);
+        namespace_regexp = str;
 }
 
 gboolean

commit 5c5b32ab61c95cfe04a727f8e7a7df5de9f1c519
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Oct 14 19:41:38 2025 +0800

    [gnc-prefs.cpp] convert to c++

diff --git a/libgnucash/core-utils/CMakeLists.txt b/libgnucash/core-utils/CMakeLists.txt
index 4cd6d972cc..4fc2ad7ff1 100644
--- a/libgnucash/core-utils/CMakeLists.txt
+++ b/libgnucash/core-utils/CMakeLists.txt
@@ -6,7 +6,7 @@ add_subdirectory(test)
 ### libgnc-core-utils
 set (core_utils_SOURCES
   binreloc.c
-  gnc-prefs.c
+  gnc-prefs.cpp
   gnc-environment.c
   gnc-filepath-utils.cpp
   gnc-gkeyfile-utils.c
diff --git a/libgnucash/core-utils/gnc-prefs.c b/libgnucash/core-utils/gnc-prefs.cpp
similarity index 100%
rename from libgnucash/core-utils/gnc-prefs.c
rename to libgnucash/core-utils/gnc-prefs.cpp
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 14fc124d62..0296ee112f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -619,7 +619,7 @@ 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.c
+libgnucash/core-utils/gnc-prefs.cpp
 libgnucash/core-utils/gnc-unicode.cpp
 libgnucash/core-utils/gnc-version.c
 libgnucash/engine/Account.cpp



Summary of changes:
 gnucash/import-export/import-main-matcher.cpp      |  51 ++++++----
 libgnucash/core-utils/CMakeLists.txt               |   2 +-
 .../core-utils/{gnc-prefs.c => gnc-prefs.cpp}      |  11 +--
 libgnucash/engine/TransLog.cpp                     | 108 +++++++++------------
 libgnucash/engine/qoflog.cpp                       |   6 ++
 po/POTFILES.in                                     |   2 +-
 6 files changed, 92 insertions(+), 88 deletions(-)
 rename libgnucash/core-utils/{gnc-prefs.c => gnc-prefs.cpp} (98%)



More information about the gnucash-changes mailing list