mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 12:14:31 -06:00
b922cfd78d
Change to NFC normalization for all comparisons because the Unicode meaning of compatible might collide with the user's intent.
279 lines
7.4 KiB
C++
279 lines
7.4 KiB
C++
/********************************************************************\
|
|
* qofutil.c -- QOF utility functions *
|
|
* Copyright (C) 1997 Robin D. Clark *
|
|
* Copyright (C) 1997-2001,2004 Linas Vepstas <linas@linas.org> *
|
|
* Copyright 2006 Neil Williams <linux@codehelp.co.uk> *
|
|
* *
|
|
* 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 *
|
|
* *
|
|
* Author: Rob Clark (rclark@cs.hmc.edu) *
|
|
* Author: Linas Vepstas (linas@linas.org) *
|
|
\********************************************************************/
|
|
|
|
#include <config.h>
|
|
|
|
#include <ctype.h>
|
|
#include <glib.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "qof.h"
|
|
#include "qof-backend.hpp"
|
|
|
|
G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_UTIL;
|
|
|
|
void
|
|
g_hash_table_foreach_sorted(GHashTable *hash_table, GHFunc func, gpointer user_data, GCompareFunc compare_func)
|
|
{
|
|
GList *iter;
|
|
GList *keys = g_list_sort(g_hash_table_get_keys(hash_table), compare_func);
|
|
|
|
for (iter = keys; iter; iter = iter->next)
|
|
{
|
|
func(iter->data, g_hash_table_lookup(hash_table, iter->data), user_data);
|
|
}
|
|
|
|
g_list_free(keys);
|
|
}
|
|
|
|
gboolean
|
|
qof_utf8_substr_nocase (const gchar *haystack, const gchar *needle)
|
|
{
|
|
gchar *haystack_casefold, *haystack_normalized;
|
|
gchar *needle_casefold, *needle_normalized;
|
|
gchar *p;
|
|
|
|
g_return_val_if_fail (haystack && needle, FALSE);
|
|
|
|
haystack_casefold = g_utf8_casefold (haystack, -1);
|
|
haystack_normalized = g_utf8_normalize (haystack_casefold, -1,
|
|
G_NORMALIZE_NFC);
|
|
g_free (haystack_casefold);
|
|
|
|
needle_casefold = g_utf8_casefold (needle, -1);
|
|
needle_normalized = g_utf8_normalize (needle_casefold, -1, G_NORMALIZE_NFC);
|
|
g_free (needle_casefold);
|
|
|
|
p = strstr (haystack_normalized, needle_normalized);
|
|
g_free (haystack_normalized);
|
|
g_free (needle_normalized);
|
|
|
|
return p != NULL;
|
|
}
|
|
|
|
/** Use g_utf8_casefold and g_utf8_collate to compare two utf8 strings,
|
|
* ignore case. Return < 0 if da compares before db, 0 if they compare
|
|
* equal, > 0 if da compares after db. */
|
|
static gint
|
|
qof_utf8_strcasecmp (const gchar *da, const gchar *db)
|
|
{
|
|
gchar *da_casefold, *db_casefold;
|
|
gint retval;
|
|
|
|
g_return_val_if_fail (da != NULL, 0);
|
|
g_return_val_if_fail (db != NULL, 0);
|
|
|
|
da_casefold = g_utf8_casefold (da, -1);
|
|
db_casefold = g_utf8_casefold (db, -1);
|
|
retval = g_utf8_collate (da_casefold, db_casefold);
|
|
g_free (da_casefold);
|
|
g_free (db_casefold);
|
|
|
|
return retval;
|
|
}
|
|
|
|
gint
|
|
safe_strcasecmp (const gchar * da, const gchar * db)
|
|
{
|
|
if ((da) && (db))
|
|
{
|
|
if ((da) != (db))
|
|
{
|
|
gint retval = qof_utf8_strcasecmp ((da), (db));
|
|
/* if strings differ, return */
|
|
if (retval) return retval;
|
|
}
|
|
}
|
|
else if ((!(da)) && (db))
|
|
{
|
|
return -1;
|
|
}
|
|
else if ((da) && (!(db)))
|
|
{
|
|
return +1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
gint
|
|
null_strcmp (const gchar * da, const gchar * db)
|
|
{
|
|
if (da && db) return strcmp (da, db);
|
|
if (!da && db && 0 == db[0]) return 0;
|
|
if (!db && da && 0 == da[0]) return 0;
|
|
if (!da && db) return -1;
|
|
if (da && !db) return +1;
|
|
return 0;
|
|
}
|
|
|
|
#define MAX_DIGITS 50
|
|
|
|
/* inverse of strtoul */
|
|
gchar *
|
|
ultostr (gulong val, gint base)
|
|
{
|
|
gchar buf[MAX_DIGITS];
|
|
gulong broke[MAX_DIGITS];
|
|
gint i;
|
|
gulong places = 0, reval;
|
|
|
|
if ((2 > base) || (36 < base)) return NULL;
|
|
|
|
/* count digits */
|
|
places = 0;
|
|
for (i = 0; i < MAX_DIGITS; i++)
|
|
{
|
|
broke[i] = val;
|
|
places ++;
|
|
val /= base;
|
|
if (0 == val) break;
|
|
}
|
|
|
|
/* normalize */
|
|
reval = 0;
|
|
for (i = places - 2; i >= 0; i--)
|
|
{
|
|
reval += broke[i+1];
|
|
reval *= base;
|
|
broke[i] -= reval;
|
|
}
|
|
|
|
/* print */
|
|
for (i = 0; i < (gint)places; i++)
|
|
{
|
|
if (10 > broke[i])
|
|
{
|
|
buf[places-1-i] = 0x30 + broke[i]; /* ascii digit zero */
|
|
}
|
|
else
|
|
{
|
|
buf[places-1-i] = 0x41 - 10 + broke[i]; /* ascii capital A */
|
|
}
|
|
}
|
|
buf[places] = 0x0;
|
|
|
|
return g_strdup (buf);
|
|
}
|
|
|
|
/* =================================================================== */
|
|
/* returns TRUE if the string is a number, possibly with whitespace */
|
|
/* =================================================================== */
|
|
|
|
gboolean
|
|
gnc_strisnum(const gchar *s)
|
|
{
|
|
if (s == NULL) return FALSE;
|
|
if (*s == 0) return FALSE;
|
|
|
|
while (*s && isspace(*s))
|
|
s++;
|
|
|
|
if (*s == 0) return FALSE;
|
|
if (!isdigit(*s)) return FALSE;
|
|
|
|
while (*s && isdigit(*s))
|
|
s++;
|
|
|
|
if (*s == 0) return TRUE;
|
|
|
|
while (*s && isspace(*s))
|
|
s++;
|
|
|
|
if (*s == 0) return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* =================================================================== */
|
|
/* Return NULL if the field is whitespace (blank, tab, formfeed etc.)
|
|
* Else return pointer to first non-whitespace character. */
|
|
/* =================================================================== */
|
|
|
|
G_GNUC_UNUSED static const gchar *
|
|
qof_util_whitespace_filter (const gchar * val)
|
|
{
|
|
size_t len;
|
|
if (!val) return NULL;
|
|
|
|
len = strspn (val, "\a\b\t\n\v\f\r ");
|
|
if (0 == val[len]) return NULL;
|
|
return val + len;
|
|
}
|
|
|
|
#ifdef THESE_CAN_BE_USEFUL_FOR_DEGUGGING
|
|
static guint g_str_hash_KEY(gconstpointer v)
|
|
{
|
|
return g_str_hash(v);
|
|
}
|
|
static guint g_str_hash_VAL(gconstpointer v)
|
|
{
|
|
return g_str_hash(v);
|
|
}
|
|
static gpointer g_strdup_VAL(gpointer v)
|
|
{
|
|
return g_strdup(v);
|
|
}
|
|
static gpointer g_strdup_KEY(gpointer v)
|
|
{
|
|
return g_strdup(v);
|
|
}
|
|
static void g_free_VAL(gpointer v)
|
|
{
|
|
return g_free(v);
|
|
}
|
|
static void g_free_KEY(gpointer v)
|
|
{
|
|
return g_free(v);
|
|
}
|
|
static gboolean qof_util_str_equal(gconstpointer v, gconstpointer v2)
|
|
{
|
|
return (v && v2) ? g_str_equal(v, v2) : FALSE;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
qof_init (void)
|
|
{
|
|
qof_log_init();
|
|
qof_string_cache_init();
|
|
qof_object_initialize ();
|
|
qof_query_init ();
|
|
qof_book_register ();
|
|
}
|
|
|
|
void
|
|
qof_close(void)
|
|
{
|
|
qof_query_shutdown ();
|
|
qof_object_shutdown ();
|
|
QofBackend::release_backends();
|
|
qof_string_cache_destroy ();
|
|
qof_log_shutdown();
|
|
}
|
|
|
|
/* ************************ END OF FILE ***************************** */
|