mirror of https://github.com/Gnucash/gnucash
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
12 KiB
428 lines
12 KiB
/********************************************************************\
|
|
* qofbackend.c -- utility routines for dealing with the data backend *
|
|
* Copyright (C) 2000 Linas Vepstas <linas@linas.org> *
|
|
* Copyright (C) 2004-5 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 *
|
|
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
|
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
#define _GNU_SOURCE
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <regex.h>
|
|
#include <glib.h>
|
|
#include <gmodule.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include "qofbackend-p.h"
|
|
|
|
static QofLogModule log_module = QOF_MOD_BACKEND;
|
|
|
|
#define QOF_CONFIG_DESC "desc"
|
|
#define QOF_CONFIG_TIP "tip"
|
|
|
|
/********************************************************************\
|
|
* error handling *
|
|
\********************************************************************/
|
|
|
|
void
|
|
qof_backend_set_error (QofBackend *be, QofBackendError err)
|
|
{
|
|
if (!be) return;
|
|
|
|
/* use stack-push semantics. Only the earliest error counts */
|
|
if (ERR_BACKEND_NO_ERR != be->last_err) return;
|
|
be->last_err = err;
|
|
}
|
|
|
|
QofBackendError
|
|
qof_backend_get_error (QofBackend *be)
|
|
{
|
|
QofBackendError err;
|
|
if (!be) return ERR_BACKEND_NO_BACKEND;
|
|
|
|
/* use 'stack-pop' semantics */
|
|
err = be->last_err;
|
|
be->last_err = ERR_BACKEND_NO_ERR;
|
|
return err;
|
|
}
|
|
|
|
void
|
|
qof_backend_set_message (QofBackend *be, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
char * buffer;
|
|
|
|
if (!be) return;
|
|
|
|
/* If there's already something here, free it */
|
|
if (be->error_msg) g_free(be->error_msg);
|
|
|
|
if (!format) {
|
|
be->error_msg = NULL;
|
|
return;
|
|
}
|
|
|
|
va_start(args, format);
|
|
buffer = (char *)g_strdup_vprintf(format, args);
|
|
va_end(args);
|
|
|
|
be->error_msg = buffer;
|
|
}
|
|
|
|
/* This should always return a valid char * */
|
|
char *
|
|
qof_backend_get_message (QofBackend *be)
|
|
{
|
|
char * msg;
|
|
|
|
if (!be) return g_strdup("ERR_BACKEND_NO_BACKEND");
|
|
if (!be->error_msg) return NULL;
|
|
|
|
/*
|
|
* Just return the contents of the error_msg and then set it to
|
|
* NULL. This is necessary, because the Backends don't seem to
|
|
* have a destroy_backend function to take care if freeing stuff
|
|
* up. The calling function should free the copy.
|
|
* Also, this is consistent with the qof_backend_get_error() popping.
|
|
*/
|
|
|
|
msg = be->error_msg;
|
|
be->error_msg = NULL;
|
|
return msg;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Get a clean backend */
|
|
void
|
|
qof_backend_init(QofBackend *be)
|
|
{
|
|
be->session_begin = NULL;
|
|
be->session_end = NULL;
|
|
be->destroy_backend = NULL;
|
|
|
|
be->load = NULL;
|
|
|
|
be->begin = NULL;
|
|
be->commit = NULL;
|
|
be->rollback = NULL;
|
|
|
|
be->compile_query = NULL;
|
|
be->free_query = NULL;
|
|
be->run_query = NULL;
|
|
|
|
be->sync = NULL;
|
|
be->load_config = NULL;
|
|
|
|
be->events_pending = NULL;
|
|
be->process_events = NULL;
|
|
|
|
be->last_err = ERR_BACKEND_NO_ERR;
|
|
if (be->error_msg) g_free (be->error_msg);
|
|
be->error_msg = NULL;
|
|
be->percentage = NULL;
|
|
be->backend_configuration = kvp_frame_new();
|
|
|
|
#ifdef GNUCASH_MAJOR_VERSION
|
|
/* XXX remove these */
|
|
be->fullpath = NULL;
|
|
be->price_lookup = NULL;
|
|
be->export = NULL;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
qof_backend_run_begin(QofBackend *be, QofInstance *inst)
|
|
{
|
|
if(!be || !inst) { return; }
|
|
if(!be->begin) { return; }
|
|
(be->begin) (be, inst);
|
|
}
|
|
|
|
gboolean
|
|
qof_backend_begin_exists(QofBackend *be)
|
|
{
|
|
if(be->begin) { return TRUE; }
|
|
else { return FALSE; }
|
|
}
|
|
|
|
void
|
|
qof_backend_run_commit(QofBackend *be, QofInstance *inst)
|
|
{
|
|
if(!be || !inst) { return; }
|
|
if(!be->commit) { return; }
|
|
(be->commit) (be, inst);
|
|
}
|
|
|
|
/* =========== Backend Configuration ================ */
|
|
|
|
void qof_backend_prepare_frame(QofBackend *be)
|
|
{
|
|
g_return_if_fail(be);
|
|
if(!kvp_frame_is_empty(be->backend_configuration)) {
|
|
kvp_frame_delete(be->backend_configuration);
|
|
be->backend_configuration = kvp_frame_new();
|
|
}
|
|
be->config_count = 0;
|
|
}
|
|
|
|
void qof_backend_prepare_option(QofBackend *be, QofBackendOption *option)
|
|
{
|
|
KvpValue *value;
|
|
gchar *temp;
|
|
gint count;
|
|
|
|
g_return_if_fail(be || option);
|
|
count = be->config_count;
|
|
count++;
|
|
value = NULL;
|
|
ENTER (" %d", count);
|
|
switch (option->type)
|
|
{
|
|
case KVP_TYPE_GINT64 : {
|
|
value = kvp_value_new_gint64(*(gint64*)option->value);
|
|
break;
|
|
}
|
|
case KVP_TYPE_DOUBLE : {
|
|
value = kvp_value_new_double(*(double*)option->value);
|
|
break;
|
|
}
|
|
case KVP_TYPE_NUMERIC : {
|
|
value = kvp_value_new_numeric(*(gnc_numeric*)option->value);
|
|
break;
|
|
}
|
|
case KVP_TYPE_STRING : {
|
|
value = kvp_value_new_string((const char*)option->value);
|
|
break;
|
|
}
|
|
case KVP_TYPE_GUID : { break; } /* unsupported */
|
|
case KVP_TYPE_TIMESPEC : {
|
|
value = kvp_value_new_timespec(*(Timespec*)option->value);
|
|
break;
|
|
}
|
|
case KVP_TYPE_BINARY : { break; } /* unsupported */
|
|
case KVP_TYPE_GLIST : { break; } /* unsupported */
|
|
case KVP_TYPE_FRAME : { break; } /* unsupported */
|
|
}
|
|
if(value) {
|
|
temp = g_strdup_printf("/%s", option->option_name);
|
|
kvp_frame_set_value(be->backend_configuration, temp, value);
|
|
PINFO (" setting value at %s", temp);
|
|
g_free(temp);
|
|
temp = g_strdup_printf("/%s/%s", QOF_CONFIG_DESC, option->option_name);
|
|
PINFO (" setting description %s at %s", option->description, temp);
|
|
kvp_frame_set_string(be->backend_configuration, temp, option->description);
|
|
PINFO (" check= %s", kvp_frame_get_string(be->backend_configuration, temp));
|
|
g_free(temp);
|
|
temp = g_strdup_printf("/%s/%s", QOF_CONFIG_TIP, option->option_name);
|
|
PINFO (" setting tooltip %s at %s", option->tooltip, temp);
|
|
kvp_frame_set_string(be->backend_configuration, temp, option->tooltip);
|
|
PINFO (" check= %s", kvp_frame_get_string(be->backend_configuration, temp));
|
|
g_free(temp);
|
|
/* only increment the counter if successful */
|
|
be->config_count = count;
|
|
}
|
|
LEAVE (" ");
|
|
}
|
|
|
|
KvpFrame* qof_backend_complete_frame(QofBackend *be)
|
|
{
|
|
g_return_val_if_fail(be, NULL);
|
|
be->config_count = 0;
|
|
return be->backend_configuration;
|
|
}
|
|
|
|
struct config_iterate {
|
|
QofBackendOptionCB fcn;
|
|
gpointer data;
|
|
gint count;
|
|
KvpFrame *recursive;
|
|
};
|
|
|
|
static void
|
|
config_foreach_cb (const char *key, KvpValue *value, gpointer data)
|
|
{
|
|
QofBackendOption option;
|
|
gint64 int64;
|
|
double db;
|
|
gnc_numeric num;
|
|
Timespec ts;
|
|
gchar *parent;
|
|
struct config_iterate *helper;
|
|
|
|
g_return_if_fail(key || value || data);
|
|
helper = (struct config_iterate*)data;
|
|
if(!helper->recursive) { PERR (" no parent frame"); return; }
|
|
// skip the presets.
|
|
if(0 == safe_strcmp(key, QOF_CONFIG_DESC)) { return; }
|
|
if(0 == safe_strcmp(key, QOF_CONFIG_TIP)) { return; }
|
|
ENTER (" key=%s", key);
|
|
option.option_name = key;
|
|
option.type = kvp_value_get_type(value);
|
|
if(!option.type) { return; }
|
|
switch (option.type)
|
|
{
|
|
case KVP_TYPE_GINT64 : {
|
|
int64 = kvp_value_get_gint64(value);
|
|
option.value = (gpointer)&int64;
|
|
break;
|
|
}
|
|
case KVP_TYPE_DOUBLE : {
|
|
db = kvp_value_get_double(value);
|
|
option.value = (gpointer)&db;
|
|
break;
|
|
}
|
|
case KVP_TYPE_NUMERIC : {
|
|
num = kvp_value_get_numeric(value);
|
|
option.value = (gpointer)#
|
|
break;
|
|
}
|
|
case KVP_TYPE_STRING : {
|
|
option.value = (gpointer)kvp_value_get_string(value);
|
|
break;
|
|
}
|
|
case KVP_TYPE_GUID : { break; } /* unsupported */
|
|
case KVP_TYPE_TIMESPEC : {
|
|
ts = kvp_value_get_timespec(value);
|
|
option.value = (gpointer)&ts;
|
|
break;
|
|
}
|
|
case KVP_TYPE_BINARY : { break; } /* unsupported */
|
|
case KVP_TYPE_GLIST : { break; } /* unsupported */
|
|
case KVP_TYPE_FRAME : { break; } /* unsupported */
|
|
}
|
|
parent = g_strdup_printf("/%s/%s", QOF_CONFIG_DESC, key);
|
|
option.description = kvp_frame_get_string(helper->recursive, parent);
|
|
g_free(parent);
|
|
parent = g_strdup_printf("/%s/%s", QOF_CONFIG_TIP, key);
|
|
option.tooltip = kvp_frame_get_string(helper->recursive, parent);
|
|
helper->count++;
|
|
helper->fcn (&option, helper->data);
|
|
LEAVE (" desc=%s tip=%s", option.description, option.tooltip);
|
|
}
|
|
|
|
void qof_backend_option_foreach(KvpFrame *config, QofBackendOptionCB cb, gpointer data)
|
|
{
|
|
struct config_iterate helper;
|
|
|
|
if(!config || !cb) { return; }
|
|
ENTER (" ");
|
|
helper.fcn = cb;
|
|
helper.count = 1;
|
|
helper.data = data;
|
|
helper.recursive = config;
|
|
kvp_frame_for_each_slot(config, config_foreach_cb, &helper);
|
|
LEAVE (" ");
|
|
}
|
|
|
|
void
|
|
qof_backend_load_config(QofBackend *be, KvpFrame *config)
|
|
{
|
|
if(!be || !config) { return; }
|
|
if(!be->load_config) { return; }
|
|
(be->load_config) (be, config);
|
|
}
|
|
|
|
KvpFrame*
|
|
qof_backend_get_config(QofBackend *be)
|
|
{
|
|
if(!be) { return NULL; }
|
|
if(!be->get_config) { return NULL; }
|
|
return (be->get_config) (be);
|
|
}
|
|
|
|
gboolean
|
|
qof_backend_commit_exists(QofBackend *be)
|
|
{
|
|
if(!be) { return FALSE; }
|
|
if(be->commit) { return TRUE; }
|
|
else { return FALSE; }
|
|
}
|
|
|
|
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_load_backend_library (const char *directory,
|
|
const char* filename, const char* init_fcn)
|
|
{
|
|
struct stat sbuf;
|
|
gchar *fullpath;
|
|
typedef void (* backend_init) (void);
|
|
GModule *backend;
|
|
backend_init gmod_init;
|
|
gpointer g;
|
|
|
|
g_return_val_if_fail(g_module_supported(), FALSE);
|
|
fullpath = g_module_build_path(directory, filename);
|
|
PINFO (" fullpath=%s", fullpath);
|
|
g_return_val_if_fail((stat(fullpath, &sbuf) == 0), FALSE);
|
|
backend = g_module_open(fullpath, G_MODULE_BIND_LAZY);
|
|
if(!backend) {
|
|
g_message ("%s: %s\n", PACKAGE, g_module_error ());
|
|
return FALSE;
|
|
}
|
|
g = &gmod_init;
|
|
if (!g_module_symbol (backend, init_fcn, g))
|
|
{
|
|
g_message ("%s: %s\n", PACKAGE, g_module_error ());
|
|
return FALSE;
|
|
}
|
|
g_module_make_resident(backend);
|
|
gmod_init();
|
|
return TRUE;
|
|
}
|
|
|
|
/************************* END OF FILE ********************************/
|