Files
gnucash/lib/libqof/qof/qofbackend.c
T
Chris Shoemaker 6a8404df1d Implement QOF_COMMIT_EDIT_PART2 as a function instead of a macro.
Any of the callbacks passed to this function may be NULL to decline the 
   use of that callback.  Also, the three callbacks are now called in three
   mutually exclusive cases, corresponding to: 
      1) a failed commit, ('on_error')
      2) a successful commit where the object remains valid, ('on_done'), and 
      3) a successful commit where the object has been destroyed, ('on_free').


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@13390 57a11ea4-9604-0410-9ed3-97b8803252fd
2006-02-26 04:56:47 +00:00

458 lines
12 KiB
C

/********************************************************************\
* 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 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, 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 <errno.h>
#include "qof.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;
}
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 of 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();
/* to be removed */
be->price_lookup = NULL;
be->export = NULL;
}
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;
switch (option->type)
{
case KVP_TYPE_GINT64 : {
value = kvp_value_new_gint64(GPOINTER_TO_INT(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);
g_free(temp);
temp = g_strdup_printf("/%s/%s", QOF_CONFIG_DESC, option->option_name);
kvp_frame_set_string(be->backend_configuration, temp, option->description);
g_free(temp);
temp = g_strdup_printf("/%s/%s", QOF_CONFIG_TIP, option->option_name);
kvp_frame_set_string(be->backend_configuration, temp, option->tooltip);
g_free(temp);
/* only increment the counter if successful */
be->config_count = count;
}
}
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)&num;
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_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;
}
gboolean
qof_load_backend_library (const char *directory,
const char* filename, const char* init_fcn)
{
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);
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 ********************************/