diff --git a/gnucash/import-export/aqb/gnc-ab-utils.c b/gnucash/import-export/aqb/gnc-ab-utils.c index 0d7b6e0d8a..e1b1d3a3d9 100644 --- a/gnucash/import-export/aqb/gnc-ab-utils.c +++ b/gnucash/import-export/aqb/gnc-ab-utils.c @@ -427,7 +427,7 @@ gnc_ab_description_to_gnc (const AB_TRANSACTION *ab_trans, gboolean is_ofx) acc = g_list_prepend (acc, gnc_ab_get_remote_name (ab_trans)); acc = g_list_prepend (acc, gnc_ab_get_purpose (ab_trans, is_ofx)); acc = g_list_prepend (acc, ab_ultimate_creditor_debtor_to_gnc (ab_trans, is_ofx)); - retval = gnc_g_list_stringjoin (acc, "; "); + retval = gnc_g_list_stringjoin_nodups (acc, "; "); g_list_free_full (acc, g_free); return retval ? retval : g_strdup (_("Unspecified")); diff --git a/libgnucash/core-utils/gnc-glib-utils.c b/libgnucash/core-utils/gnc-glib-utils.c index b2e55af29b..cd837ee55a 100644 --- a/libgnucash/core-utils/gnc-glib-utils.c +++ b/libgnucash/core-utils/gnc-glib-utils.c @@ -23,9 +23,11 @@ #include #include +#include #include #include #include +#include #include "gnc-glib-utils.h" @@ -287,9 +289,25 @@ gnc_g_list_cut(GList **list, GList *cut_point) cut_point->prev = NULL; } +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 + return true; +} -gchar * -gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep) +static gchar * +gnc_g_list_stringjoin_internal (GList *list_of_strings, const gchar *sep, bool testdups) { gint seplen = sep ? strlen(sep) : 0; gint length = -seplen; @@ -311,14 +329,31 @@ gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep) gchar *str = n->data; if (!str || !str[0]) continue; - if (sep && (p != retval)) - p = g_stpcpy (p, sep); - p = g_stpcpy (p, str); + if (!testdups || utf8_strstr (&str, retval)) + { + if (sep && (p != retval)) + p = g_stpcpy (p, sep); + p = g_stpcpy (p, str); + if (testdups) + g_free (str); + } } return retval; } +gchar * +gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep) +{ + return gnc_g_list_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); +} + gint gnc_list_length_cmp (const GList *list, size_t len) { diff --git a/libgnucash/core-utils/gnc-glib-utils.h b/libgnucash/core-utils/gnc-glib-utils.h index eb291336bb..a2770aaaf8 100644 --- a/libgnucash/core-utils/gnc-glib-utils.h +++ b/libgnucash/core-utils/gnc-glib-utils.h @@ -184,6 +184,19 @@ void gnc_g_list_cut(GList **list, GList *cut_point); * caller. **/ gchar * gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep); +/** + * @brief Like stringjoin but ensures that the string to be added isn't + * already part of the return string. + * + * @param list_of_strings A GList of chars* + * + * @param sep a separator or NULL + * + * @return A newly allocated string that has to be g_free'd by the + * caller. + **/ +gchar * gnc_g_list_stringjoin_nodups (GList *list_of_strings, const gchar *sep); + /** * @brief Scans the GList elements the minimum number of iterations * required to test it against a specified size. Returns -1, 0 or 1 diff --git a/libgnucash/core-utils/test/test-gnc-glib-utils.c b/libgnucash/core-utils/test/test-gnc-glib-utils.c index 43a74f1c4e..2f87d80f96 100644 --- a/libgnucash/core-utils/test/test-gnc-glib-utils.c +++ b/libgnucash/core-utils/test/test-gnc-glib-utils.c @@ -115,6 +115,24 @@ test_g_list_stringjoin (gconstpointer data) 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) { @@ -146,6 +164,7 @@ main (int argc, char *argv[]) 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();