mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Replace gnc-engine-util with qofutil
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@13745 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -12,7 +12,6 @@ AM_CFLAGS = \
|
||||
libqof_la_SOURCES = \
|
||||
deprecated.c \
|
||||
gnc-date.c \
|
||||
gnc-engine-util.c \
|
||||
gnc-numeric.c \
|
||||
guid.c \
|
||||
kvp_frame.c \
|
||||
@@ -30,6 +29,7 @@ libqof_la_SOURCES = \
|
||||
qofobject.c \
|
||||
qofquerycore.c \
|
||||
qofreference.c \
|
||||
qofutil.c \
|
||||
qofsession.c \
|
||||
qofbookmerge.c
|
||||
|
||||
@@ -38,14 +38,12 @@ qofincludedir = ${pkgincludedir}
|
||||
qofinclude_HEADERS = \
|
||||
deprecated.h \
|
||||
gnc-date.h \
|
||||
gnc-engine-util.h \
|
||||
gnc-numeric.h \
|
||||
guid.h \
|
||||
kvp_frame.h \
|
||||
kvp-util.h \
|
||||
kvp-util-p.h \
|
||||
qof.h \
|
||||
qof-be-utils.h \
|
||||
qofbackend.h \
|
||||
qofbackend-p.h \
|
||||
qofclass.h \
|
||||
@@ -64,6 +62,7 @@ qofinclude_HEADERS = \
|
||||
qofreference.h \
|
||||
qofsession.h \
|
||||
qofsql.h \
|
||||
qofutil.h \
|
||||
qofbookmerge.h
|
||||
|
||||
nodist_qofinclude_HEADERS = \
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
/********************************************************************\
|
||||
* gnc-engine-util.c -- QOF utility functions *
|
||||
* Copyright (C) 1997 Robin D. Clark *
|
||||
* Copyright (C) 1997-2001,2004 Linas Vepstas <linas@linas.org> *
|
||||
* *
|
||||
* 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 "gnc-engine-util.h"
|
||||
|
||||
|
||||
/********************************************************************\
|
||||
\********************************************************************/
|
||||
|
||||
/* Search for str2 in first nchar chars of str1, ignore case.. Return
|
||||
* pointer to first match, or null. */
|
||||
char *
|
||||
strncasestr(const unsigned char *str1, const unsigned char *str2, size_t len)
|
||||
{
|
||||
while (*str1 && len--)
|
||||
{
|
||||
if (toupper(*str1) == toupper(*str2))
|
||||
{
|
||||
if (strncasecmp(str1,str2,strlen(str2)) == 0)
|
||||
{
|
||||
return (char *) str1;
|
||||
}
|
||||
}
|
||||
str1++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for str2 in str1, ignore case. Return pointer to first
|
||||
* match, or null. */
|
||||
char *
|
||||
strcasestr(const char *str1, const char *str2)
|
||||
{
|
||||
size_t len = strlen (str1);
|
||||
char * retval = strncasestr (str1, str2, len);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
\********************************************************************/
|
||||
|
||||
int
|
||||
safe_strcmp (const char * da, const char * db)
|
||||
{
|
||||
SAFE_STRCMP (da, db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
safe_strcasecmp (const char * da, const char * db)
|
||||
{
|
||||
SAFE_STRCASECMP (da, db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
null_strcmp (const char * da, const char * 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 */
|
||||
char *
|
||||
ultostr (unsigned long val, int base)
|
||||
{
|
||||
char buf[MAX_DIGITS];
|
||||
unsigned long broke[MAX_DIGITS];
|
||||
int i;
|
||||
unsigned long 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<(int)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 unsigned char *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;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* our own version of stpcpy
|
||||
\********************************************************************/
|
||||
|
||||
char *
|
||||
gnc_stpcpy (char *dest, const char *src)
|
||||
{
|
||||
strcpy (dest, src);
|
||||
return (dest + strlen (src));
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* Return NULL if the field is whitespace (blank, tab, formfeed etc.)
|
||||
* Else return pointer to first non-whitespace character. */
|
||||
|
||||
const char *
|
||||
qof_util_whitespace_filter (const char * 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;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* Return integer 1 if the string starts with 't' or 'T' or contains the
|
||||
* word 'true' or 'TRUE'; if string is a number, return that number. */
|
||||
|
||||
int
|
||||
qof_util_bool_to_int (const char * val)
|
||||
{
|
||||
const char * p = qof_util_whitespace_filter (val);
|
||||
if (!p) return 0;
|
||||
if ('t' == p[0]) return 1;
|
||||
if ('T' == p[0]) return 1;
|
||||
if ('y' == p[0]) return 1;
|
||||
if ('Y' == p[0]) return 1;
|
||||
if (strstr (p, "true")) return 1;
|
||||
if (strstr (p, "TRUE")) return 1;
|
||||
if (strstr (p, "yes")) return 1;
|
||||
if (strstr (p, "YES")) return 1;
|
||||
return atoi (val);
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
* The engine string cache
|
||||
\********************************************************************/
|
||||
|
||||
static GCache * gnc_string_cache = NULL;
|
||||
|
||||
#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 gnc_str_equal(gconstpointer v, gconstpointer v2)
|
||||
{
|
||||
return (v && v2) ? g_str_equal(v, v2) : FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
GCache*
|
||||
gnc_engine_get_string_cache(void)
|
||||
{
|
||||
if(!gnc_string_cache) {
|
||||
gnc_string_cache = g_cache_new(
|
||||
(GCacheNewFunc) g_strdup, /* value_new_func */
|
||||
g_free, /* value_destroy_func */
|
||||
(GCacheDupFunc) g_strdup, /* key_dup_func */
|
||||
g_free, /* key_destroy_func */
|
||||
g_str_hash, /* hash_key_func */
|
||||
g_str_hash, /* hash_value_func */
|
||||
g_str_equal); /* key_equal_func */
|
||||
}
|
||||
return gnc_string_cache;
|
||||
}
|
||||
|
||||
void
|
||||
gnc_engine_string_cache_destroy (void)
|
||||
{
|
||||
if (gnc_string_cache)
|
||||
g_cache_destroy (gnc_string_cache);
|
||||
gnc_string_cache = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gnc_string_cache_remove(gconstpointer key)
|
||||
{
|
||||
if (key)
|
||||
g_cache_remove(gnc_engine_get_string_cache(), key);
|
||||
}
|
||||
|
||||
/* TODO: It would be better if this returned gpointerconst. The
|
||||
returned strings really should be treated as const. Callers must
|
||||
not modify them. */
|
||||
gpointer
|
||||
gnc_string_cache_insert(gconstpointer key)
|
||||
{
|
||||
if (key)
|
||||
return g_cache_insert(gnc_engine_get_string_cache(), (gpointer)key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
qof_init (void)
|
||||
{
|
||||
gnc_engine_get_string_cache ();
|
||||
guid_init ();
|
||||
qof_object_initialize ();
|
||||
qof_query_init ();
|
||||
qof_book_register ();
|
||||
}
|
||||
|
||||
void
|
||||
qof_close(void)
|
||||
{
|
||||
qof_query_shutdown ();
|
||||
qof_object_shutdown ();
|
||||
guid_shutdown ();
|
||||
gnc_engine_string_cache_destroy ();
|
||||
}
|
||||
|
||||
|
||||
/************************* END OF FILE ******************************\
|
||||
\********************************************************************/
|
||||
@@ -1,326 +0,0 @@
|
||||
/********************************************************************\
|
||||
* gnc-engine-util.h -- QOF utility functions *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
|
||||
/** @addtogroup Utilities
|
||||
@{ */
|
||||
/** @file gnc-engine-util.h
|
||||
@brief QOF utility functions
|
||||
(This file is due to be renamed qofutil.h in libqof2.
|
||||
It will remain as a placeholder during libqof1.)
|
||||
@author Copyright (C) 1997 Robin D. Clark <rclark@cs.hmc.edu>
|
||||
@author Copyright (C) 2000 Bill Gribble <grib@billgribble.com>
|
||||
@author Copyright (C) 1997-2002,2004 Linas Vepstas <linas@linas.org>
|
||||
*/
|
||||
|
||||
#ifndef QOF_UTIL_H
|
||||
#define QOF_UTIL_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stddef.h>
|
||||
#include "qof.h"
|
||||
|
||||
/* Macros *****************************************************/
|
||||
|
||||
/** \deprecated Use the function versions, safe_strcmp() and
|
||||
safe_strcasecmp(). These macros will be made private in libqof2.
|
||||
|
||||
If the pointer arguments are equal, the macro does nothing and
|
||||
safe_strcmp / safe_strcasecmp return 0.
|
||||
|
||||
These macros change program control flow and are not good
|
||||
substitutes for the function equivalents.
|
||||
*/
|
||||
#define SAFE_STRCMP_REAL(fcn,da,db) { \
|
||||
if ((da) && (db)) { \
|
||||
if ((da) != (db)) { \
|
||||
int retval = fcn ((da), (db)); \
|
||||
/* if strings differ, return */ \
|
||||
if (retval) return retval; \
|
||||
} \
|
||||
} else \
|
||||
if ((!(da)) && (db)) { \
|
||||
return -1; \
|
||||
} else \
|
||||
if ((da) && (!(db))) { \
|
||||
return +1; \
|
||||
} \
|
||||
}
|
||||
|
||||
/** \deprecated use safe_strcmp() */
|
||||
#define SAFE_STRCMP(da,db) SAFE_STRCMP_REAL(strcmp,(da),(db))
|
||||
/** \deprecated use safe_strcasecmp() */
|
||||
#define SAFE_STRCASECMP(da,db) SAFE_STRCMP_REAL(strcasecmp,(da),(db))
|
||||
|
||||
/** \name typedef enum as string macros
|
||||
@{
|
||||
*/
|
||||
#define ENUM_BODY(name, value) \
|
||||
name value,
|
||||
|
||||
#define AS_STRING_CASE(name, value) \
|
||||
case name: { return #name; }
|
||||
|
||||
#define FROM_STRING_CASE(name, value) \
|
||||
if (strcmp(str, #name) == 0) { \
|
||||
return name; }
|
||||
|
||||
#define DEFINE_ENUM(name, list) \
|
||||
typedef enum { \
|
||||
list(ENUM_BODY) \
|
||||
}name;
|
||||
|
||||
#define AS_STRING_DEC(name, list) \
|
||||
const char* name##asString(name n);
|
||||
|
||||
#define AS_STRING_FUNC(name, list) \
|
||||
const char* name##asString(name n) { \
|
||||
switch (n) { \
|
||||
list(AS_STRING_CASE) \
|
||||
default: return ""; } }
|
||||
|
||||
#define FROM_STRING_DEC(name, list) \
|
||||
name name##fromString \
|
||||
(const char* str);
|
||||
|
||||
#define FROM_STRING_FUNC(name, list) \
|
||||
name name##fromString \
|
||||
(const char* str) { \
|
||||
if(str == NULL) { return 0; } \
|
||||
list(FROM_STRING_CASE) \
|
||||
return 0; }
|
||||
|
||||
/** @} */
|
||||
|
||||
/** \name enum as string with no typedef
|
||||
@{
|
||||
|
||||
Similar but used when the enum is NOT a typedef
|
||||
Make sure you use the DEFINE_ENUM_NON_TYPEDEF macro.
|
||||
|
||||
You can precede the FROM_STRING_FUNC_NON_TYPEDEF
|
||||
and AS_STRING_FUNC_NON_TYPEDEF macros with the
|
||||
keyword static if appropriate.
|
||||
|
||||
ENUM_BODY is used in both types.
|
||||
*/
|
||||
|
||||
#define DEFINE_ENUM_NON_TYPEDEF(name, list) \
|
||||
enum name { \
|
||||
list(ENUM_BODY) \
|
||||
};
|
||||
|
||||
#define FROM_STRING_DEC_NON_TYPEDEF(name, list) \
|
||||
void name##fromString \
|
||||
(const char* str, enum name *type);
|
||||
|
||||
#define FROM_STRING_CASE_NON_TYPEDEF(name, value) \
|
||||
if (strcmp(str, #name) == 0) { *type = name; }
|
||||
|
||||
#define FROM_STRING_FUNC_NON_TYPEDEF(name, list) \
|
||||
void name##fromString \
|
||||
(const char* str, enum name *type) { \
|
||||
if(str == NULL) { return; } \
|
||||
list(FROM_STRING_CASE_NON_TYPEDEF) }
|
||||
|
||||
#define AS_STRING_DEC_NON_TYPEDEF(name, list) \
|
||||
const char* name##asString(enum name n);
|
||||
|
||||
#define AS_STRING_FUNC_NON_TYPEDEF(name, list) \
|
||||
const char* name##asString(enum name n) { \
|
||||
switch (n) { \
|
||||
list(AS_STRING_CASE_NON_TYPEDEF) \
|
||||
default: return ""; } }
|
||||
|
||||
#define AS_STRING_CASE_NON_TYPEDEF(name, value) \
|
||||
case name: { return #name; }
|
||||
|
||||
/** @} */
|
||||
|
||||
/** \deprecated Define the long long int conversion for scanf
|
||||
* HAVE_SCANF_LLD will be removed from libqof2
|
||||
* */
|
||||
#if HAVE_SCANF_LLD
|
||||
# define GNC_SCANF_LLD "%lld" /**< \deprecated
|
||||
use G_GINT64_FORMAT instead. */
|
||||
#else
|
||||
# define GNC_SCANF_LLD "%qd" /**< \deprecated */
|
||||
#endif
|
||||
|
||||
/** @name Convenience wrappers
|
||||
@{
|
||||
*/
|
||||
|
||||
/** \brief Initialise the Query Object Framework
|
||||
|
||||
Use in place of separate init functions (like guid_init()
|
||||
and qof_query_init() etc.) to protect against future changes.
|
||||
*/
|
||||
void qof_init (void);
|
||||
|
||||
/** \brief Safely close down the Query Object Framework
|
||||
|
||||
Use in place of separate close / shutdown functions
|
||||
(like guid_shutdown(), qof_query_shutdown() etc.) to protect
|
||||
against future changes.
|
||||
*/
|
||||
void qof_close (void);
|
||||
|
||||
/** @} */
|
||||
|
||||
/* **** Prototypes *********************************************/
|
||||
|
||||
/** The safe_strcmp compares strings da and db the same way that strcmp()
|
||||
does, except that either may be null. This routine assumes that
|
||||
a non-null string is always greater than a null string.
|
||||
|
||||
@param da string 1.
|
||||
@param db string 2.
|
||||
|
||||
@return If da == NULL && db != NULL, returns -1.
|
||||
If da != NULL && db == NULL, returns +1.
|
||||
If da != NULL && db != NULL, returns the result of
|
||||
strcmp(da, db).
|
||||
If da == NULL && db == NULL, returns 0.
|
||||
*/
|
||||
int safe_strcmp (const char * da, const char * db);
|
||||
|
||||
/** case sensitive comparison of strings da and db - either
|
||||
may be NULL. A non-NULL string is greater than a NULL string.
|
||||
|
||||
@param da string 1.
|
||||
@param db string 2.
|
||||
|
||||
@return If da == NULL && db != NULL, returns -1.
|
||||
If da != NULL && db == NULL, returns +1.
|
||||
If da != NULL && db != NULL, returns the result of
|
||||
strcmp(da, db).
|
||||
If da == NULL && db == NULL, returns 0.
|
||||
*/
|
||||
int safe_strcasecmp (const char * da, const char * db);
|
||||
|
||||
/** The null_strcmp compares strings a and b the same way that strcmp()
|
||||
* does, except that either may be null. This routine assumes that
|
||||
* a null string is equal to the empty string.
|
||||
*/
|
||||
int null_strcmp (const char * da, const char * db);
|
||||
|
||||
/** Search for str2 in first nchar chars of str1, ignore case. Return
|
||||
* pointer to first match, or null. These are just like that strnstr
|
||||
* and the strstr functions, except that they ignore the case. */
|
||||
extern char *strncasestr(const unsigned char *str1, const unsigned char *str2,
|
||||
size_t len);
|
||||
extern char *strcasestr(const char *str1, const char *str2);
|
||||
|
||||
/** The ultostr() subroutine is the inverse of strtoul(). It accepts a
|
||||
* number and prints it in the indicated base. The returned string
|
||||
* should be g_freed when done. */
|
||||
char * ultostr (unsigned long val, int base);
|
||||
|
||||
/** Returns true if string s is a number, possibly surrounded by
|
||||
* whitespace. */
|
||||
gboolean gnc_strisnum(const unsigned char *s);
|
||||
|
||||
/** Local copy of stpcpy, used wtih libc's that don't have one. */
|
||||
char * gnc_stpcpy (char *dest, const char *src);
|
||||
|
||||
#ifndef HAVE_STPCPY
|
||||
#define stpcpy gnc_stpcpy
|
||||
#endif
|
||||
|
||||
/** Return NULL if the field is whitespace (blank, tab, formfeed etc.)
|
||||
* Else return pointer to first non-whitespace character.
|
||||
*/
|
||||
const char * qof_util_whitespace_filter (const char * val);
|
||||
|
||||
/** Return integer 1 if the string starts with 't' or 'T' or
|
||||
* contains the word 'true' or 'TRUE'; if string is a number,
|
||||
* return that number. (Leading whitespace is ignored). */
|
||||
int qof_util_bool_to_int (const char * val);
|
||||
|
||||
|
||||
/** The QOF String Cache:
|
||||
*
|
||||
* Many strings used throughout QOF and QOF applications are likely to
|
||||
* be duplicated.
|
||||
*
|
||||
* QOF provides a reference counted cache system for the strings, which
|
||||
* shares strings whenever possible.
|
||||
*
|
||||
* Use gnc_string_cache_insert to insert a string into the cache (it
|
||||
* will return a pointer to the cached string). Basically you should
|
||||
* use this instead of g_strdup.
|
||||
*
|
||||
* Use gnc_string_cache_remove (giving it a pointer to a cached
|
||||
* string) if the string is unused. If this is the last reference to
|
||||
* the string it will be removed from the cache, otherwise it will
|
||||
* just decrement the reference count. Basically you should use this
|
||||
* instead of g_free.
|
||||
*
|
||||
* Just in case it's not clear: The remove function must NOT be called
|
||||
* for the string you passed INTO the insert function. It must be
|
||||
* called for the _cached_ string that is _returned_ by the insert
|
||||
* function.
|
||||
*
|
||||
* Note that all the work is done when inserting or removing. Once
|
||||
* cached the strings are just plain C strings.
|
||||
*
|
||||
* The string cache is demand-created on first use.
|
||||
*
|
||||
**/
|
||||
|
||||
/** \deprecated use qof_init instead.
|
||||
|
||||
Get the gnc_string_cache. Create it if it doesn't exist already.
|
||||
*/
|
||||
GCache* gnc_engine_get_string_cache(void);
|
||||
|
||||
/** Destroy the gnc_string_cache */
|
||||
void gnc_engine_string_cache_destroy (void);
|
||||
|
||||
/** You can use this function as a destroy notifier for a GHashTable
|
||||
that uses common strings as keys (or values, for that matter.)
|
||||
*/
|
||||
void gnc_string_cache_remove(gconstpointer key);
|
||||
|
||||
/** You can use this function with g_hash_table_insert(), for the key
|
||||
(or value), as long as you use the destroy notifier above.
|
||||
*/
|
||||
gpointer gnc_string_cache_insert(gconstpointer key);
|
||||
|
||||
#define CACHE_INSERT(str) gnc_string_cache_insert((gconstpointer)(str))
|
||||
#define CACHE_REMOVE(str) gnc_string_cache_remove((str))
|
||||
|
||||
/* Replace cached string currently in 'dst' with string in 'src'.
|
||||
* Typical usage:
|
||||
* void foo_set_name(Foo *f, const char *str) {
|
||||
* CACHE_REPLACE(f->name, str);
|
||||
* }
|
||||
* It avoids unnecessary ejection by doing INSERT before REMOVE.
|
||||
*/
|
||||
#define CACHE_REPLACE(dst, src) do { \
|
||||
gpointer tmp = CACHE_INSERT((src)); \
|
||||
CACHE_REMOVE((dst)); \
|
||||
(dst) = tmp; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#endif /* QOF_UTIL_H */
|
||||
/** @} */
|
||||
@@ -80,7 +80,7 @@
|
||||
#include "qoflog.h"
|
||||
#include "gnc-date.h"
|
||||
#include "gnc-numeric.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "qofutil.h"
|
||||
#include "guid.h"
|
||||
#include "kvp_frame.h"
|
||||
#include "kvp-util.h"
|
||||
@@ -98,7 +98,6 @@
|
||||
#include "qofsql.h"
|
||||
#include "qofchoice.h"
|
||||
#include "qofbookmerge.h"
|
||||
#include "qof-be-utils.h"
|
||||
#include "qofreference.h"
|
||||
#include "qofla-dir.h"
|
||||
#include "deprecated.h"
|
||||
|
||||
547
lib/libqof/qof/qofutil.c
Normal file
547
lib/libqof/qof/qofutil.c
Normal file
@@ -0,0 +1,547 @@
|
||||
/********************************************************************\
|
||||
* 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"
|
||||
|
||||
static QofLogModule log_module = QOF_MOD_UTIL;
|
||||
|
||||
/* Search for str2 in first nchar chars of str1, ignore case.. Return
|
||||
* pointer to first match, or null. */
|
||||
gchar *
|
||||
strncasestr(const guchar *str1, const guchar *str2, size_t len)
|
||||
{
|
||||
while (*str1 && len--)
|
||||
{
|
||||
if (toupper(*str1) == toupper(*str2))
|
||||
{
|
||||
if (strncasecmp(str1,str2,strlen(str2)) == 0)
|
||||
{
|
||||
return (gchar *) str1;
|
||||
}
|
||||
}
|
||||
str1++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for str2 in str1, ignore case. Return pointer to first
|
||||
* match, or null. */
|
||||
gchar *
|
||||
strcasestr(const gchar *str1, const gchar *str2)
|
||||
{
|
||||
size_t len = strlen (str1);
|
||||
gchar * retval = strncasestr (str1, str2, len);
|
||||
return retval;
|
||||
}
|
||||
|
||||
gint
|
||||
safe_strcmp (const gchar * da, const gchar * db)
|
||||
{
|
||||
if ((da) && (db)) {
|
||||
if ((da) != (db)) {
|
||||
gint retval = strcmp ((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
|
||||
safe_strcasecmp (const gchar * da, const gchar * db)
|
||||
{
|
||||
if ((da) && (db)) {
|
||||
if ((da) != (db)) {
|
||||
gint retval = 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 guchar *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. */
|
||||
/* =================================================================== */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* Return integer 1 if the string starts with 't' or 'T' or contains the
|
||||
* word 'true' or 'TRUE'; if string is a number, return that number. */
|
||||
/* =================================================================== */
|
||||
|
||||
gint
|
||||
qof_util_bool_to_int (const gchar * val)
|
||||
{
|
||||
const gchar * p = qof_util_whitespace_filter (val);
|
||||
if (!p) return 0;
|
||||
if ('t' == p[0]) return 1;
|
||||
if ('T' == p[0]) return 1;
|
||||
if ('y' == p[0]) return 1;
|
||||
if ('Y' == p[0]) return 1;
|
||||
if (strstr (p, "true")) return 1;
|
||||
if (strstr (p, "TRUE")) return 1;
|
||||
if (strstr (p, "yes")) return 1;
|
||||
if (strstr (p, "YES")) return 1;
|
||||
return atoi (val);
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* Entity edit and commit utilities */
|
||||
/* =================================================================== */
|
||||
|
||||
gboolean
|
||||
qof_begin_edit(QofInstance *inst)
|
||||
{
|
||||
QofBackend * be;
|
||||
|
||||
if (!inst) { return FALSE; }
|
||||
(inst->editlevel)++;
|
||||
if (1 < inst->editlevel) { return FALSE; }
|
||||
if (0 >= inst->editlevel) { inst->editlevel = 1; }
|
||||
be = qof_book_get_backend (inst->book);
|
||||
if (be && qof_backend_begin_exists(be)) {
|
||||
qof_backend_run_begin(be, inst);
|
||||
} else { inst->dirty = TRUE; }
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean qof_commit_edit(QofInstance *inst)
|
||||
{
|
||||
QofBackend * be;
|
||||
|
||||
if (!inst) { return FALSE; }
|
||||
(inst->editlevel)--;
|
||||
if (0 < inst->editlevel) { return FALSE; }
|
||||
if ((-1 == inst->editlevel) && inst->dirty)
|
||||
{
|
||||
be = qof_book_get_backend ((inst)->book);
|
||||
if (be && qof_backend_begin_exists(be)) {
|
||||
qof_backend_run_begin(be, inst);
|
||||
}
|
||||
inst->editlevel = 0;
|
||||
}
|
||||
if (0 > inst->editlevel) { inst->editlevel = 0; }
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
qof_commit_edit_part2(QofInstance *inst,
|
||||
void (*on_error)(QofInstance *, QofBackendError),
|
||||
void (*on_done)(QofInstance *),
|
||||
void (*on_free)(QofInstance *))
|
||||
{
|
||||
QofBackend * be;
|
||||
|
||||
/* See if there's a backend. If there is, invoke it. */
|
||||
be = qof_book_get_backend(inst->book);
|
||||
if (be && qof_backend_commit_exists(be)) {
|
||||
QofBackendError errcode;
|
||||
|
||||
/* clear errors */
|
||||
do {
|
||||
errcode = qof_backend_get_error(be);
|
||||
} while (ERR_BACKEND_NO_ERR != errcode);
|
||||
|
||||
qof_backend_run_commit(be, inst);
|
||||
errcode = qof_backend_get_error(be);
|
||||
if (ERR_BACKEND_NO_ERR != errcode) {
|
||||
/* XXX Should perform a rollback here */
|
||||
inst->do_free = FALSE;
|
||||
|
||||
/* Push error back onto the stack */
|
||||
qof_backend_set_error (be, errcode);
|
||||
if (on_error)
|
||||
on_error(inst, errcode);
|
||||
return FALSE;
|
||||
}
|
||||
/* XXX the backend commit code should clear dirty!! */
|
||||
inst->dirty = FALSE;
|
||||
}
|
||||
if (inst->do_free) {
|
||||
if (on_free)
|
||||
on_free(inst);
|
||||
return TRUE;
|
||||
}
|
||||
if (on_done)
|
||||
on_done(inst);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* The QOF string cache */
|
||||
/* =================================================================== */
|
||||
|
||||
static GCache * qof_string_cache = NULL;
|
||||
|
||||
#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
|
||||
#ifdef QOF_DISABLE_DEPRECATED
|
||||
static GCache*
|
||||
qof_util_get_string_cache(void)
|
||||
#else
|
||||
GCache*
|
||||
qof_util_get_string_cache(void)
|
||||
#endif
|
||||
{
|
||||
if(!qof_string_cache) {
|
||||
qof_string_cache = g_cache_new(
|
||||
(GCacheNewFunc) g_strdup, /* value_new_func */
|
||||
g_free, /* value_destroy_func */
|
||||
(GCacheDupFunc) g_strdup, /* key_dup_func */
|
||||
g_free, /* key_destroy_func */
|
||||
g_str_hash, /* hash_key_func */
|
||||
g_str_hash, /* hash_value_func */
|
||||
g_str_equal); /* key_equal_func */
|
||||
}
|
||||
return qof_string_cache;
|
||||
}
|
||||
|
||||
void
|
||||
qof_util_string_cache_destroy (void)
|
||||
{
|
||||
if (qof_string_cache)
|
||||
g_cache_destroy (qof_string_cache);
|
||||
qof_string_cache = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
qof_util_string_cache_remove(gconstpointer key)
|
||||
{
|
||||
if (key)
|
||||
g_cache_remove(qof_util_get_string_cache(), key);
|
||||
}
|
||||
|
||||
gpointer
|
||||
qof_util_string_cache_insert(gconstpointer key)
|
||||
{
|
||||
if (key)
|
||||
return g_cache_insert(qof_util_get_string_cache(), (gpointer)key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gchar*
|
||||
qof_util_param_as_string(QofEntity *ent, QofParam *param)
|
||||
{
|
||||
gchar *param_string, param_date[MAX_DATE_LENGTH];
|
||||
gchar param_sa[GUID_ENCODING_LENGTH + 1];
|
||||
gboolean known_type;
|
||||
QofType paramType;
|
||||
const GUID *param_guid;
|
||||
time_t param_t;
|
||||
gnc_numeric param_numeric, (*numeric_getter) (QofEntity*, QofParam*);
|
||||
Timespec param_ts, (*date_getter) (QofEntity*, QofParam*);
|
||||
double param_double, (*double_getter) (QofEntity*, QofParam*);
|
||||
gboolean param_boolean, (*boolean_getter) (QofEntity*, QofParam*);
|
||||
gint32 param_i32, (*int32_getter) (QofEntity*, QofParam*);
|
||||
gint64 param_i64, (*int64_getter) (QofEntity*, QofParam*);
|
||||
gchar param_char, (*char_getter) (QofEntity*, QofParam*);
|
||||
|
||||
param_string = NULL;
|
||||
known_type = FALSE;
|
||||
paramType = param->param_type;
|
||||
if(safe_strcmp(paramType, QOF_TYPE_STRING) == 0) {
|
||||
param_string = g_strdup(param->param_getfcn(ent, param));
|
||||
if(param_string == NULL) { param_string = ""; }
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_DATE) == 0) {
|
||||
date_getter = (Timespec (*)(QofEntity*, QofParam*))param->param_getfcn;
|
||||
param_ts = date_getter(ent, param);
|
||||
param_t = timespecToTime_t(param_ts);
|
||||
strftime(param_date, MAX_DATE_LENGTH,
|
||||
QOF_UTC_DATE_FORMAT, gmtime(¶m_t));
|
||||
param_string = g_strdup(param_date);
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if((safe_strcmp(paramType, QOF_TYPE_NUMERIC) == 0) ||
|
||||
(safe_strcmp(paramType, QOF_TYPE_DEBCRED) == 0)) {
|
||||
numeric_getter = (gnc_numeric (*)(QofEntity*, QofParam*)) param->param_getfcn;
|
||||
param_numeric = numeric_getter(ent, param);
|
||||
param_string = g_strdup(gnc_numeric_to_string(param_numeric));
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_GUID) == 0) {
|
||||
param_guid = param->param_getfcn(ent, param);
|
||||
guid_to_string_buff(param_guid, param_sa);
|
||||
param_string = g_strdup(param_sa);
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_INT32) == 0) {
|
||||
int32_getter = (gint32 (*)(QofEntity*, QofParam*)) param->param_getfcn;
|
||||
param_i32 = int32_getter(ent, param);
|
||||
param_string = g_strdup_printf("%d", param_i32);
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_INT64) == 0) {
|
||||
int64_getter = (gint64 (*)(QofEntity*, QofParam*)) param->param_getfcn;
|
||||
param_i64 = int64_getter(ent, param);
|
||||
param_string = g_strdup_printf("%"G_GINT64_FORMAT, param_i64);
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_DOUBLE) == 0) {
|
||||
double_getter = (double (*)(QofEntity*, QofParam*)) param->param_getfcn;
|
||||
param_double = double_getter(ent, param);
|
||||
param_string = g_strdup_printf("%f", param_double);
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_BOOLEAN) == 0){
|
||||
boolean_getter = (gboolean (*)(QofEntity*, QofParam*)) param->param_getfcn;
|
||||
param_boolean = boolean_getter(ent, param);
|
||||
/* Boolean values need to be lowercase for QSF validation. */
|
||||
if(param_boolean == TRUE) { param_string = g_strdup("true"); }
|
||||
else { param_string = g_strdup("false"); }
|
||||
known_type = TRUE;
|
||||
return param_string;
|
||||
}
|
||||
/* "kvp" contains repeating values, cannot be a single string for the frame. */
|
||||
if(safe_strcmp(paramType, QOF_TYPE_KVP) == 0) {
|
||||
KvpFrame *frame = NULL;
|
||||
frame = param->param_getfcn(ent, param);
|
||||
known_type = TRUE;
|
||||
if(!kvp_frame_is_empty(frame))
|
||||
{
|
||||
GHashTable *hash = kvp_frame_get_hash(frame);
|
||||
param_string = g_strdup_printf("%s(%d)", QOF_TYPE_KVP,
|
||||
g_hash_table_size(hash));
|
||||
}
|
||||
return param_string;
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_CHAR) == 0) {
|
||||
char_getter = (gchar (*)(QofEntity*, QofParam*)) param->param_getfcn;
|
||||
param_char = char_getter(ent, param);
|
||||
known_type = TRUE;
|
||||
return g_strdup_printf("%c", param_char);
|
||||
}
|
||||
/* "collect" contains repeating values, cannot be a single string. */
|
||||
if(safe_strcmp(paramType, QOF_TYPE_COLLECT) == 0)
|
||||
{
|
||||
QofCollection *col = NULL;
|
||||
col = param->param_getfcn(ent, param);
|
||||
known_type = TRUE;
|
||||
return g_strdup_printf("%s(%d)",
|
||||
qof_collection_get_type(col), qof_collection_count(col));
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_TYPE_CHOICE) == 0)
|
||||
{
|
||||
QofEntity *child = NULL;
|
||||
child = param->param_getfcn(ent, param);
|
||||
if(!child) { return param_string; }
|
||||
known_type = TRUE;
|
||||
return g_strdup(qof_object_printable(child->e_type, child));
|
||||
}
|
||||
if(safe_strcmp(paramType, QOF_PARAM_BOOK) == 0)
|
||||
{
|
||||
QofBackend *be;
|
||||
QofBook *book;
|
||||
book = param->param_getfcn(ent, param);
|
||||
PINFO (" book param %p", book);
|
||||
be = qof_book_get_backend(book);
|
||||
known_type = TRUE;
|
||||
PINFO (" backend=%p", be);
|
||||
if(!be) { return QOF_PARAM_BOOK; }
|
||||
param_string = g_strdup(be->fullpath);
|
||||
PINFO (" fullpath=%s", param_string);
|
||||
if(param_string) { return param_string; }
|
||||
param_guid = qof_book_get_guid(book);
|
||||
guid_to_string_buff(param_guid, param_sa);
|
||||
PINFO (" book GUID=%s", param_sa);
|
||||
param_string = g_strdup(param_sa);
|
||||
return param_string;
|
||||
}
|
||||
if(!known_type)
|
||||
{
|
||||
QofEntity *child = NULL;
|
||||
child = param->param_getfcn(ent, param);
|
||||
if(!child) { return param_string; }
|
||||
return g_strdup(qof_object_printable(child->e_type, child));
|
||||
}
|
||||
return g_strdup("");
|
||||
}
|
||||
|
||||
void
|
||||
qof_init (void)
|
||||
{
|
||||
qof_util_get_string_cache ();
|
||||
guid_init ();
|
||||
qof_object_initialize ();
|
||||
qof_query_init ();
|
||||
qof_book_register ();
|
||||
}
|
||||
|
||||
void
|
||||
qof_close(void)
|
||||
{
|
||||
qof_query_shutdown ();
|
||||
qof_object_shutdown ();
|
||||
guid_shutdown ();
|
||||
qof_util_string_cache_destroy ();
|
||||
}
|
||||
|
||||
/* ************************ END OF FILE ***************************** */
|
||||
443
lib/libqof/qof/qofutil.h
Normal file
443
lib/libqof/qof/qofutil.h
Normal file
@@ -0,0 +1,443 @@
|
||||
/********************************************************************\
|
||||
* qof-util.h -- QOF utility functions *
|
||||
* *
|
||||
* 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 *
|
||||
\********************************************************************/
|
||||
|
||||
/** @addtogroup Utilities
|
||||
@{ */
|
||||
/** @file qof-util.h
|
||||
@brief QOF utility functions
|
||||
@author Copyright (C) 1997 Robin D. Clark <rclark@cs.hmc.edu>
|
||||
@author Copyright (C) 2000 Bill Gribble <grib@billgribble.com>
|
||||
@author Copyright (C) 1997-2002,2004 Linas Vepstas <linas@linas.org>
|
||||
@author Copyright 2006 Neil Williams <linux@codehelp.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef QOF_UTIL_H
|
||||
#define QOF_UTIL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "qof.h"
|
||||
#include "qoflog.h"
|
||||
#include "qofutil.h"
|
||||
#include "qofbackend-p.h"
|
||||
#include "qofbook.h"
|
||||
#include "qofinstance.h"
|
||||
|
||||
#define QOF_MOD_UTIL "qof-utilities"
|
||||
|
||||
/** \name typedef enum as string macros
|
||||
@{
|
||||
*/
|
||||
#define ENUM_BODY(name, value) \
|
||||
name value,
|
||||
|
||||
#define AS_STRING_CASE(name, value) \
|
||||
case name: { return #name; }
|
||||
|
||||
#define FROM_STRING_CASE(name, value) \
|
||||
if (strcmp(str, #name) == 0) { \
|
||||
return name; }
|
||||
|
||||
#define DEFINE_ENUM(name, list) \
|
||||
typedef enum { \
|
||||
list(ENUM_BODY) \
|
||||
}name;
|
||||
|
||||
#define AS_STRING_DEC(name, list) \
|
||||
const gchar* name##asString(name n);
|
||||
|
||||
#define AS_STRING_FUNC(name, list) \
|
||||
const gchar* name##asString(name n) { \
|
||||
switch (n) { \
|
||||
list(AS_STRING_CASE) \
|
||||
default: return ""; } }
|
||||
|
||||
#define FROM_STRING_DEC(name, list) \
|
||||
name name##fromString \
|
||||
(const gchar* str);
|
||||
|
||||
#define FROM_STRING_FUNC(name, list) \
|
||||
name name##fromString \
|
||||
(const gchar* str) { \
|
||||
if(str == NULL) { return 0; } \
|
||||
list(FROM_STRING_CASE) \
|
||||
return 0; }
|
||||
|
||||
/** @} */
|
||||
|
||||
/** \name enum as string with no typedef
|
||||
@{
|
||||
|
||||
Similar but used when the enum is NOT a typedef
|
||||
Make sure you use the DEFINE_ENUM_NON_TYPEDEF macro.
|
||||
|
||||
You can precede the FROM_STRING_FUNC_NON_TYPEDEF
|
||||
and AS_STRING_FUNC_NON_TYPEDEF macros with the
|
||||
keyword static if appropriate.
|
||||
|
||||
ENUM_BODY is used in both types.
|
||||
*/
|
||||
|
||||
#define DEFINE_ENUM_NON_TYPEDEF(name, list) \
|
||||
enum name { \
|
||||
list(ENUM_BODY) \
|
||||
};
|
||||
|
||||
#define FROM_STRING_DEC_NON_TYPEDEF(name, list) \
|
||||
void name##fromString \
|
||||
(const gchar* str, enum name *type);
|
||||
|
||||
#define FROM_STRING_CASE_NON_TYPEDEF(name, value) \
|
||||
if (strcmp(str, #name) == 0) { *type = name; }
|
||||
|
||||
#define FROM_STRING_FUNC_NON_TYPEDEF(name, list) \
|
||||
void name##fromString \
|
||||
(const gchar* str, enum name *type) { \
|
||||
if(str == NULL) { return; } \
|
||||
list(FROM_STRING_CASE_NON_TYPEDEF) }
|
||||
|
||||
#define AS_STRING_DEC_NON_TYPEDEF(name, list) \
|
||||
const gchar* name##asString(enum name n);
|
||||
|
||||
#define AS_STRING_FUNC_NON_TYPEDEF(name, list) \
|
||||
const gchar* name##asString(enum name n) { \
|
||||
switch (n) { \
|
||||
list(AS_STRING_CASE_NON_TYPEDEF) \
|
||||
default: return ""; } }
|
||||
|
||||
#define AS_STRING_CASE_NON_TYPEDEF(name, value) \
|
||||
case name: { return #name; }
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name Convenience wrappers
|
||||
@{
|
||||
*/
|
||||
|
||||
/** \brief Initialise the Query Object Framework
|
||||
|
||||
Use in place of separate init functions (like guid_init()
|
||||
and qof_query_init() etc.) to protect against future changes.
|
||||
*/
|
||||
void qof_init (void);
|
||||
|
||||
/** \brief Safely close down the Query Object Framework
|
||||
|
||||
Use in place of separate close / shutdown functions
|
||||
(like guid_shutdown(), qof_query_shutdown() etc.) to protect
|
||||
against future changes.
|
||||
*/
|
||||
void qof_close (void);
|
||||
|
||||
/** @} */
|
||||
|
||||
/* **** Prototypes *********************************************/
|
||||
|
||||
/** The safe_strcmp compares strings da and db the same way that strcmp()
|
||||
does, except that either may be null. This routine assumes that
|
||||
a non-null string is always greater than a null string.
|
||||
|
||||
@param da string 1.
|
||||
@param db string 2.
|
||||
|
||||
@return If da == NULL && db != NULL, returns -1.
|
||||
If da != NULL && db == NULL, returns +1.
|
||||
If da != NULL && db != NULL, returns the result of
|
||||
strcmp(da, db).
|
||||
If da == NULL && db == NULL, returns 0.
|
||||
*/
|
||||
gint safe_strcmp (const gchar * da, const gchar * db);
|
||||
|
||||
/** case sensitive comparison of strings da and db - either
|
||||
may be NULL. A non-NULL string is greater than a NULL string.
|
||||
|
||||
@param da string 1.
|
||||
@param db string 2.
|
||||
|
||||
@return If da == NULL && db != NULL, returns -1.
|
||||
If da != NULL && db == NULL, returns +1.
|
||||
If da != NULL && db != NULL, returns the result of
|
||||
strcmp(da, db).
|
||||
If da == NULL && db == NULL, returns 0.
|
||||
*/
|
||||
gint safe_strcasecmp (const gchar * da, const gchar * db);
|
||||
|
||||
/** The null_strcmp compares strings a and b the same way that strcmp()
|
||||
* does, except that either may be null. This routine assumes that
|
||||
* a null string is equal to the empty string.
|
||||
*/
|
||||
gint null_strcmp (const gchar * da, const gchar * db);
|
||||
|
||||
/** Search for str2 in first nchar chars of str1, ignore case. Return
|
||||
* pointer to first match, or null. These are just like that strnstr
|
||||
* and the strstr functions, except that they ignore the case. */
|
||||
extern gchar *strncasestr(const guchar *str1, const guchar *str2,
|
||||
size_t len);
|
||||
extern gchar *strcasestr(const gchar *str1, const gchar *str2);
|
||||
|
||||
/** The ultostr() subroutine is the inverse of strtoul(). It accepts a
|
||||
* number and prints it in the indicated base. The returned string
|
||||
* should be g_freed when done. */
|
||||
gchar * ultostr (gulong val, gint base);
|
||||
|
||||
/** Returns true if string s is a number, possibly surrounded by
|
||||
* whitespace. */
|
||||
gboolean gnc_strisnum(const guchar *s);
|
||||
|
||||
#ifndef HAVE_STPCPY
|
||||
#define stpcpy g_stpcpy
|
||||
#endif
|
||||
|
||||
/** Return NULL if the field is whitespace (blank, tab, formfeed etc.)
|
||||
* Else return pointer to first non-whitespace character.
|
||||
*/
|
||||
const gchar * qof_util_whitespace_filter (const gchar * val);
|
||||
|
||||
/** Return integer 1 if the string starts with 't' or 'T' or
|
||||
* contains the word 'true' or 'TRUE'; if string is a number,
|
||||
* return that number. (Leading whitespace is ignored). */
|
||||
gint qof_util_bool_to_int (const gchar * val);
|
||||
|
||||
/** \brief Converts a parameter to a printable string.
|
||||
|
||||
The returned string must be freed by the caller.
|
||||
*/
|
||||
gchar* qof_util_param_as_string(QofEntity *ent, QofParam *param);
|
||||
|
||||
/** The QOF String Cache:
|
||||
*
|
||||
* Many strings used throughout QOF and QOF applications are likely to
|
||||
* be duplicated.
|
||||
*
|
||||
* QOF provides a reference counted cache system for the strings, which
|
||||
* shares strings whenever possible.
|
||||
*
|
||||
* Use qof_util_string_cache_insert to insert a string into the cache (it
|
||||
* will return a pointer to the cached string). Basically you should
|
||||
* use this instead of g_strdup.
|
||||
*
|
||||
* Use qof_util_string_cache_remove (giving it a pointer to a cached
|
||||
* string) if the string is unused. If this is the last reference to
|
||||
* the string it will be removed from the cache, otherwise it will
|
||||
* just decrement the reference count. Basically you should use this
|
||||
* instead of g_free.
|
||||
*
|
||||
* Just in case it's not clear: The remove function must NOT be called
|
||||
* for the string you passed INTO the insert function. It must be
|
||||
* called for the _cached_ string that is _returned_ by the insert
|
||||
* function.
|
||||
*
|
||||
* Note that all the work is done when inserting or removing. Once
|
||||
* cached the strings are just plain C strings.
|
||||
*
|
||||
* The string cache is demand-created on first use.
|
||||
*
|
||||
**/
|
||||
/** Destroy the qof_util_string_cache */
|
||||
void qof_util_string_cache_destroy (void);
|
||||
|
||||
/** You can use this function as a destroy notifier for a GHashTable
|
||||
that uses common strings as keys (or values, for that matter.)
|
||||
*/
|
||||
void qof_util_string_cache_remove(gconstpointer key);
|
||||
|
||||
/** You can use this function with g_hash_table_insert(), for the key
|
||||
(or value), as long as you use the destroy notifier above.
|
||||
*/
|
||||
gpointer qof_util_string_cache_insert(gconstpointer key);
|
||||
|
||||
#define CACHE_INSERT(str) qof_util_string_cache_insert((gconstpointer)(str))
|
||||
#define CACHE_REMOVE(str) qof_util_string_cache_remove((str))
|
||||
|
||||
/* Replace cached string currently in 'dst' with string in 'src'.
|
||||
* Typical usage:
|
||||
* void foo_set_name(Foo *f, const char *str) {
|
||||
* CACHE_REPLACE(f->name, str);
|
||||
* }
|
||||
* It avoids unnecessary ejection by doing INSERT before REMOVE.
|
||||
*/
|
||||
#define CACHE_REPLACE(dst, src) do { \
|
||||
gpointer tmp = CACHE_INSERT((src)); \
|
||||
CACHE_REMOVE((dst)); \
|
||||
(dst) = tmp; \
|
||||
} while (0)
|
||||
|
||||
#define QOF_CACHE_NEW(void) qof_util_string_cache_insert("")
|
||||
|
||||
/** begin_edit helper
|
||||
*
|
||||
* @param inst: an instance of QofInstance
|
||||
*
|
||||
* The caller should use this macro first and then perform any other operations.
|
||||
|
||||
Uses newly created functions to allow the macro to be used
|
||||
when QOF is linked as a library. qofbackend-p.h is a private header.
|
||||
*/
|
||||
|
||||
#define QOF_BEGIN_EDIT(inst) \
|
||||
if (!(inst)) return; \
|
||||
\
|
||||
(inst)->editlevel++; \
|
||||
if (1 < (inst)->editlevel) return; \
|
||||
\
|
||||
if (0 >= (inst)->editlevel) \
|
||||
{ \
|
||||
PERR ("unbalanced call - resetting (was %d)", (inst)->editlevel); \
|
||||
(inst)->editlevel = 1; \
|
||||
} \
|
||||
ENTER ("(inst=%p)", (inst)); \
|
||||
\
|
||||
/* See if there's a backend. If there is, invoke it. */ \
|
||||
{ \
|
||||
QofBackend * be; \
|
||||
be = qof_book_get_backend ((inst)->book); \
|
||||
if (be && qof_backend_begin_exists(be)) { \
|
||||
qof_backend_run_begin(be, (inst)); \
|
||||
} else { \
|
||||
/* We tried and failed to start transaction! */ \
|
||||
(inst)->dirty = TRUE; \
|
||||
} \
|
||||
} \
|
||||
LEAVE (" ");
|
||||
|
||||
/** \brief function version of QOF_BEGIN_EDIT
|
||||
|
||||
The macro cannot be used in a function that returns a value,
|
||||
this function can be used instead.
|
||||
*/
|
||||
gboolean qof_begin_edit(QofInstance *inst);
|
||||
|
||||
/**
|
||||
* commit_edit helpers
|
||||
*
|
||||
* The caller should call PART1 as the first thing, then
|
||||
* perform any local operations prior to calling the backend.
|
||||
* Then call PART2.
|
||||
*/
|
||||
|
||||
/**
|
||||
* part1 -- deal with the editlevel
|
||||
*
|
||||
* @param inst: an instance of QofInstance
|
||||
*/
|
||||
|
||||
#define QOF_COMMIT_EDIT_PART1(inst) { \
|
||||
if (!(inst)) return; \
|
||||
\
|
||||
(inst)->editlevel--; \
|
||||
if (0 < (inst)->editlevel) return; \
|
||||
\
|
||||
/* The pricedb suffers from delayed update... */ \
|
||||
/* This may be setting a bad precedent for other types, I fear. */ \
|
||||
/* Other types probably really should handle begin like this. */ \
|
||||
if ((-1 == (inst)->editlevel) && (inst)->dirty) \
|
||||
{ \
|
||||
QofBackend * be; \
|
||||
be = qof_book_get_backend ((inst)->book); \
|
||||
if (be && qof_backend_begin_exists(be)) { \
|
||||
qof_backend_run_begin(be, (inst)); \
|
||||
} \
|
||||
(inst)->editlevel = 0; \
|
||||
} \
|
||||
if (0 > (inst)->editlevel) \
|
||||
{ \
|
||||
PERR ("unbalanced call - resetting (was %d)", (inst)->editlevel); \
|
||||
(inst)->editlevel = 0; \
|
||||
} \
|
||||
ENTER ("(inst=%p) dirty=%d do-free=%d", \
|
||||
(inst), (inst)->dirty, (inst)->do_free); \
|
||||
}
|
||||
|
||||
/** \brief function version of QOF_COMMIT_EDIT_PART1
|
||||
|
||||
The macro cannot be used in a function that returns a value,
|
||||
this function can be used instead. Only Part1 is implemented.
|
||||
*/
|
||||
gboolean qof_commit_edit(QofInstance *inst);
|
||||
|
||||
/**
|
||||
* part2 -- deal with the backend
|
||||
*
|
||||
* @param inst: an instance of QofInstance
|
||||
* @param on_error: a function called if there is a backend error.
|
||||
* void (*on_error)(inst, QofBackendError)
|
||||
* @param on_done: a function called after the commit is completed
|
||||
* successfully for an object which remained valid.
|
||||
* void (*on_done)(inst)
|
||||
* @param on_free: a function called if the commit succeeded and the instance
|
||||
* is to be freed.
|
||||
* void (*on_free)(inst)
|
||||
*
|
||||
* Note that only *one* callback will be called (or zero, if that
|
||||
* callback is NULL). In particular, 'on_done' will not be called for
|
||||
* an object which is to be freed.
|
||||
*
|
||||
* Returns TRUE, if the commit succeeded, FALSE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
qof_commit_edit_part2(QofInstance *inst,
|
||||
void (*on_error)(QofInstance *, QofBackendError),
|
||||
void (*on_done)(QofInstance *),
|
||||
void (*on_free)(QofInstance *));
|
||||
|
||||
/** \brief Macro version of ::qof_commit_edit_part2
|
||||
|
||||
\note This macro changes programme flow if the instance is freed.
|
||||
*/
|
||||
#define QOF_COMMIT_EDIT_PART2(inst,on_error,on_done,on_free) { \
|
||||
QofBackend * be; \
|
||||
\
|
||||
/* See if there's a backend. If there is, invoke it. */ \
|
||||
be = qof_book_get_backend ((inst)->book); \
|
||||
if (be && qof_backend_commit_exists(be)) \
|
||||
{ \
|
||||
QofBackendError errcode; \
|
||||
\
|
||||
/* clear errors */ \
|
||||
do { \
|
||||
errcode = qof_backend_get_error (be); \
|
||||
} while (ERR_BACKEND_NO_ERR != errcode); \
|
||||
\
|
||||
qof_backend_run_commit(be, (inst)); \
|
||||
errcode = qof_backend_get_error (be); \
|
||||
if (ERR_BACKEND_NO_ERR != errcode) \
|
||||
{ \
|
||||
/* XXX Should perform a rollback here */ \
|
||||
(inst)->do_free = FALSE; \
|
||||
\
|
||||
/* Push error back onto the stack */ \
|
||||
qof_backend_set_error (be, errcode); \
|
||||
(on_error)((inst), errcode); \
|
||||
} \
|
||||
/* XXX the backend commit code should clear dirty!! */ \
|
||||
(inst)->dirty = FALSE; \
|
||||
} \
|
||||
(on_done)(inst); \
|
||||
\
|
||||
LEAVE ("inst=%p, dirty=%d do-free=%d", \
|
||||
(inst), (inst)->dirty, (inst)->do_free); \
|
||||
if ((inst)->do_free) { \
|
||||
(on_free)(inst); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif /* QOF_UTIL_H */
|
||||
/** @} */
|
||||
Reference in New Issue
Block a user