Add GNCSession object. Refactor session code from GNCBook to GNCSession.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@5455 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas
2001-10-02 09:10:35 +00:00
parent 77913bb212
commit aeff5c25de
39 changed files with 1650 additions and 1388 deletions

View File

@@ -26,7 +26,6 @@
#include <string.h>
#include "Backend.h"
#include "Group.h"
#include "global-options.h"
#include "gnc-commodity.h"
#include "gnc-component-manager.h"
@@ -36,6 +35,7 @@
#include "gnc-file-dialog.h"
#include "gnc-file-history.h"
#include "gnc-file-p.h"
#include "gnc-session.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "messages.h"
@@ -47,30 +47,30 @@
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_GUI;
static GNCBook *current_book = NULL;
static GNCSession * current_session = NULL;
static GNCCanCancelSaveCB can_cancel_cb = NULL;
static GNCBook *
gnc_get_current_book_internal (void)
static GNCSession *
gnc_get_current_session_internal (void)
{
if (!current_book)
current_book = gnc_book_new ();
if (!current_session)
current_session = gnc_session_new ();
return current_book;
return current_session;
}
void
gnc_file_init (void)
{
gnc_set_current_book_handler (gnc_get_current_book_internal);
gnc_set_current_session_handler (gnc_get_current_session_internal);
/* Make sure we have a curent book. */
gnc_get_current_book_internal ();
/* Make sure we have a current session. */
gnc_get_current_session_internal ();
}
static gboolean
show_book_error (GNCBackendError io_error, const char *newfile)
show_session_error (GNCBackendError io_error, const char *newfile)
{
gboolean uh_oh = TRUE;
const char *fmt;
@@ -220,13 +220,13 @@ show_book_error (GNCBackendError io_error, const char *newfile)
}
static void
gnc_add_history (GNCBook *book)
gnc_add_history (GNCSession * session)
{
char *url;
if (!book) return;
if (!session) return;
url = xaccResolveURL (gnc_book_get_url (book));
url = xaccResolveURL (gnc_session_get_url (session));
if (!url)
return;
@@ -243,20 +243,20 @@ gnc_book_opened (void)
{
gh_call2 (gh_eval_str("gnc:hook-run-danglers"),
gh_eval_str("gnc:*book-opened-hook*"),
gh_str02scm(gnc_book_get_url(current_book)));
gh_str02scm(gnc_session_get_url(current_session)));
}
void
gnc_file_new (void)
{
GNCBook *book;
GNCSession *session;
/* If user attempts to start a new session before saving results of
* the last one, prompt them to clean up their act. */
if (!gnc_file_query_save ())
return;
book = gnc_get_current_book_internal ();
session = gnc_get_current_session_internal ();
/* close any ongoing file sessions, and free the accounts.
* disable events so we don't get spammed by redraws. */
@@ -264,15 +264,15 @@ gnc_file_new (void)
gh_call2(gh_eval_str("gnc:hook-run-danglers"),
gh_eval_str("gnc:*book-closed-hook*"),
gh_str02scm(gnc_book_get_url(book)));
gnc_book_destroy (book);
current_book = NULL;
gh_str02scm(gnc_session_get_url(session)));
gnc_session_destroy (session);
current_session = NULL;
gnc_commodity_table_remove_non_iso (gnc_engine_commodities ());
/* start a new book */
gnc_get_current_book_internal ();
gnc_get_current_session_internal ();
gh_call1(gh_eval_str("gnc:hook-run-danglers"),
gh_eval_str("gnc:*new-book-hook*"));
@@ -286,7 +286,7 @@ gnc_file_new (void)
gboolean
gnc_file_query_save (void)
{
GNCBook * book = gnc_get_current_book_internal ();
GNCSession * session = gnc_get_current_session_internal ();
/* If user wants to mess around before finishing business with
* the old file, give em a chance to figure out what's up.
@@ -294,7 +294,7 @@ gnc_file_query_save (void)
* up the file-selection dialog, we don't blow em out of the water;
* instead, give them another chance to say "no" to the verify box.
*/
while (gnc_book_not_saved(book))
while (gnc_book_not_saved(gnc_session_get_book (session)))
{
GNCVerifyResult result;
const char *message = _("Changes have been made since the last "
@@ -326,9 +326,8 @@ gnc_file_query_save (void)
static gboolean
gnc_post_file_open (const char * filename)
{
GNCBook *new_book;
GNCSession *new_session;
gboolean uh_oh = FALSE;
AccountGroup *new_group;
char * newfile;
GNCBackendError io_err = ERR_BACKEND_NO_ERR;
@@ -337,7 +336,7 @@ gnc_post_file_open (const char * filename)
newfile = xaccResolveURL (filename);
if (!newfile)
{
show_book_error (ERR_FILEIO_FILE_NOT_FOUND, filename);
show_session_error (ERR_FILEIO_FILE_NOT_FOUND, filename);
return FALSE;
}
@@ -353,29 +352,27 @@ gnc_post_file_open (const char * filename)
/* -- this code is almost identical in FileOpen and FileSaveAs -- */
gh_call2(gh_eval_str("gnc:hook-run-danglers"),
gh_eval_str("gnc:*book-closed-hook*"),
gh_str02scm(gnc_book_get_url(current_book)));
gh_str02scm(gnc_session_get_url(current_session)));
gnc_book_destroy (current_book);
current_book = NULL;
gnc_session_destroy (current_session);
current_session = NULL;
gnc_commodity_table_remove_non_iso (gnc_engine_commodities ());
/* load the accounts from the users datafile */
/* but first, check to make sure we've got a book going. */
new_book = gnc_book_new ();
/* but first, check to make sure we've got a session going. */
new_session = gnc_session_new ();
new_group = NULL;
gnc_book_begin (new_book, newfile, FALSE, FALSE);
io_err = gnc_book_get_error (new_book);
gnc_session_begin (new_session, newfile, FALSE, FALSE);
io_err = gnc_session_get_error (new_session);
/* if file appears to be locked, ask the user ... */
if (ERR_BACKEND_LOCKED == io_err)
{
if (FALSE == show_book_error (io_err, newfile))
if (FALSE == show_session_error (io_err, newfile))
{
/* user told us to ignore locks. So ignore them. */
gnc_book_begin (new_book, newfile, TRUE, FALSE);
gnc_session_begin (new_session, newfile, TRUE, FALSE);
}
else
{
@@ -391,17 +388,17 @@ gnc_post_file_open (const char * filename)
else if ((ERR_BACKEND_NO_SUCH_DB == io_err) ||
(ERR_SQL_DB_TOO_OLD == io_err))
{
if (FALSE == show_book_error (io_err, newfile))
if (FALSE == show_session_error (io_err, newfile))
{
/* user told us to create a new database. Do it. */
gnc_book_begin (new_book, newfile, FALSE, TRUE);
gnc_session_begin (new_session, newfile, FALSE, TRUE);
}
}
/* Check for errors again, since above may have cleared the lock.
* If its still locked, still, doesn't exist, still too old, then
* don't bother with the message, just die. */
io_err = gnc_book_get_error (new_book);
io_err = gnc_session_get_error (new_session);
if ((ERR_BACKEND_LOCKED == io_err) ||
(ERR_BACKEND_NO_SUCH_DB == io_err) ||
(ERR_SQL_DB_TOO_OLD == io_err))
@@ -410,25 +407,27 @@ gnc_post_file_open (const char * filename)
}
else
{
uh_oh = show_book_error (io_err, newfile);
uh_oh = show_session_error (io_err, newfile);
}
if (!uh_oh)
{
gnc_book_load (new_book);
AccountGroup *new_group;
gnc_session_load (new_session);
/* check for i/o error, put up appropriate error dialog */
io_err = gnc_book_get_error (new_book);
uh_oh = show_book_error (io_err, newfile);
io_err = gnc_session_get_error (new_session);
uh_oh = show_session_error (io_err, newfile);
new_group = gnc_book_get_group (new_book);
new_group = gnc_book_get_group (gnc_session_get_book (new_session));
if (uh_oh) new_group = NULL;
/* Umm, came up empty-handed, but no error:
* The backend forgot to set an error. So make one up. */
if (!uh_oh && !new_group)
{
uh_oh = show_book_error (ERR_BACKEND_MISC, newfile);
uh_oh = show_session_error (ERR_BACKEND_MISC, newfile);
}
}
@@ -437,7 +436,7 @@ gnc_post_file_open (const char * filename)
/* going down -- abandon ship */
if (uh_oh)
{
gnc_book_destroy (new_book);
gnc_session_destroy (new_session);
/* well, no matter what, I think it's a good idea to have a
* topgroup around. For example, early in the gnucash startup
@@ -445,7 +444,7 @@ gnc_post_file_open (const char * filename)
* reason, we don't want to leave them high & dry without a
* topgroup, because if the user continues, then bad things will
* happen. */
current_book = gnc_get_current_book_internal ();
current_session = gnc_get_current_session_internal ();
g_free (newfile);
@@ -459,14 +458,14 @@ gnc_post_file_open (const char * filename)
/* if we got to here, then we've successfully gotten a new session */
/* close up the old file session (if any) */
current_book = new_book;
current_session = new_session;
gnc_book_opened ();
/* --------------- END CORE SESSION CODE -------------- */
/* clean up old stuff, and then we're outta here. */
gnc_add_history (current_book);
gnc_add_history (current_session);
g_free (newfile);
@@ -492,7 +491,7 @@ gnc_file_open (void)
* user fails to pick a file (by e.g. hitting the cancel button), we
* might be left with a null topgroup, which leads to nastiness when
* user goes to create their very first account. So create one. */
gnc_get_current_book_internal ();
gnc_get_current_session_internal ();
return result;
}
@@ -516,15 +515,15 @@ gnc_file_save (void)
{
GNCBackendError io_err;
const char * newfile;
GNCBook *book;
GNCSession *session;
ENTER (" ");
/* hack alert -- Somehow make sure all in-progress edits get committed! */
/* If we don't have a filename/path to save to get one. */
book = gnc_get_current_book_internal ();
session = gnc_get_current_session_internal ();
if (!gnc_book_get_file_path (book))
if (!gnc_session_get_file_path (session))
{
gnc_file_save_as ();
return;
@@ -532,16 +531,16 @@ gnc_file_save (void)
/* use the current session to save to file */
gnc_set_busy_cursor (NULL, TRUE);
gnc_book_save (book);
gnc_session_save (session);
gnc_unset_busy_cursor (NULL);
/* Make sure everything's OK - disk could be full, file could have
become read-only etc. */
newfile = gnc_book_get_file_path (book);
io_err = gnc_book_get_error (book);
newfile = gnc_session_get_file_path (session);
io_err = gnc_session_get_error (session);
if (ERR_BACKEND_NO_ERR != io_err)
{
show_book_error (io_err, newfile);
show_session_error (io_err, newfile);
if (been_here_before) return;
been_here_before = TRUE;
@@ -550,13 +549,13 @@ gnc_file_save (void)
return;
}
gnc_add_history (book);
gnc_add_history (session);
gnc_book_mark_saved (book);
gnc_book_mark_saved (gnc_session_get_book (session));
/* save the main window state */
gh_call1 (gh_eval_str("gnc:main-window-save-state"),
gh_str02scm(gnc_book_get_url(current_book)));
gh_str02scm(gnc_session_get_url(current_session)));
LEAVE (" ");
}
@@ -564,11 +563,8 @@ gnc_file_save (void)
void
gnc_file_save_as (void)
{
AccountGroup *group;
GNCPriceDB *pdb;
GList *sxList;
AccountGroup *templateGroup;
GNCBook *new_book;
GNCSession *new_session;
GNCSession *session;
GNCBook *book;
const char *filename;
char *newfile;
@@ -585,12 +581,12 @@ gnc_file_save_as (void)
newfile = xaccResolveURL (filename);
if (!newfile)
{
show_book_error (ERR_FILEIO_FILE_NOT_FOUND, filename);
show_session_error (ERR_FILEIO_FILE_NOT_FOUND, filename);
return;
}
book = gnc_get_current_book_internal ();
oldfile = gnc_book_get_file_path (book);
session = gnc_get_current_session_internal ();
oldfile = gnc_session_get_file_path (session);
if (oldfile && (strcmp(oldfile, newfile) == 0))
{
g_free (newfile);
@@ -600,26 +596,23 @@ gnc_file_save_as (void)
/* -- this session code is NOT identical in FileOpen and FileSaveAs -- */
// FIXME: this might want to be a function of the GNCBook, since it
// needs to sync with changes to the internals/structure of
// GNCBook... --jsled
group = gnc_book_get_group(book);
pdb = gnc_book_get_pricedb(book);
sxList = gnc_book_get_schedxactions(book);
templateGroup = gnc_book_get_template_group(book);
/* FIXME: this might want to be a function of the GNCSession, since
* it needs to sync with changes to the internals/structure of
* GNCSession. */
book = gnc_session_get_book (session);
new_book = gnc_book_new ();
gnc_book_begin (new_book, newfile, FALSE, FALSE);
new_session = gnc_session_new ();
gnc_session_begin (new_session, newfile, FALSE, FALSE);
io_err = gnc_book_get_error (new_book);
io_err = gnc_session_get_error (new_session);
/* if file appears to be locked, ask the user ... */
if (ERR_BACKEND_LOCKED == io_err)
{
if (FALSE == show_book_error (io_err, newfile))
if (FALSE == show_session_error (io_err, newfile))
{
/* user told us to ignore locks. So ignore them. */
gnc_book_begin (new_book, newfile, TRUE, FALSE);
gnc_session_begin (new_session, newfile, TRUE, FALSE);
}
}
@@ -627,37 +620,37 @@ gnc_file_save_as (void)
else if ((ERR_BACKEND_NO_SUCH_DB == io_err) ||
(ERR_SQL_DB_TOO_OLD == io_err))
{
if (FALSE == show_book_error (io_err, newfile))
{
/* user told us to create a new database. Do it. */
gnc_book_begin (new_book, newfile, FALSE, TRUE);
}
if (FALSE == show_session_error (io_err, newfile))
{
/* user told us to create a new database. Do it. */
gnc_session_begin (new_session, newfile, FALSE, TRUE);
}
}
/* check again for session errors (since above dialog may have
* cleared a file lock & moved things forward some more)
* This time, errors will be fatal.
*/
io_err = gnc_book_get_error (new_book);
io_err = gnc_session_get_error (new_session);
if (ERR_BACKEND_NO_ERR != io_err)
{
show_book_error (io_err, newfile);
gnc_book_destroy (new_book);
show_session_error (io_err, newfile);
gnc_session_destroy (new_session);
g_free (newfile);
return;
}
/* if we got to here, then we've successfully gotten a new session */
/* close up the old file session (if any) */
gnc_book_set_group(book, NULL);
gnc_book_set_pricedb(book, NULL);
gnc_book_destroy (book);
current_book = new_book;
gnc_session_set_book (session, NULL);
gnc_session_destroy (session);
current_session = new_session;
/* --------------- END CORE SESSION CODE -------------- */
/* oops ... file already exists ... ask user what to do... */
if (gnc_book_save_may_clobber_data (new_book))
if (gnc_session_save_may_clobber_data (new_session))
{
const char *format = _("The file \n %s\n already exists.\n"
"Are you sure you want to overwrite it?");
@@ -679,10 +672,7 @@ gnc_file_save_as (void)
}
/* OK, save the data to the file ... */
gnc_book_set_group(new_book, group);
gnc_book_set_pricedb(new_book, pdb);
gnc_book_set_schedxactions(new_book, sxList);
gnc_book_set_template_group(new_book, templateGroup);
gnc_session_set_book (new_session, book);
gnc_file_save ();
@@ -693,9 +683,9 @@ gnc_file_save_as (void)
void
gnc_file_quit (void)
{
GNCBook *book;
GNCSession *session;
book = gnc_get_current_book_internal ();
session = gnc_get_current_session_internal ();
/* disable events; otherwise the mass deletetion of accounts and
* transactions during shutdown would cause massive redraws */
@@ -703,10 +693,10 @@ gnc_file_quit (void)
gh_call2(gh_eval_str("gnc:hook-run-danglers"),
gh_eval_str("gnc:*book-closed-hook*"),
gh_str02scm(gnc_book_get_url(book)));
gh_str02scm(gnc_session_get_url(session)));
gnc_book_destroy (book);
current_book = NULL;
gnc_session_destroy (session);
current_session = NULL;
gnc_engine_resume_events ();
gnc_gui_refresh_all ();

View File

@@ -114,8 +114,6 @@
#include <glib.h>
#include "gnc-book.h"
void gnc_file_new (void);
gboolean gnc_file_open (void);
void gnc_file_save (void);

View File

@@ -52,7 +52,7 @@ static gboolean reverse_balance_inited = FALSE;
static SCM reverse_balance_callback_id = SCM_UNDEFINED;
static gboolean reverse_type[NUM_ACCOUNT_TYPES];
static GNCBookCB book_cb = NULL;
static GNCSessionCB session_cb = NULL;
/********************************************************************\
@@ -198,30 +198,30 @@ gnc_reverse_balance (Account *account)
}
void
gnc_set_current_book_handler (GNCBookCB cb)
gnc_set_current_session_handler (GNCSessionCB cb)
{
book_cb = cb;
session_cb = cb;
}
GNCSession *
gnc_get_current_session (void)
{
if (session_cb)
return session_cb ();
return NULL;
}
GNCBook *
gnc_get_current_book (void)
{
GNCBook *book;
if (book_cb)
return book_cb ();
return NULL;
return gnc_session_get_book (gnc_get_current_session ());
}
AccountGroup *
gnc_get_current_group (void)
{
GNCBook *book;
book = gnc_get_current_book ();
return gnc_book_get_group (book);
return gnc_book_get_group (gnc_get_current_book ());
}
const char *

View File

@@ -28,12 +28,10 @@
#include <glib.h>
#include <locale.h>
#include "gnc-book.h"
#include "gnc-commodity.h"
#include "gnc-numeric.h"
#include "gnc-session.h"
typedef GNCBook * (*GNCBookCB) (void);
typedef GNCSession * (*GNCSessionCB) (void);
/* User Settings ****************************************************/
@@ -46,8 +44,9 @@ gboolean gnc_reverse_balance_type(GNCAccountType type);
/* Engine enhancements & i18n ***************************************/
void gnc_set_current_book_handler (GNCBookCB cb);
void gnc_set_current_session_handler (GNCSessionCB cb);
GNCSession * gnc_get_current_session (void);
GNCBook * gnc_get_current_book (void);
AccountGroup * gnc_get_current_group (void);

View File

@@ -23,8 +23,7 @@
#include "io-gncxml-v2.h"
#include "gnc-backend-api.h"
#include "gnc-book.h"
#include "gnc-book-p.h"
#include "gnc-session.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
@@ -39,7 +38,7 @@ struct FileBackend_struct
char *linkfile;
int lockfd;
GNCBook *book;
GNCSession *session;
};
typedef struct FileBackend_struct FileBackend;
@@ -58,8 +57,8 @@ static gboolean gnc_file_be_write_to_file(FileBackend *be,
gboolean make_backup);
static void
file_book_begin(Backend *be_start, GNCBook *book, const char *book_id,
gboolean ignore_lock, gboolean create_if_nonexistent)
file_session_begin(Backend *be_start, GNCSession *session, const char *book_id,
gboolean ignore_lock, gboolean create_if_nonexistent)
{
FileBackend* be;
char *dirname;
@@ -67,13 +66,14 @@ file_book_begin(Backend *be_start, GNCBook *book, const char *book_id,
ENTER (" ");
be = (FileBackend*)be_start;
be->book = book;
be = (FileBackend*) be_start;
be->session = session;
/* Make sure the directory is there */
dirname = g_strdup (book->fullpath);
be->fullpath = g_strdup (book->fullpath);
dirname = g_strdup (gnc_session_get_file_path (session));
be->fullpath = g_strdup (dirname);
p = strrchr (dirname, '/');
if (p && p != dirname)
{
@@ -116,51 +116,29 @@ file_load_file(Backend *be)
if(!gnc_file_be_load_from_file((FileBackend*)be))
{
xaccBackendSetError(be, ERR_BACKEND_MISC);
g_free(((FileBackend*)be)->lockfile); ((FileBackend*)be)->lockfile = NULL;
g_free(((FileBackend*)be)->lockfile);
((FileBackend*)be)->lockfile = NULL;
return FALSE;
}
return TRUE;
}
static AccountGroup*
file_book_load(Backend *be)
static void
file_book_load (Backend *be)
{
AccountGroup* ret = gnc_book_get_group(((FileBackend*)be)->book);
if(ret == NULL)
if (!file_load_file(be))
{
if(!file_load_file(be))
{
PERR("file_load_file returned FALSE");
return NULL;
}
PERR("file_load_file returned FALSE");
}
ret = gnc_book_get_group(((FileBackend*)be)->book);
return ret;
}
static GNCPriceDB*
file_price_load(Backend *be)
{
GNCPriceDB* ret = gnc_book_get_pricedb(((FileBackend*)be)->book);
if(ret == NULL)
{
if(!file_load_file(be))
{
PERR("file_load_file returned FALSE");
return NULL;
}
}
ret = gnc_book_get_pricedb(((FileBackend*)be)->book);
return ret;
}
static void
file_book_end(Backend *be_start)
file_session_end(Backend *be_start)
{
FileBackend* be;
be = (FileBackend*)be_start;
if (be->linkfile)
unlink (be->linkfile);
@@ -187,7 +165,7 @@ file_destroy_backend(Backend *be)
}
static void
file_all_sync(Backend* be, AccountGroup *ag, GNCPriceDB *pricedb)
file_sync_all(Backend* be, GNCBook *book)
{
gnc_file_be_write_to_file((FileBackend*)be, TRUE);
}
@@ -202,10 +180,10 @@ gnc_backend_new(void)
be = (Backend*)fbe;
xaccInitBackend(be);
be->book_begin = file_book_begin;
be->session_begin = file_session_begin;
be->book_load = file_book_load;
be->price_load = file_price_load;
be->book_end = file_book_end;
be->price_load = NULL;
be->session_end = file_session_end;
be->destroy_backend = file_destroy_backend;
/* be->account_begin_edit = file_account_begin_edit; */
@@ -218,7 +196,7 @@ gnc_backend_new(void)
/* be->run_query = file_run_query; */
/* be->price_lookup = file_price_lookup; */
be->all_sync = file_all_sync;
be->sync_all = file_sync_all;
/* be->events_pending = file_events_pending; */
/* be->process_events = file_process_events; */
@@ -228,8 +206,8 @@ gnc_backend_new(void)
fbe->linkfile = NULL;
fbe->lockfd = -1;
fbe->book = NULL;
fbe->session = NULL;
return be;
}
@@ -386,20 +364,20 @@ gnc_file_be_load_from_file(FileBackend *be)
{
case GNC_BOOK_XML2_FILE:
return happy_or_push_error((Backend*)be,
gnc_book_load_from_xml_file_v2(be->book,
NULL),
gnc_session_load_from_xml_file_v2
(be->session, NULL),
ERR_BACKEND_MISC);
case GNC_BOOK_XML1_FILE:
return happy_or_push_error((Backend*)be,
gnc_book_load_from_xml_file(be->book),
gnc_session_load_from_xml_file(be->session),
ERR_BACKEND_MISC);
case GNC_BOOK_BIN_FILE:
{
/* presume it's an old-style binary file */
GNCBackendError error;
gnc_book_load_from_binfile(be->book);
error = gnc_book_get_binfile_io_error();
gnc_session_load_from_binfile(be->session);
error = gnc_get_binfile_io_error();
if(error == ERR_BACKEND_NO_ERR) {
return TRUE;
@@ -552,6 +530,9 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
{
const gchar *datafile;
char *tmp_name;
GNCBook *book;
book = gnc_session_get_book (be->session);
datafile = be->fullpath;
@@ -573,7 +554,7 @@ gnc_file_be_write_to_file(FileBackend *be, gboolean make_backup)
}
}
if(gnc_book_write_to_xml_file_v2(be->book, tmp_name))
if(gnc_book_write_to_xml_file_v2(book, tmp_name))
{
if(unlink(datafile) != 0 && errno != ENOENT)
{

View File

@@ -286,7 +286,7 @@ static int readTSDate( int fd, Timespec *, int token );
/*******************************************************/
GNCBackendError
gnc_book_get_binfile_io_error(void)
gnc_get_binfile_io_error(void)
{
/* reset the error code */
int rc = error_code;
@@ -495,8 +495,8 @@ gnc_load_financials_from_fd(GNCBook *book, int fd)
GNCPriceDB *tmpdb;
if(cvt_potential_prices_to_pricedb_and_cleanup(&tmpdb)) {
GNCPriceDB *db = gnc_book_get_pricedb(book);
if(db) gnc_pricedb_destroy(db);
gnc_book_set_pricedb(book, tmpdb);
if(db) gnc_pricedb_destroy(db);
} else {
PWARN("pricedb import failed.");
error_code = ERR_BACKEND_MISC;
@@ -508,8 +508,8 @@ gnc_load_financials_from_fd(GNCBook *book, int fd)
{
AccountGroup *g = gnc_book_get_group(book);
if (g) xaccFreeAccountGroup(g);
gnc_book_set_group(book, grp);
if (g) xaccFreeAccountGroup(g);
}
/* mark the newly read book as saved, since the act of putting it
@@ -527,17 +527,17 @@ gnc_load_financials_from_fd(GNCBook *book, int fd)
* Return: the struct with the program data in it *
\********************************************************************/
void
gnc_book_load_from_binfile(GNCBook *book)
gnc_session_load_from_binfile(GNCSession *session)
{
int fd;
const gchar *datafile = gnc_book_get_file_path(book);
const gchar *datafile = gnc_session_get_file_path(session);
if(!datafile) {
error_code = ERR_BACKEND_MISC;
return;
}
maingrp = 0x0;
maingrp = NULL;
error_code = ERR_BACKEND_NO_ERR;
fd = open( datafile, RFLAGS, 0 );
@@ -546,10 +546,10 @@ gnc_book_load_from_binfile(GNCBook *book)
return;
}
if(!gnc_load_financials_from_fd(book, fd)) return;
if (!gnc_load_financials_from_fd(gnc_session_get_book (session), fd))
return;
close(fd);
return;
}
/********************************************************************\

View File

@@ -31,7 +31,7 @@
#define IO_GNCBIN_H
#include "Backend.h"
#include "gnc-book.h"
#include "gnc-session.h"
/** PROTOTYPES ******************************************************/
@@ -40,17 +40,16 @@
* are not inherently safe against file-locking errors. For direct
* file IO, the gnc-book's higher level functions should be used.
*
* gnc_book_load_from_binfile() will load the financial data
* gnc_session_load_from_binfile() will load the financial data
* represented by the book's file_path into the indicated book.
*
* gnc_book_get_binfile_io_error() will return an error code for any
* error detected that occured during reading or writing. It will
* reset the error code after being called. The current
* implementation can be thought of as a "stack of depth one", and
* this routine as a "pop". Future implementations may have a
* deeper stack.
* */
void gnc_book_load_from_binfile(GNCBook *book);
GNCBackendError gnc_book_get_binfile_io_error(void);
* gnc_get_binfile_io_error() will return an error code for any error
* detected that occured during reading or writing. It will reset the
* error code after being called. The current implementation can be
* thought of as a "stack of depth one", and this routine as a "pop".
* Future implementations may have a deeper stack.
*/
void gnc_session_load_from_binfile(GNCSession *session);
GNCBackendError gnc_get_binfile_io_error(void);
#endif /* IO_GNCBIN_H */

View File

@@ -343,17 +343,21 @@ gncxml_setup_for_read (GNCParseStatus *global_parse_status)
/* ================================================================== */
gboolean
gnc_book_load_from_xml_file(GNCBook *book)
gnc_session_load_from_xml_file(GNCSession *session)
{
gboolean parse_ok;
gpointer parse_result = NULL;
sixtp *top_level_pr;
GNCParseStatus global_parse_status;
const gchar *filename;
GNCBook *book;
book = gnc_session_get_book (session);
g_return_val_if_fail(session, FALSE);
g_return_val_if_fail(book, FALSE);
filename = gnc_book_get_file_path(book);
filename = gnc_session_get_file_path(session);
g_return_val_if_fail(filename, FALSE);
top_level_pr = gncxml_setup_for_read (&global_parse_status);
@@ -374,25 +378,26 @@ gnc_book_load_from_xml_file(GNCBook *book)
{
AccountGroup *g = gnc_book_get_group(book);
if(g) xaccFreeAccountGroup(g);
gnc_book_set_group(book, global_parse_status.account_group);
if(g) xaccFreeAccountGroup(g);
}
if(global_parse_status.pricedb)
{
GNCPriceDB *db = gnc_book_get_pricedb(book);
if(db) gnc_pricedb_destroy(db);
gnc_book_set_pricedb(book, global_parse_status.pricedb);
if(db) gnc_pricedb_destroy(db);
}
else
{
GNCPriceDB *db = gnc_book_get_pricedb(book);
if(db) gnc_pricedb_destroy(db);
gnc_book_set_pricedb(book, gnc_pricedb_create());
if(db) gnc_pricedb_destroy(db);
}
/* Fix account and transaction commodities */

View File

@@ -393,16 +393,19 @@ generic_callback(const char *tag, gpointer globaldata, gpointer data)
}
gboolean
gnc_book_load_from_xml_file_v2(
GNCBook *book,
gnc_session_load_from_xml_file_v2(
GNCSession *session,
void (*countcallback)(const char *type, load_counter count))
{
GNCBook *book;
sixtp_gdv2 *gd;
sixtp *top_parser;
sixtp *main_parser;
gd = g_new0(sixtp_gdv2, 1);
book = gnc_session_get_book (session);
gd->book = book;
gd->counter.accounts_loaded = 0;
gd->counter.accounts_total = 0;
@@ -417,12 +420,12 @@ gnc_book_load_from_xml_file_v2(
{
AccountGroup *g = gnc_book_get_group(book);
if(g) xaccFreeAccountGroup(g);
gnc_book_set_group(book, xaccMallocAccountGroup());
if(g) xaccFreeAccountGroup(g);
}
gd->countCallback = countcallback;
top_parser = sixtp_new();
main_parser = sixtp_new();
@@ -451,7 +454,7 @@ gnc_book_load_from_xml_file_v2(
/* stop logging while we load */
xaccLogDisable ();
if(!gnc_xml_parse_file(top_parser, gnc_book_get_file_path(book),
if(!gnc_xml_parse_file(top_parser, gnc_session_get_file_path(session),
generic_callback, gd))
{
sixtp_destroy(top_parser);

View File

@@ -33,14 +33,14 @@
#include <glib.h>
#include "gnc-book.h"
#include "gnc-session.h"
#include "Account.h"
#include "Transaction.h"
#include "gnc-commodity.h"
#include "gnc-pricedb.h"
#include "SchedXaction.h"
struct _load_counter_struct
typedef struct
{
int accounts_total;
int accounts_loaded;
@@ -56,9 +56,7 @@ struct _load_counter_struct
int schedXactions_total;
int schedXactions_loaded;
};
typedef struct _load_counter_struct load_counter;
} load_counter;
struct sixtp_global_data_v2_struct
{
@@ -73,7 +71,7 @@ struct sixtp_global_data_v2_struct
* gnc-schedxactions-xml-v2.c and the add-to-book callback in
* io-gncxml-v2.c.
**/
typedef struct _gnc_template_xaction_data
typedef struct
{
GList *accts;
GList *transactions;
@@ -82,8 +80,8 @@ typedef struct _gnc_template_xaction_data
typedef struct sixtp_global_data_v2_struct sixtp_gdv2;
/* read in an account group from a file */
gboolean gnc_book_load_from_xml_file_v2(
GNCBook *book,
gboolean gnc_session_load_from_xml_file_v2(
GNCSession *session,
void (*countcallback)(const char *type, load_counter count));
/* write all account info to a file */

View File

@@ -34,14 +34,14 @@
#include <glib.h>
#include "gnc-book.h"
#include "gnc-session.h"
#include "Query.h"
/* FIXME: eventually, we probably need to add an error stack
accessable via gnc_book_get_xml_io_error() a la binfile. */
/* read in an account group from a file */
gboolean gnc_book_load_from_xml_file(GNCBook *book);
gboolean gnc_session_load_from_xml_file(GNCSession *session);
/* The is_gncxml_file() routine checks to see if the first few
* chars of the file look like gnc-xml data.

View File

@@ -9,8 +9,7 @@
#include "Backend.h"
#include "TransLog.h"
#include "gnc-book.h"
#include "gnc-book.h"
#include "gnc-session.h"
#include "gnc-engine.h"
#include "gnc-module.h"
#include "io-gncxml-v2.h"
@@ -45,22 +44,28 @@ remove_locks(const char *filename)
static void
test_load_file(const char *filename)
{
GNCSession *session;
GNCBook *book;
book = gnc_book_new();
session = gnc_session_new();
remove_locks(filename);
gnc_book_begin(book, filename, FALSE, FALSE);
gnc_session_begin(session, filename, FALSE, FALSE);
gnc_book_load_from_xml_file_v2(book, NULL);
gnc_session_load_from_xml_file_v2(session, NULL);
book = gnc_session_get_book (session);
do_test (xaccGroupGetBook (gnc_book_get_group (book)) == book,
"book and group don't match");
do_test_args(
gnc_book_get_error(book) == ERR_BACKEND_NO_ERR,
"book load xml2", __FILE__, __LINE__, "%d for file %s",
gnc_book_get_error(book), filename);
gnc_session_get_error(session) == ERR_BACKEND_NO_ERR,
"session load xml2", __FILE__, __LINE__, "%d for file %s",
gnc_session_get_error(session), filename);
gnc_book_destroy(book);
gnc_session_destroy(session);
}
static void

View File

@@ -68,43 +68,43 @@ test_file(const char *filename)
for(i = 0; possible_envs[i] != NULL; i++)
{
GNCBook *book;
GNCSession *session;
char *cmd;
char *new_file = gen_new_file_name(filename, possible_envs[i]);
char *putenv_str;
GNCBook *new_book;
GNCSession *new_session;
book = gnc_book_new();
session = gnc_session_new();
if(!gnc_book_begin(book, filename, TRUE, FALSE))
if(!gnc_session_begin(session, filename, TRUE, FALSE))
{
gnc_book_destroy(book);
return g_strdup("gnc_book_begin");
gnc_session_destroy(session);
return g_strdup("gnc_session_begin");
}
if(!gnc_book_load(book))
if(!gnc_session_load(session))
{
int error = gnc_book_get_error(book);
gnc_book_destroy(book);
return g_strdup_printf("gnc_book_load errorid %d", error);
int error = gnc_session_get_error(session);
gnc_session_destroy(session);
return g_strdup_printf("gnc_session_load errorid %d", error);
}
putenv_str = g_strdup_printf ("%s=%s", "LANG", possible_envs[i]);
putenv (putenv_str);
g_free (putenv_str);
new_book = gnc_book_new();
new_session = gnc_session_new();
if(!gnc_book_begin(new_book, new_file, FALSE, FALSE))
if(!gnc_session_begin(new_session, new_file, FALSE, FALSE))
{
g_free(new_file);
gnc_book_destroy(book);
gnc_book_destroy(new_book);
return g_strdup_printf("gnc_book_begin 2 with LANG=%s",
gnc_session_destroy(session);
gnc_session_destroy(new_session);
return g_strdup_printf("gnc_session_begin 2 with LANG=%s",
possible_envs[i]);
}
gnc_book_save(new_book);
gnc_session_save(new_session);
cmd = g_strdup_printf(diff_command, filename, new_file);
@@ -112,16 +112,16 @@ test_file(const char *filename)
{
g_free(cmd);
g_free(new_file);
gnc_book_destroy(book);
gnc_book_destroy(new_book);
gnc_session_destroy(session);
gnc_session_destroy(new_session);
return g_strdup_printf("run_command_get_return with LANG=%s",
possible_envs[i]);
}
g_free(new_file);
g_free(cmd);
gnc_book_destroy(book);
gnc_book_destroy(new_book);
gnc_session_destroy(session);
gnc_session_destroy(new_session);
}
return NULL;

View File

@@ -135,6 +135,14 @@ pgendGetUserGecos (PGBackend *be)
return NULL;
}
AccountGroup *
pgendGetTopGroup (PGBackend *be)
{
if (!be) return NULL;
return gnc_book_get_group (gnc_session_get_book (be->session));
}
/* ============================================================= */
/* This routine finds the commodity by parsing a string
* of the form NAMESPACE::MNEMONIC
@@ -490,6 +498,7 @@ pgendRunQuery (Backend *bend, Query *q)
{
PGBackend *be = (PGBackend *)bend;
const char * sql_query_string;
AccountGroup *topgroup;
sqlQuery *sq;
GList *node, *anode, *xaction_list= NULL, *acct_list = NULL;
@@ -505,8 +514,10 @@ pgendRunQuery (Backend *bend, Query *q)
sq = sqlQuery_new();
sql_query_string = sqlQuery_build (sq, q);
topgroup = pgendGetTopGroup (be);
/* stage transactions, save some postgres overhead */
xaccGroupBeginStagedTransactionTraversals (be->topgroup);
xaccGroupBeginStagedTransactionTraversals (topgroup);
/* We will be doing a bulk insertion of transactions below.
* We can gain a tremendous performance improvement,
@@ -521,16 +532,16 @@ pgendRunQuery (Backend *bend, Query *q)
* by not very much.
*/
ncalls = 0;
xaccAccountGroupBeginEdit(be->topgroup);
xaccAccountGroupBeginEdit(topgroup);
pgendFillOutToCheckpoint (be, sql_query_string);
xaccAccountGroupCommitEdit(be->topgroup);
xaccAccountGroupCommitEdit(topgroup);
PINFO ("number of calls to fill out=%d", ncalls);
sql_Query_destroy(sq);
/* the fill-out will dirty a lot of data. That's irrelevent,
* mark it all as having been saved. */
xaccGroupMarkSaved (be->topgroup);
xaccGroupMarkSaved (topgroup);
pgendEnable(be);
gnc_engine_resume_events();
@@ -641,9 +652,11 @@ pgendGetAllTransactions (PGBackend *be, AccountGroup *grp)
static void
pgendSync (Backend *bend, AccountGroup *grp)
pgendSync (Backend *bend, GNCBook *book)
{
PGBackend *be = (PGBackend *)bend;
AccountGroup *grp = gnc_book_get_group (book);
ENTER ("be=%p, grp=%p", be, grp);
be->version_check = (guint32) time(0);
@@ -714,12 +727,14 @@ trans_traverse_cb (Transaction *trans, void *cb_data)
}
static void
pgendSyncSingleFile (Backend *bend, AccountGroup *grp)
pgendSyncSingleFile (Backend *bend, GNCBook *book)
{
char *p;
PGBackend *be = (PGBackend *)bend;
AccountGroup *grp = gnc_book_get_group (book);
ENTER ("be=%p, grp=%p", be, grp);
p = "BEGIN;\n"
"LOCK TABLE gncAccount IN EXCLUSIVE MODE;\n"
"LOCK TABLE gncCommodity IN EXCLUSIVE MODE;\n"
@@ -852,14 +867,14 @@ pgendSessionGetMode (PGBackend *be)
/* ============================================================= */
/* Instead of loading the book, just set the lock error */
static AccountGroup *
static void
pgend_book_load_single_lockerr (Backend *bend)
{
PGBackend *be = (PGBackend *)bend;
if (!be) return NULL;
if (!be) return;
xaccBackendSetError (&be->be, ERR_BACKEND_LOCKED);
return NULL;
}
/* ============================================================= */
@@ -1139,8 +1154,8 @@ pgend_session_end (Backend *bend)
/* prevent further callbacks into backend */
pgendDisable(be);
be->be.book_begin = NULL;
be->be.book_end = NULL;
be->be.session_begin = NULL;
be->be.session_end = NULL;
/* note the logoff time in the session directory */
pgendSessionEnd (be);
@@ -1177,13 +1192,16 @@ pgend_session_end (Backend *bend)
* and never the transactions, need to be loaded.
*/
static AccountGroup *
static void
pgend_book_load_poll (Backend *bend)
{
Timespec ts = gnc_iso8601_to_timespec_local (CK_BEFORE_LAST_DATE);
AccountGroup *grp;
PGBackend *be = (PGBackend *)bend;
if (!be) return NULL;
if (!be) return;
grp = pgendGetTopGroup (be);
/* don't send events to GUI, don't accept callbacks to backend */
gnc_engine_suspend_events();
@@ -1191,39 +1209,12 @@ pgend_book_load_poll (Backend *bend)
be->version_check = (guint32) time(0);
pgendKVPInit(be);
grp = pgendGetAllAccounts (be, NULL);
pgendGetAllAccounts (be, grp);
pgendGroupGetAllBalances (be, grp, ts);
/* re-enable events */
pgendEnable(be);
gnc_engine_resume_events();
be->topgroup = grp;
return grp;
}
/* ============================================================= */
/* The pgend_price_load_poll() routine creates the pricedb, but
* doesn't actually put any prices in it. These are polled on
* an as-needed basis.
*/
static GNCPriceDB *
pgend_price_load_poll (Backend *bend)
{
GNCPriceDB *prdb;
PGBackend *be = (PGBackend *)bend;
if (!be) return NULL;
/* don't send events to GUI */
gnc_engine_suspend_events();
prdb = gnc_pricedb_create();
/* re-enable events */
gnc_engine_resume_events();
return prdb;
}
/* ============================================================= */
@@ -1234,12 +1225,15 @@ pgend_price_load_poll (Backend *bend)
* not handled.
*/
static AccountGroup *
static void
pgend_book_load_single (Backend *bend)
{
AccountGroup *grp;
PGBackend *be = (PGBackend *)bend;
if (!be) return NULL;
if (!be) return;
grp = pgendGetTopGroup (be);
/* don't send events to GUI, don't accept callbacks to backend */
gnc_engine_suspend_events();
@@ -1247,15 +1241,12 @@ pgend_book_load_single (Backend *bend)
be->version_check = (guint32) time(0);
pgendKVPInit(be);
grp = pgendGetAllAccounts (be, NULL);
pgendGetAllAccounts (be, grp);
pgendGetMassTransactions (be, grp);
/* re-enable events */
pgendEnable(be);
gnc_engine_resume_events();
be->topgroup = grp;
return grp;
}
/* ============================================================= */
@@ -1263,25 +1254,28 @@ pgend_book_load_single (Backend *bend)
* price data from the database.
*/
static GNCPriceDB *
static void
pgend_price_load_single (Backend *bend)
{
GNCPriceDB *prdb;
PGBackend *be = (PGBackend *)bend;
if (!be) return NULL;
GNCBook *book;
GNCPriceDB *db;
if (!be) return;
/* don't send events to GUI, don't accept callbacks to backend */
gnc_engine_suspend_events();
pgendDisable(be);
be->version_check = (guint32) time(0);
prdb = pgendGetAllPrices (be, NULL);
book = gnc_session_get_book (be->session);
db = gnc_book_get_pricedb (book);
pgendGetAllPrices (be, db);
/* re-enable events */
pgendEnable(be);
gnc_engine_resume_events();
return prdb;
}
/* ============================================================= */
@@ -1305,8 +1299,11 @@ db_exists_cb (PGBackend *be, PGresult *result, int j, gpointer data)
static void
pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
gboolean ignore_lock, gboolean create_new_db)
pgend_session_begin (Backend *backend,
GNCSession *session,
const char * sessionid,
gboolean ignore_lock,
gboolean create_new_db)
{
int really_do_create = 0;
int rc;
@@ -1320,7 +1317,7 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
if (!backend) return;
be = (PGBackend*)backend;
ENTER("be=%p, sessionid=%s", be,
sessionid ? sessionid : "(null)");
@@ -1328,6 +1325,8 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
pgend_session_end ((Backend *) be);
pgendInit (be);
be->session = session;
/* Parse the sessionid for the hostname, port number and db name.
* The expected URL format is
* postgres://some.host.com/db_name
@@ -1783,13 +1782,13 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
be->be.price_commit_edit = NULL;
be->be.run_query = NULL;
be->be.price_lookup = NULL;
be->be.sync = pgendSyncSingleFile;
be->be.sync_all = pgendSyncSingleFile;
be->be.sync_price = pgendSyncPriceDBSingleFile;
be->be.events_pending = NULL;
be->be.process_events = NULL;
PWARN ("mode=single-file is final beta -- \n"
"we've fixed all known bugs but that doesn't mean\n"
"there aren't any! We think its safe to use.\n");
"there aren't any! We think it's safe to use.\n");
break;
case MODE_SINGLE_UPDATE:
@@ -1805,19 +1804,19 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
be->be.price_commit_edit = pgend_price_commit_edit;
be->be.run_query = NULL;
be->be.price_lookup = NULL;
be->be.sync = pgendSync;
be->be.sync_all = pgendSync;
be->be.sync_price = pgendSyncPriceDB;
be->be.events_pending = NULL;
be->be.process_events = NULL;
PWARN ("mode=single-update is final beta -- \n"
"we've fixed all known bugs but that doesn't mean\n"
"there aren't any! We think its safe to use.\n");
"there aren't any! We think it's safe to use.\n");
break;
case MODE_POLL:
pgendEnable(be);
be->be.book_load = pgend_book_load_poll;
be->be.price_load = pgend_price_load_poll;
be->be.price_load = NULL;
be->be.account_begin_edit = NULL;
be->be.account_commit_edit = pgend_account_commit_edit;
be->be.trans_begin_edit = NULL;
@@ -1827,7 +1826,7 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
be->be.price_commit_edit = pgend_price_commit_edit;
be->be.run_query = pgendRunQuery;
be->be.price_lookup = pgendPriceLookup;
be->be.sync = pgendSync;
be->be.sync_all = pgendSync;
be->be.sync_price = pgendSyncPriceDB;
be->be.events_pending = NULL;
be->be.process_events = NULL;
@@ -1844,7 +1843,7 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
pgendSessionSetupNotifies (be);
be->be.book_load = pgend_book_load_poll;
be->be.price_load = pgend_price_load_poll;
be->be.price_load = NULL;
be->be.account_begin_edit = NULL;
be->be.account_commit_edit = pgend_account_commit_edit;
be->be.trans_begin_edit = NULL;
@@ -1854,7 +1853,7 @@ pgend_session_begin (Backend *backend, GNCBook *book, const char * sessionid,
be->be.price_commit_edit = pgend_price_commit_edit;
be->be.run_query = pgendRunQuery;
be->be.price_lookup = pgendPriceLookup;
be->be.sync = pgendSync;
be->be.sync_all = pgendSync;
be->be.sync_price = pgendSyncPriceDB;
be->be.events_pending = pgendEventsPending;
be->be.process_events = pgendProcessEvents;
@@ -1898,7 +1897,7 @@ pgendDisable (PGBackend *be)
be->snr.price_commit_edit = be->be.price_commit_edit;
be->snr.run_query = be->be.run_query;
be->snr.price_lookup = be->be.price_lookup;
be->snr.sync = be->be.sync;
be->snr.sync_all = be->be.sync_all;
be->snr.sync_price = be->be.sync_price;
be->snr.events_pending = be->be.events_pending;
be->snr.process_events = be->be.process_events;
@@ -1912,7 +1911,7 @@ pgendDisable (PGBackend *be)
be->be.price_commit_edit = NULL;
be->be.run_query = NULL;
be->be.price_lookup = NULL;
be->be.sync = NULL;
be->be.sync_all = NULL;
be->be.sync_price = NULL;
be->be.events_pending = NULL;
be->be.process_events = NULL;
@@ -1941,7 +1940,7 @@ pgendEnable (PGBackend *be)
be->be.price_commit_edit = be->snr.price_commit_edit;
be->be.run_query = be->snr.run_query;
be->be.price_lookup = be->snr.price_lookup;
be->be.sync = be->snr.sync;
be->be.sync_all = be->snr.sync_all;
be->be.sync_price = be->snr.sync_price;
be->be.events_pending = be->snr.events_pending;
be->be.process_events = be->snr.process_events;
@@ -1968,8 +1967,9 @@ pgendInit (PGBackend *be)
/* generic backend handlers */
xaccInitBackend((Backend*)be);
be->be.book_begin = pgend_session_begin;
be->be.book_end = pgend_session_end;
be->be.session_begin = pgend_session_begin;
be->be.session_end = pgend_session_end;
be->nest_count = 0;
pgendDisable(be);
@@ -2015,7 +2015,7 @@ pgendInit (PGBackend *be)
}
be->ipath_max = 0;
be->topgroup = NULL;
be->session = NULL;
}
/* ============================================================= */

View File

@@ -107,15 +107,17 @@ struct _pgend {
int path_cache_size;
int ipath_max;
/* enginge data caches -- not used anywhere except in session_end */
AccountGroup *topgroup;
/* enginge data caches */
GNCSession *session;
};
/*
* pgendNew creates a new postgress backend
* pgendNew creates a new postgres backend
*/
Backend * pgendNew (void);
AccountGroup * pgendGetTopGroup (PGBackend *be);
void pgendDisable (PGBackend *be);
void pgendEnable (PGBackend *be);

View File

@@ -384,7 +384,7 @@ pgendCopyAccountToEngine (PGBackend *be, const GUID *acct_guid)
pbuff = stpcpy (pbuff, "';");
SEND_QUERY (be,be->buff, 0);
pgendGetResults (be, get_account_cb, be->topgroup);
pgendGetResults (be, get_account_cb, pgendGetTopGroup (be));
acc = xaccAccountLookup (acct_guid);
/* restore any kvp data associated with the transaction and splits */

View File

@@ -289,13 +289,13 @@ pgendProcessEvents (Backend *bend)
case GNC_EVENT_MODIFY:
/* if the remote user created an account, mirror it here */
pgendCopyAccountToEngine (be, &(ev->guid));
xaccGroupMarkSaved (be->topgroup);
xaccGroupMarkSaved (pgendGetTopGroup (be));
break;
case GNC_EVENT_DESTROY: {
Account * acc = xaccAccountLookup (&(ev->guid));
xaccAccountBeginEdit (acc);
xaccAccountDestroy (acc);
xaccGroupMarkSaved (be->topgroup);
xaccGroupMarkSaved (pgendGetTopGroup (be));
break;
}
}

View File

@@ -5,9 +5,9 @@
#include "Backend.h"
#include "TransLog.h"
#include "gnc-book.h"
#include "gnc-engine.h"
#include "gnc-module.h"
#include "gnc-session.h"
#include "test-stuff.h"
#include "test-engine-stuff.h"
@@ -17,46 +17,53 @@ run_test (void)
{
GNCBook *book;
GNCBook *book_db;
GNCSession *session;
GNCSession *session_db;
GNCBackendError io_err;
char *filename;
book = get_random_book ();
session = gnc_session_new ();
gnc_session_set_book (session, book);
filename = g_strdup ("postgres://localhost:7777/gnc_test?mode=single-file");
gnc_book_begin (book, filename, FALSE, TRUE);
gnc_session_begin (session, filename, FALSE, TRUE);
io_err = gnc_book_get_error (book);
io_err = gnc_session_get_error (session);
if (!do_test (io_err == ERR_BACKEND_NO_ERR, "Beginning gnc_test"))
return;
gnc_book_save (book);
gnc_session_save (session);
if (!do_test (io_err == ERR_BACKEND_NO_ERR, "Saving gnc_test"))
return;
gnc_book_end (book);
gnc_session_end (session);
if (!do_test (io_err == ERR_BACKEND_NO_ERR, "Ending gnc_test"))
return;
if (!do_test (gnc_book_get_url (book) == NULL, "book url not NULL"))
if (!do_test (gnc_session_get_url (session) == NULL, "session url not NULL"))
return;
book_db = gnc_book_new ();
session_db = gnc_session_new ();
gnc_book_begin (book_db, filename, FALSE, FALSE);
gnc_session_begin (session_db, filename, FALSE, FALSE);
g_free (filename);
io_err = gnc_book_get_error (book_db);
io_err = gnc_session_get_error (session_db);
if (!do_test (io_err == ERR_BACKEND_NO_ERR, "Beginning gnc_test load"))
return;
gnc_book_load (book_db);
gnc_session_load (session_db);
if (!do_test (io_err == ERR_BACKEND_NO_ERR, "Loading gnc_test"))
return;
gnc_book_end (book_db);
gnc_session_end (session_db);
if (!do_test (io_err == ERR_BACKEND_NO_ERR, "Ending gnc_test load"))
return;
book_db = gnc_session_get_book (session_db);
do_test (gnc_book_equal (book, book_db), "Books not equal");
}
@@ -90,7 +97,7 @@ guile_main (int argc, char **argv)
int
main (int argc, char ** argv)
{
/* getchar (); */
getchar ();
gh_enter (argc, argv, guile_main);

View File

@@ -102,7 +102,7 @@ static void rpcendEnable (RPCBackend *be)
be->be.price_commit_edit = be->snr.price_commit_edit;
be->be.run_query = be->snr.run_query;
be->be.price_lookup = be->snr.price_lookup;
be->be.sync = be->snr.sync;
be->be.sync_all = be->snr.sync_all;
be->be.sync_price = be->snr.sync_price;
}
@@ -125,7 +125,7 @@ static void rpcendDisable (RPCBackend *be)
be->snr.price_commit_edit = be->be.price_commit_edit;
be->snr.run_query = be->be.run_query;
be->snr.price_lookup = be->be.price_lookup;
be->snr.sync = be->be.sync;
be->snr.sync_all = be->be.sync_all;
be->snr.sync_price = be->be.sync_price;
/* And turn off future calls */
@@ -138,7 +138,7 @@ static void rpcendDisable (RPCBackend *be)
be->be.price_commit_edit = NULL;
be->be.run_query = NULL;
be->be.price_lookup = NULL;
be->be.sync = NULL;
be->be.sync_all = NULL;
be->be.sync_price = NULL;
}
@@ -329,7 +329,7 @@ static void rpcend_add_gnccommoditylist (RPCBackend *be, gnc_commoditylist *cl)
/*
* book_load will only load the commodity table and account tree
*/
static AccountGroup *
static void
rpcend_book_load (Backend *bend)
{
RPCBackend *be = (RPCBackend *)bend;
@@ -337,7 +337,7 @@ rpcend_book_load (Backend *bend)
gncrpc_ptr backend;
gncrpc_book_load_ret ret;
VERIFY_BE (be, NULL);
VERIFY_BEV (be);
ENTER ("be=%p", be);
@@ -350,7 +350,7 @@ rpcend_book_load (Backend *bend)
if (ret.error != 0) {
xaccBackendSetError (&be->be, ret.error);
return NULL;
return;
}
/* suspend events */
@@ -363,7 +363,11 @@ rpcend_book_load (Backend *bend)
/* Parse the AccountGroup */
ag = gnc_book_get_group (be->book);
if (!ag)
{
ag = xaccMallocAccountGroup ();
gnc_book_set_group (be->book, ag);
}
rpcend_add_gncacctlist (be, ag, ret.acctlist);
/* Mark the newly read group as saved, since the act of putting
@@ -375,22 +379,22 @@ rpcend_book_load (Backend *bend)
gnc_engine_resume_events ();
/* Free the RPC results */
CLNT_FREERES (be->client, (xdrproc_t)xdr_gncrpc_book_load_ret, (caddr_t)&ret);
CLNT_FREERES (be->client,
(xdrproc_t)xdr_gncrpc_book_load_ret,
(caddr_t)&ret);
LEAVE ("be=%p, ag=%p", be, ag);
return ag;
}
static GNCPriceDB *
static void
rpcend_price_load (Backend *bend)
{
/* XXX hack alert -- implement this */
PERR ("price loading not implemented");
return gnc_pricedb_create();
}
static void rpcend_book_end (Backend *bend)
static void
rpcend_session_end (Backend *bend)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_ptr backend;
@@ -732,7 +736,7 @@ static void rpcend_run_query (Backend *bend, Query *q)
}
static void
rpcend_sync (Backend *bend, AccountGroup *acctgrp)
rpcend_sync_all (Backend *bend, GNCBook *book)
{
RPCBackend *be = (RPCBackend *)bend;
gncrpc_sync1_args args1;
@@ -740,6 +744,8 @@ rpcend_sync (Backend *bend, AccountGroup *acctgrp)
gncrpc_sync2_args args2;
int ret2 = -1;
gnc_commodity_table *ct = gnc_book_get_commodity_table (be->book);
AccountGroup *acctgrp = gnc_book_get_group (be->book);
VERIFY_BEV (be);
ENTER ("be=%p, ag=%p", be, acctgrp);
@@ -855,10 +861,13 @@ static gboolean rpcend_process_events (Backend *bend)
return changed;
}
static void rpcend_book_begin (Backend *backend, GNCBook *book,
const char *book_id,
gboolean ignore_lock, gboolean create)
static void
rpcend_session_begin (Backend *backend,
GNCSession *session,
const char *book_id,
gboolean ignore_lock, gboolean create)
{
GNCBook *book;
RPCBackend *be;
char *url, *start, *end, *rest;
@@ -871,8 +880,10 @@ static void rpcend_book_begin (Backend *backend, GNCBook *book,
(ignore_lock == TRUE ? "true" : "false"),
(create == TRUE ? "true" : "false"));
book = gnc_session_get_book (session);
/* close any dangling sessions from before and then reinitialize */
rpcend_book_end ((Backend *)be);
rpcend_session_end ((Backend *)be);
rpcendInit (be);
/* Remember my book */
@@ -952,7 +963,7 @@ static void rpcend_book_begin (Backend *backend, GNCBook *book,
/* Setup callbacks */
/* XXX hack alert -- need to implement price saving/loading */
rpcendEnable (be);
be->be.book_end = rpcend_book_end;
be->be.session_end = rpcend_session_end;
be->be.book_load = rpcend_book_load;
be->be.price_load = rpcend_price_load;
be->be.account_begin_edit = rpcend_account_begin_edit;
@@ -964,7 +975,7 @@ static void rpcend_book_begin (Backend *backend, GNCBook *book,
be->be.price_commit_edit = rpcend_price_commit_edit;
be->be.run_query = rpcend_run_query;
be->be.price_lookup = rpcend_price_lookup;
be->be.sync = rpcend_sync;
be->be.sync_all = rpcend_sync_all;
be->be.sync_price = rpcend_sync_price;
be->be.events_pending = rpcend_events_pending;
be->be.process_events = rpcend_process_events;
@@ -977,7 +988,7 @@ rpcendInit (RPCBackend *be)
{
/* The only callback that should work is Begin */
xaccInitBackend((Backend*)be);
be->be.book_begin = rpcend_book_begin;
be->be.session_begin = rpcend_session_begin;
rpcendDisable (be);
be->be.last_err = ERR_BACKEND_NO_ERR;

View File

@@ -340,7 +340,7 @@ static gnc_splitlist * rpcend_build_splitlist (GList *lst)
split->reconciled = olds->reconciled;
split->date_reconciled = *((gncTimespec *)&(olds->date_reconciled));
split->value = *((gncNumeric *)&(olds->value));
split->damount = *((gncNumeric *)&(olds->damount));
split->damount = *((gncNumeric *)&(olds->amount));
new = malloc (sizeof (*new));
new->split = split;

View File

@@ -180,10 +180,10 @@ xaccPriceDBGetBackend (GNCPriceDB *prdb)
void
xaccInitBackend(Backend *be)
{
be->book_begin = NULL;
be->session_begin = NULL;
be->book_load = NULL;
be->price_load = NULL;
be->book_end = NULL;
be->session_end = NULL;
be->destroy_backend = NULL;
be->account_begin_edit = NULL;
@@ -196,8 +196,8 @@ xaccInitBackend(Backend *be)
be->run_query = NULL;
be->price_lookup = NULL;
be->all_sync = NULL;
be->sync = NULL;
be->sync_all = NULL;
be->sync_group = NULL;
be->sync_price = NULL;
be->events_pending = NULL;

View File

@@ -91,6 +91,6 @@ typedef enum {
} GNCBackendError;
/* NOTE: if you modify GNCBackendError, please update src/scm/gnc.gwp */
typedef struct _backend Backend;
typedef struct backend_s Backend;
#endif /* XACC_BACKEND_H */

View File

@@ -47,11 +47,11 @@
#include "Group.h"
#include "Query.h"
#include "Transaction.h"
#include "gnc-book.h"
#include "gnc-session.h"
#include "gnc-pricedb.h"
/*
* The book_begin() routine gives the backend a second initialization
* The session_begin() routine gives the backend a second initialization
* opportunity. It is suggested that the backend check that
* the URL is syntactically correct, and that it is actually
* reachable. This is probably(?) a good time to initialize
@@ -156,13 +156,16 @@
* a stack) of all the errors that have occurred.
*/
struct _backend
struct backend_s
{
void (*book_begin) (Backend *be, GNCBook *book, const char *book_id,
gboolean ignore_lock, gboolean create_if_nonexistent);
AccountGroup * (*book_load) (Backend *);
GNCPriceDB * (*price_load) (Backend *);
void (*book_end) (Backend *);
void (*session_begin) (Backend *be,
GNCSession *session,
const char *book_id,
gboolean ignore_lock,
gboolean create_if_nonexistent);
void (*book_load) (Backend *);
void (*price_load) (Backend *);
void (*session_end) (Backend *);
void (*destroy_backend) (Backend *);
void (*account_begin_edit) (Backend *, Account *);
@@ -176,13 +179,13 @@ struct _backend
void (*run_query) (Backend *, Query *);
void (*price_lookup) (Backend *, GNCPriceLookup *);
void (*all_sync) (Backend *, AccountGroup *, GNCPriceDB *);
void (*sync) (Backend *, AccountGroup *);
void (*sync_all) (Backend *, GNCBook *book);
void (*sync_group) (Backend *, AccountGroup *);
void (*sync_price) (Backend *, GNCPriceDB *);
gboolean (*events_pending) (Backend *be);
gboolean (*process_events) (Backend *be);
GNCBackendError last_err;
};

View File

@@ -28,6 +28,7 @@ libgncmod_engine_la_SOURCES = \
gnc-event.c \
gnc-numeric.c \
gnc-pricedb.c \
gnc-session.c \
gncmod-engine.c \
guid.c \
kvp_frame.c \
@@ -59,6 +60,8 @@ gncinclude_HEADERS = \
gnc-event.h \
gnc-numeric.h \
gnc-pricedb.h \
gnc-session.h \
gnc-session-p.h \
guid.h \
kvp_frame.h \
messages.h

View File

@@ -21,19 +21,21 @@
;; Copyright 2000 Rob Browning <rlb@cs.utexas.edu>
(define (gnc:url->loaded-book url ignore-lock? create-if-needed?)
(define (gnc:url->loaded-session url ignore-lock? create-if-needed?)
;; Return a <gnc:Book*> representing the data stored at the given
;; url or #f on failure -- this should later be changed to returning
;; the symbol representing the book error... On success, the book
;; will already be loaded.
(let* ((book (gnc:book-new))
(result (and book
(gnc:book-begin book url ignore-lock? create-if-needed?)
(gnc:book-load book)
book)))
(let* ((session (gnc:session-new))
(result (and session
(gnc:session-begin session url
ignore-lock?
create-if-needed?)
(gnc:session-load session)
session)))
(or result
(begin (gnc:book-destroy book) #f))))
(begin (gnc:session-destroy session) #f))))
(define (gnc:transaction-map-splits thunk transaction)
(let ((retval '()))

View File

@@ -44,7 +44,7 @@
(export gnc:setup-default-namespaces)
(export gnc:load-iso-4217-currencies)
(export gnc:url->loaded-book)
(export gnc:url->loaded-session)
(export gnc:transaction-map-splits)
(export gnc:group-map-all-accounts)
(export gnc:group-map-accounts)

View File

@@ -29,20 +29,13 @@
#ifndef GNC_BOOK_P_H
#define GNC_BOOK_P_H
#include "Group.h"
#include "Backend.h"
#include "BackendP.h"
#include "gnc-pricedb.h"
#include "TransLog.h"
#include "gnc-engine-util.h"
#include "gnc-pricedb-p.h"
#include "DateUtils.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "TransLog.h"
#include "gnc-book.h"
#include "gnc-pricedb.h"
#include "Group.h"
#include "gnc-engine-util.h"
#include "gnc-engine.h"
#include "gnc-pricedb-p.h"
struct gnc_book_struct
{
@@ -51,34 +44,9 @@ struct gnc_book_struct
GList *sched_xactions;
AccountGroup *template_group;
/*
* should be set true if sched_xactions is changed
* before saving
*/
/* should be set true if sched_xactions is changed */
gboolean sx_notsaved;
/* the requested book id, in the form or a URI, such as
* file:/some/where, or sql:server.host.com:555
*/
char *book_id;
/* if any book subroutine failed, this records the failure reason
* (file not found, etc).
* This is a 'stack' that is one deep.
* FIXME: This is a hack. I'm trying to move us away from static
* global vars. This may be a temp fix if we decide to integrate
* FileIO errors into GNCBook errors.
*/
GNCBackendError last_err;
char *error_message;
char *fullpath;
char *logpath;
/* ---------------------------------------------------- */
/* This struct member applies for network, rpc and SQL i/o */
/* It is not currently used for file i/o, but it should be. */
Backend *backend;
};
@@ -86,17 +54,13 @@ struct gnc_book_struct
void gnc_book_set_group(GNCBook *book, AccountGroup *grp);
void gnc_book_set_pricedb(GNCBook *book, GNCPriceDB *db);
void gnc_book_set_backend (GNCBook *book, Backend *be);
/*
* used by backends to mark the notsaved as FALSE just after
* loading. Do not use otherwise!
*/
void gnc_book_mark_saved(GNCBook *book);
void gnc_book_push_error (GNCBook *book, GNCBackendError err,
const char *message);
Backend* gncBackendInit_file(const char *book_id, void *data);
#endif /* GNC_BOOK_P_H */

View File

@@ -24,8 +24,7 @@
* gnc-book.c
*
* FUNCTION:
* Encapsulate all the information about a gnucash dataset, including
* the methods used to read and write them to datastores.
* Encapsulate all the information about a gnucash dataset.
*
* HISTORY:
* Created by Linas Vepstas December 1998
@@ -62,58 +61,6 @@ static short module = MOD_IO;
/* ---------------------------------------------------------------------- */
static void
gnc_book_clear_error (GNCBook *book)
{
book->last_err = ERR_BACKEND_NO_ERR;
g_free(book->error_message);
book->error_message = NULL;
}
void
gnc_book_push_error (GNCBook *book, GNCBackendError err, const char *message)
{
if (!book) return;
g_free (book->error_message);
book->last_err = err;
book->error_message = g_strdup (message);
}
GNCBackendError
gnc_book_get_error (GNCBook * book)
{
if (!book) return ERR_BACKEND_NO_BACKEND;
return book->last_err;
}
static const char *
get_default_error_message(GNCBackendError err)
{
return "";
}
const char *
gnc_book_get_error_message(GNCBook *book)
{
if(!book) return "";
if(!book->error_message) return get_default_error_message(book->last_err);
return book->error_message;
}
GNCBackendError
gnc_book_pop_error (GNCBook * book)
{
GNCBackendError err;
if (!book) return ERR_BACKEND_NO_BACKEND;
err = book->last_err;
gnc_book_clear_error(book);
return err;
}
/* ---------------------------------------------------------------------- */
const char *TEMPLATE_ACCOUNT_NAME = "__account for template transactions__";
static void
@@ -121,7 +68,7 @@ gnc_book_init (GNCBook *book)
{
Account *template_acct;
if(!book) return;
if (!book) return;
book->topgroup = xaccMallocAccountGroup();
book->pricedb = gnc_pricedb_create();
@@ -130,11 +77,8 @@ gnc_book_init (GNCBook *book)
book->sx_notsaved = FALSE;
book->template_group = xaccMallocAccountGroup();
book->book_id = NULL;
gnc_book_clear_error (book);
book->fullpath = NULL;
book->logpath = NULL;
book->backend = NULL;
xaccGroupSetBook (book->topgroup, book);
xaccGroupSetBook (book->template_group, book);
}
GNCBook *
@@ -177,6 +121,15 @@ gnc_book_set_group (GNCBook *book, AccountGroup *grp)
book->topgroup = grp;
}
void
gnc_book_set_backend (GNCBook *book, Backend *be)
{
if (!book) return;
xaccGroupSetBackend (book->topgroup, be);
xaccPriceDBSetBackend (book->pricedb, be);
}
/* ---------------------------------------------------------------------- */
static int
@@ -226,24 +179,28 @@ gnc_book_set_schedxactions( GNCBook *book, GList *newList )
if ( book == NULL ) return;
book->sched_xactions = newList;
book->sx_notsaved = TRUE;
return;
}
AccountGroup *
gnc_book_get_template_group( GNCBook *book )
{
if ( book == NULL ) return NULL;
return book->template_group;
if (!book) return NULL;
return book->template_group;
}
void
gnc_book_set_template_group( GNCBook *book, AccountGroup *templateGroup )
gnc_book_set_template_group (GNCBook *book, AccountGroup *templateGroup)
{
if ( book == NULL ) return;
book->template_group = templateGroup;
}
if (!book) return;
/* ---------------------------------------------------------------------- */
if (book->template_group == templateGroup)
return;
xaccGroupSetBook (book->template_group, NULL);
xaccGroupSetBook (templateGroup, book);
book->template_group = templateGroup;
}
Backend *
xaccGNCBookGetBackend (GNCBook *book)
@@ -252,26 +209,6 @@ xaccGNCBookGetBackend (GNCBook *book)
return book->backend;
}
/* ---------------------------------------------------------------------- */
const char *
gnc_book_get_file_path (GNCBook *book)
{
if (!book) return NULL;
return book->fullpath;
}
/* ---------------------------------------------------------------------- */
const char *
gnc_book_get_url (GNCBook *book)
{
if (!book) return NULL;
return book->book_id;
}
/* ---------------------------------------------------------------------- */
static void
mark_sx_clean(gpointer data, gpointer user_data)
{
@@ -293,8 +230,8 @@ book_sxns_mark_saved(GNCBook *book)
void
gnc_book_mark_saved(GNCBook *book)
{
/* FIXME: is this the right behaviour if book == NULL? */
g_return_if_fail(book);
if (!book) return;
xaccGroupMarkSaved(gnc_book_get_group(book));
gnc_pricedb_mark_clean(gnc_book_get_pricedb(book));
@@ -302,239 +239,6 @@ gnc_book_mark_saved(GNCBook *book)
book_sxns_mark_saved(book);
}
/* ---------------------------------------------------------------------- */
static void
gnc_book_int_backend_load_error(GNCBook *book, char *message, char *dll_err)
{
PWARN (message, dll_err ? dll_err : "");
g_free(book->fullpath);
book->fullpath = NULL;
g_free(book->logpath);
book->logpath = NULL;
g_free(book->book_id);
book->book_id = NULL;
gnc_book_push_error (book, ERR_BACKEND_NO_BACKEND, NULL);
}
/* FIXME : reinstate better error messages with gnc_module errors */
static void
gnc_book_load_backend(GNCBook * book, char * backend_name)
{
GNCModule mod;
Backend *(* be_new_func)(void);
char * mod_name = g_strdup_printf("gnucash/backend/%s", backend_name);
/* FIXME: this needs to be smarter with version numbers. */
mod = gnc_module_load(mod_name, 0);
if(mod)
{
be_new_func = gnc_module_lookup(mod, "gnc_backend_new");
if(be_new_func)
{
book->backend = be_new_func();
}
else
{
gnc_book_int_backend_load_error(book, " can't find backend_new ",
"");
}
}
else
{
gnc_book_int_backend_load_error(book, " failed to load '%s' backend",
backend_name);
}
g_free(mod_name);
}
gboolean
gnc_book_begin (GNCBook *book, const char * book_id,
gboolean ignore_lock, gboolean create_if_nonexistent)
{
int rc;
if (!book) return FALSE;
ENTER (" ignore_lock=%d, book-id=%s", ignore_lock,
book_id ? book_id : "(null)");
/* clear the error condition of previous errors */
gnc_book_clear_error (book);
/* check to see if this session is already open */
if (gnc_book_get_url(book))
{
gnc_book_push_error (book, ERR_BACKEND_LOCKED, NULL);
LEAVE("bad book url");
return FALSE;
}
/* seriously invalid */
if (!book_id)
{
gnc_book_push_error (book, ERR_BACKEND_NO_BACKEND, NULL);
LEAVE("bad book_id");
return FALSE;
}
/* Store the sessionid URL */
book->book_id = g_strdup (book_id);
book->fullpath = xaccResolveURL(book_id);
if (!book->fullpath)
{
gnc_book_push_error (book, ERR_FILEIO_FILE_NOT_FOUND, NULL);
LEAVE("bad fullpath");
return FALSE; /* ouch */
}
PINFO ("filepath=%s", book->fullpath ? book->fullpath : "(null)");
book->logpath = xaccResolveFilePath(book->fullpath);
PINFO ("logpath=%s", book->logpath ? book->logpath : "(null)");
/* check to see if this is a type we know how to handle */
if (!g_strncasecmp(book_id, "file:", 5) ||
*book->fullpath == '/')
{
gnc_book_load_backend(book, "file" );
}
#if 0
/* load different backend based on URL. We should probably
* dynamically load these based on some config file ... */
else if ((!g_strncasecmp(book_id, "http://", 7)) ||
(!g_strncasecmp(book_id, "https://", 8)))
{
/* create the backend */
book->backend = xmlendNew();
}
#endif
else if (!g_strncasecmp(book_id, "postgres://", 11))
{
gnc_book_load_backend(book, "postgres");
}
else if (!g_strncasecmp(book_id, "rpc://", 6))
{
gnc_book_load_backend(book, "rpc");
}
/* if there's a begin method, call that. */
if (book->backend && book->backend->book_begin)
{
int err;
(book->backend->book_begin)(book->backend, book,
gnc_book_get_url(book), ignore_lock,
create_if_nonexistent);
PINFO("Run book_begin on backend");
err = xaccBackendGetError(book->backend);
if (err != ERR_BACKEND_NO_ERR)
{
g_free(book->fullpath);
book->fullpath = NULL;
g_free(book->logpath);
book->logpath = NULL;
g_free(book->book_id);
book->book_id = NULL;
gnc_book_push_error (book, err, NULL);
LEAVE("backend error");
return FALSE;
}
}
LEAVE(" ");
return TRUE;
}
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_load (GNCBook *book)
{
GNCBackendError backend_err;
Backend *be;
if (!book) return FALSE;
if (!gnc_book_get_url(book)) return FALSE;
ENTER ("book_id=%s", gnc_book_get_url(book)
? gnc_book_get_url(book) : "(null)");
/* At this point, we should are supposed to have a valid book
* id and a lock on the file. */
xaccLogDisable();
xaccGroupMarkDoFree (book->topgroup);
xaccFreeAccountGroup (book->topgroup);
book->topgroup = NULL;
gnc_pricedb_destroy(book->pricedb);
book->pricedb = NULL;
xaccLogSetBaseName(book->logpath);
xaccLogEnable();
gnc_book_clear_error (book);
/* This code should be sufficient to initialize *any* backend,
* whether http, postgres, or anything else that might come along.
* Basically, the idea is that by now, a backend has already been
* created & set up. At this point, we only need to get the
* top-level account group out of the backend, and that is a
* generic, backend-independent operation.
*/
be = book->backend;
/* Starting the session should result in a bunch of accounts
* and currencies being downloaded, but probably no transactions;
* The GUI will need to do a query for that.
*/
if (be)
{
xaccLogDisable();
if(be->book_load)
{
xaccLogSetBaseName(book->logpath);
book->topgroup = (be->book_load) (be);
xaccGroupSetBackend (book->topgroup, be);
gnc_book_push_error(book, xaccBackendGetError(be), NULL);
}
if (be->price_load)
{
book->pricedb = (be->price_load) (be);
/* we just got done loading, it can't possibly be dirty !! */
gnc_book_mark_saved(book);
xaccPriceDBSetBackend (book->pricedb, be);
gnc_book_push_error(book, xaccBackendGetError(be), NULL);
}
xaccLogEnable();
}
if (!book->topgroup)
{
LEAVE("topgroup NULL");
return FALSE;
}
if (!book->pricedb)
{
LEAVE("pricedb NULL");
return FALSE;
}
if (gnc_book_get_error(book) != ERR_BACKEND_NO_ERR)
{
LEAVE("error from backend %d", gnc_book_get_error(book));
return FALSE;
}
LEAVE("book_id=%s", gnc_book_get_url(book)
? gnc_book_get_url(book) : "(null)");
return TRUE;
}
static gboolean
book_sxlist_notsaved(GNCBook *book)
{
@@ -556,12 +260,10 @@ book_sxlist_notsaved(GNCBook *book)
return FALSE;
}
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_not_saved(GNCBook *book)
{
if(!book) return FALSE;
if (!book) return FALSE;
return(xaccGroupNotSaved(book->topgroup)
||
@@ -570,158 +272,11 @@ gnc_book_not_saved(GNCBook *book)
book_sxlist_notsaved(book));
}
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_save_may_clobber_data (GNCBook *book)
{
/* FIXME: Make sure this doesn't need more sophisticated semantics
* in the face of special file, devices, pipes, symlinks, etc. */
struct stat statbuf;
if (!book) return FALSE;
if (!book->fullpath) return FALSE;
if (stat(book->fullpath, &statbuf) == 0) return TRUE;
return FALSE;
}
/* ---------------------------------------------------------------------- */
static gboolean
save_error_handler(Backend *be, GNCBook *book)
{
int err;
err = xaccBackendGetError(be);
if (ERR_BACKEND_NO_ERR != err)
{
gnc_book_push_error (book, err, NULL);
/* we close the backend here ... isn't this a bit harsh ??? */
if (be->book_end)
{
(be->book_end)(be);
}
return TRUE;
}
return FALSE;
}
void
gnc_book_save (GNCBook *book)
{
Backend *be;
if (!book) return;
ENTER ("book_id=%s", gnc_book_get_url(book)
? gnc_book_get_url(book) : "(null)");
/* if there is a backend, and the backend is reachablele
* (i.e. we can communicate with it), then synchronize with
* the backend. If we cannot contact the backend (e.g.
* because we've gone offline, the network has crashed, etc.)
* then give the user the option to save to disk.
*/
be = book->backend;
if (be) {
/* if invoked as SaveAs(), then backend not yet set */
xaccGroupSetBackend (book->topgroup, be);
xaccPriceDBSetBackend (book->pricedb, be);
if(be->all_sync)
{
(be->all_sync)(be, book->topgroup, book->pricedb);
if(save_error_handler(be, book))
return;
}
else
{
if (be->sync && book->topgroup) {
(be->sync)(be, book->topgroup);
if(save_error_handler(be, book))
return;
}
if (be->sync_price && book->pricedb) {
(be->sync_price)(be, book->pricedb);
if(save_error_handler(be, book))
return;
}
}
return;
}
/* if the fullpath doesn't exist, either the user failed to initialize,
* or the lockfile was never obtained. Either way, we can't write. */
gnc_book_clear_error (book);
if (!book->fullpath)
{
gnc_book_push_error (book, ERR_BACKEND_MISC, NULL);
return;
}
LEAVE(" ");
}
/* ---------------------------------------------------------------------- */
void
gnc_book_end (GNCBook *book)
{
if (!book) return;
ENTER ("book_id=%s", gnc_book_get_url(book)
? gnc_book_get_url(book) : "(null)");
/* close down the backend first */
if (book->backend && book->backend->book_end)
{
(book->backend->book_end)(book->backend);
}
gnc_book_clear_error (book);
g_free (book->fullpath);
book->fullpath = NULL;
g_free (book->logpath);
book->logpath = NULL;
g_free (book->book_id);
book->book_id = NULL;
LEAVE(" ");
}
void
gnc_book_destroy (GNCBook *book)
{
if (!book) return;
ENTER ("book_id=%s", gnc_book_get_url(book)
? gnc_book_get_url(book) : "(null)");
xaccLogDisable();
gnc_book_end (book);
/* destroy the backend */
if (book->backend && book->backend->destroy_backend)
{
book->backend->destroy_backend(book->backend);
}
else
{
g_free(book->backend);
}
xaccGroupSetBackend (book->topgroup, NULL);
xaccPriceDBSetBackend (book->pricedb, NULL);
/* mark the accounts as being freed
* to avoid tons of balance recomputations. */
xaccGroupMarkDoFree (book->topgroup);
@@ -732,6 +287,8 @@ gnc_book_destroy (GNCBook *book)
gnc_pricedb_destroy (book->pricedb);
book->pricedb = NULL;
/* FIXME: destroy SX data members here, too */
xaccLogEnable();
g_free (book);
@@ -757,316 +314,3 @@ gnc_book_equal (GNCBook *book_1, GNCBook *book_2)
return TRUE;
}
gboolean
gnc_book_events_pending (GNCBook *book)
{
if (!book) return FALSE;
if (!book->backend) return FALSE;
if (!book->backend->events_pending) return FALSE;
return book->backend->events_pending (book->backend);
}
gboolean
gnc_book_process_events (GNCBook *book)
{
if (!book) return FALSE;
if (!book->backend) return FALSE;
if (!book->backend->process_events) return FALSE;
return book->backend->process_events (book->backend);
}
/* ---------------------------------------------------------------------- */
/*
* If $HOME/.gnucash/data directory doesn't exist, then create it.
*/
static void
MakeHomeDir (void)
{
int rc;
struct stat statbuf;
char *home;
char *path;
char *data;
/* Punt. Can't figure out where home is. */
home = getenv ("HOME");
if (!home) return;
path = g_strconcat(home, "/.gnucash", NULL);
rc = stat (path, &statbuf);
if (rc)
{
/* assume that the stat failed only because the dir is absent,
* and not because its read-protected or other error.
* Go ahead and make it. Don't bother much with checking mkdir
* for errors; seems pointless. */
mkdir (path, S_IRWXU); /* perms = S_IRWXU = 0700 */
}
data = g_strconcat (path, "/data", NULL);
rc = stat (data, &statbuf);
if (rc)
mkdir (data, S_IRWXU);
g_free (path);
g_free (data);
}
/* ---------------------------------------------------------------------- */
/* XXX hack alert -- we should be yanking this out of some config file */
static char * searchpaths[] =
{
"/usr/share/gnucash/data/",
"/usr/local/share/gnucash/data/",
"/usr/share/gnucash/accounts/",
"/usr/local/share/gnucash/accounts/",
NULL,
};
typedef gboolean (*pathGenerator)(char *pathbuf, int which);
static gboolean
xaccAddEndPath(char *pathbuf, const char *ending, int len)
{
if(len + strlen(pathbuf) >= PATH_MAX)
return FALSE;
strcat (pathbuf, ending);
return TRUE;
}
static gboolean
xaccCwdPathGenerator(char *pathbuf, int which)
{
if(which != 0)
{
return FALSE;
}
else
{
/* try to find a file by this name in the cwd ... */
if (getcwd (pathbuf, PATH_MAX) == NULL)
return FALSE;
strcat (pathbuf, "/");
return TRUE;
}
}
static gboolean
xaccDataPathGenerator(char *pathbuf, int which)
{
char *path;
if(which != 0)
{
return FALSE;
}
else
{
path = getenv ("HOME");
if (!path)
return FALSE;
if (PATH_MAX <= (strlen (path) + 20))
return FALSE;
strcpy (pathbuf, path);
strcat (pathbuf, "/.gnucash/data/");
return TRUE;
}
}
static gboolean
xaccUserPathPathGenerator(char *pathbuf, int which)
{
char *path = NULL;
if(searchpaths[which] == NULL)
{
return FALSE;
}
else
{
path = searchpaths[which];
if (PATH_MAX <= strlen(path))
return FALSE;
strcpy (pathbuf, path);
return TRUE;
}
}
char *
xaccResolveFilePath (const char * filefrag)
{
struct stat statbuf;
char pathbuf[PATH_MAX];
pathGenerator gens[4];
char *filefrag_dup;
int namelen;
int i;
/* seriously invalid */
if (!filefrag)
{
PERR("filefrag is NULL");
return NULL;
}
ENTER ("filefrag=%s", filefrag);
/* ---------------------------------------------------- */
/* OK, now we try to find or build an absolute file path */
/* check for an absolute file path */
if (*filefrag == '/')
return g_strdup (filefrag);
if (!g_strncasecmp(filefrag, "file:", 5))
{
char *ret = g_new(char, strlen(filefrag) - 5 + 1);
strcpy(ret, filefrag + 5);
return ret;
}
/* get conservative on the length so that sprintf(getpid()) works ... */
/* strlen ("/.LCK") + sprintf (%x%d) */
namelen = strlen (filefrag) + 25;
gens[0] = xaccCwdPathGenerator;
gens[1] = xaccDataPathGenerator;
gens[2] = xaccUserPathPathGenerator;
gens[3] = NULL;
for (i = 0; gens[i] != NULL; i++)
{
int j;
for(j = 0; gens[i](pathbuf, j) ; j++)
{
if(xaccAddEndPath(pathbuf, filefrag, namelen))
{
int rc = stat (pathbuf, &statbuf);
if ((!rc) && (S_ISREG(statbuf.st_mode)))
{
return (g_strdup (pathbuf));
}
}
}
}
/* OK, we didn't find the file. */
/* make sure that the gnucash home dir exists. */
MakeHomeDir();
filefrag_dup = g_strdup (filefrag);
/* Replace '/' with ',' for non file backends */
if (strstr (filefrag, "://"))
{
char *p;
p = strchr (filefrag_dup, '/');
while (p) {
*p = ',';
p = strchr (filefrag_dup, '/');
}
}
/* Lets try creating a new file in $HOME/.gnucash/data */
if (xaccDataPathGenerator(pathbuf, 0))
{
if(xaccAddEndPath(pathbuf, filefrag_dup, namelen))
{
g_free (filefrag_dup);
return (g_strdup (pathbuf));
}
}
/* OK, we still didn't find the file */
/* Lets try creating a new file in the cwd */
if (xaccCwdPathGenerator(pathbuf, 0))
{
if(xaccAddEndPath(pathbuf, filefrag_dup, namelen))
{
g_free (filefrag_dup);
return (g_strdup (pathbuf));
}
}
g_free (filefrag_dup);
return NULL;
}
/* ---------------------------------------------------------------------- */
char *
xaccResolveURL (const char * pathfrag)
{
/* seriously invalid */
if (!pathfrag) return NULL;
/* At this stage of checking, URL's are always, by definition,
* resolved. If there's an error connecting, we'll find out later.
*
* FIXME -- we should probably use ghttp_uri_validate
* to make sure hte uri is in good form...
*/
if (!g_strncasecmp (pathfrag, "http://", 7) ||
!g_strncasecmp (pathfrag, "https://", 8) ||
!g_strncasecmp (pathfrag, "postgres://", 11) ||
!g_strncasecmp (pathfrag, "rpc://", 6))
{
return g_strdup(pathfrag);
}
if (!g_strncasecmp (pathfrag, "file:", 5)) {
return (xaccResolveFilePath (pathfrag+5));
}
return (xaccResolveFilePath (pathfrag));
}
/* ---------------------------------------------------------------------- */
/* this should go in a separate binary to create a rpc server */
void
gnc_run_rpc_server (void)
{
char * dll_err;
void * dll_handle;
int (*rpc_run)(short);
int ret;
/* open and resolve all symbols now (we don't want mystery
* failure later) */
dll_handle = dlopen ("libgnc_rpc.so", RTLD_NOW);
if (! dll_handle)
{
dll_err = dlerror();
PWARN (" can't load library: %s\n", dll_err ? dll_err : "");
return;
}
rpc_run = dlsym (dll_handle, "rpc_server_run");
dll_err = dlerror();
if (dll_err)
{
dll_err = dlerror();
PWARN (" can't find symbol: %s\n", dll_err ? dll_err : "");
return;
}
ret = (*rpc_run)(0);
/* XXX How do we force an exit? */
}

View File

@@ -25,24 +25,7 @@
* gnc-book.h
*
* FUNCTION:
* Encapsulate all the information about a gnucash dataset, including
* the methods used to read and write them to datastores.
*
* This class provides several important services:
*
* 1) Prevents multiple users from editing the same file at the same
* time, thus avoiding lost data due to race conditions. Thus
* an open session implies that the associated file is locked.
*
* 2) Provides a search path for the file to be edited. This should
* simplify install & maintenance problems for naive users who
* may not have a good grasp on what a file system is, or where
* they want to keep their data files.
*
* The current implementations assumes the use of files and file
* locks; however, the API was designed to be general enough to
* allow the use of generic URL's, and/or implementation on top
* of SQL or other database/persistant object technology.
* Encapsulate all the information about a gnucash dataset.
*
* HISTORY:
* Created by Linas Vepstas December 1998
@@ -68,59 +51,6 @@ typedef struct gnc_book_struct GNCBook;
GNCBook * gnc_book_new (void);
void gnc_book_destroy (GNCBook *book);
/* The gnc_book_begin () method begins a new book. It takes as an argument
* the book id. The book id must be a string in the form of a URI/URL.
* In the current implementation, the following URL's are supported
* -- File URI of the form
* "file:/home/somewhere/somedir/file.xac"
* The path part must be a valid path. The file-part must be
* a valid old-style-xacc or new-style-gnucash-format file. Paths
* may be relative or absolute. If the path is relative; that is,
* if the argument is "file:somefile.xac" then a sequence of
* search paths are checked for a file of this name.
*
* -- Postgres URI of the form
* "postgres://hostname.com/dbname"
* See the sql subdirectory for more info.
*
* The 'ignore_lock' argument, if set to TRUE, will cause this routine
* to ignore any file locks that it finds. If set to FALSE, then
* file locks will be tested and obeyed.
*
* If the file exists, can be opened and read, and a lock can be obtained
* then a lock will be obtained and the function returns TRUE.
*
* If the file/database doesn't exist, and the create_if_nonexistent
* flag is set to TRUE, then the database is created.
*
* Otherwise the function returns FALSE.
*/
gboolean gnc_book_begin (GNCBook *book, const char * book_id,
gboolean ignore_lock, gboolean create_if_nonexistent);
/* The gnc_book_load() method loads the data associated with the book.
* The function returns TRUE on success.
*/
gboolean gnc_book_load (GNCBook *book);
/* The gnc_book_get_error() routine can be used to obtain the reason
* for any failure. Calling this routine returns the current error.
*
* The gnc_book_pop_error() routine can be used to obtain the reason
* for any failure. Calling this routine resets the error value.
*
* This routine allows an implementation of multiple error values,
* e.g. in a stack, where this routine pops the top value. The current
* implementation has a stack that is one-deep.
*
* See Backend.h for a listing of returned errors.
*/
GNCBackendError gnc_book_get_error (GNCBook *book);
const char * gnc_book_get_error_message(GNCBook *book);
GNCBackendError gnc_book_pop_error (GNCBook *book);
AccountGroup *gnc_book_get_group (GNCBook *book);
void gnc_book_set_group(GNCBook *book, AccountGroup *group);
GNCPriceDB *gnc_book_get_pricedb (GNCBook *book);
@@ -145,72 +75,14 @@ void gnc_book_set_schedxactions( GNCBook *book, GList *newList );
AccountGroup *gnc_book_get_template_group( GNCBook *book );
void gnc_book_set_template_group( GNCBook *book, AccountGroup *templateGroup );
/* The gnc_book_get_file_path() routine returns the fully-qualified file
* path for the book. That is, if a relative or partial filename
* was for the book, then it had to have been fully resolved to
* open the book. This routine returns the result of this resolution.
* The path is always guarenteed to reside in the local file system,
* even if the book itself was opened as a URL. (currently, the
* filepath is derived from the url by substituting commas for
* slashes).
*
* The gnc_book_get_url() routine returns the url that was opened.
* URL's for local files take the form of
* file:/some/where/some/file.gml
*/
const char * gnc_book_get_file_path (GNCBook *book);
const char * gnc_book_get_url (GNCBook *book);
/*
* The gnc_book_not_saved() subroutine will return TRUE
* if any data in the book hasn't been saved to long-term storage.
*/
gboolean gnc_book_not_saved(GNCBook *book);
/* FIXME: This isn't as thorough as we might want it to be... */
gboolean gnc_book_save_may_clobber_data (GNCBook *book);
/* The gnc_book_save() method will commit all changes that have been
* made to the book. In the current implementation, this is nothing
* more than a write to the file of the current AccountGroup of the
* book.
*
* The gnc_book_end() method will release the session lock. It will *not*
* save the account group to a file. Thus, this method acts as an "abort"
* or "rollback" primitive.
*/
void gnc_book_save (GNCBook *book);
void gnc_book_end (GNCBook *book);
/* The gnc_book_equal() method returns TRUE if the engine data
* in the two given books is equal. */
gboolean gnc_book_equal (GNCBook *book_1, GNCBook *book_2);
/* The gnc_book_events_pending() method will return TRUE if the backend
* has pending events which must be processed to bring the engine
* up to date with the backend.
*
* The gnc_book_process_events() method will process any events indicated
* by the gnc_book_events_pending() method. It returns TRUE if the
* engine was modified while engine events were suspended.
*/
gboolean gnc_book_events_pending (GNCBook *book);
gboolean gnc_book_process_events (GNCBook *book);
/* The xaccResolveFilePath() routine is a utility that will accept
* a fragmentary filename as input, and resolve it into a fully
* qualified path in the file system, i.e. a path that begins with
* a leading slash. First, the current working directory is
* searched for the file. Next, the directory $HOME/.gnucash/data,
* and finally, a list of other (configurable) paths. If the file
* is not found, then the path $HOME/.gnucash/data is used. If
* $HOME is not defined, then the current working directory is
* used.
*/
char * xaccResolveFilePath (const char * filefrag);
char * xaccResolveURL (const char * pathfrag);
/* Run the RPC Server */
void gnc_run_rpc_server (void);
#endif /* GNC_BOOK_H */

View File

@@ -0,0 +1,83 @@
/********************************************************************\
* gnc-session-p.h -- private functions for gnc sessions. *
* *
* 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 *
* *
\********************************************************************/
/*
* HISTORY:
* Copyright (c) 2001 Linux Developers Group
*/
#ifndef GNC_SESSION_P_H
#define GNC_SESSION_P_H
#include "BackendP.h"
#include "TransLog.h"
#include "gnc-book.h"
#include "gnc-engine-util.h"
#include "gnc-engine.h"
#include "gnc-session.h"
struct gnc_session_struct
{
GNCBook *book;
/* the requested book id, in the form or a URI, such as
* file:/some/where, or sql:server.host.com:555
*/
char *book_id;
/* if any book subroutine failed, this records the failure reason
* (file not found, etc).
* This is a 'stack' that is one deep.
* FIXME: This is a hack. I'm trying to move us away from static
* global vars. This may be a temp fix if we decide to integrate
* FileIO errors into GNCBook errors.
*/
GNCBackendError last_err;
char *error_message;
char *fullpath;
char *logpath;
/* ---------------------------------------------------- */
/* This struct member applies for network, rpc and SQL i/o */
/* It is not currently used for file i/o, but it should be. */
Backend *backend;
};
void gnc_session_set_book (GNCSession *session, GNCBook *book);
Backend * gnc_session_get_backend (GNCSession *session);
/*
* used by backends to mark the notsaved as FALSE just after
* loading. Do not use otherwise!
*/
void gnc_session_push_error (GNCSession *session, GNCBackendError err,
const char *message);
Backend* gncBackendInit_file(const char *book_id, void *data);
#endif

887
src/engine/gnc-session.c Normal file
View File

@@ -0,0 +1,887 @@
/********************************************************************\
* gnc-sesssion.c -- session access (connection to backend) *
* *
* 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 *
\********************************************************************/
/*
* FILE:
* gnc-session.c
*
* FUNCTION:
* Encapsulate a connection to a GnuCash backend.
*
* HISTORY:
* Created by Linas Vepstas December 1998
* Copyright (c) 1998-2001 Linas Vepstas
* Copyright (c) 2000 Dave Peticolas
*/
#include "config.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>
#include "BackendP.h"
#include "TransLog.h"
#include "gnc-engine-util.h"
#include "DateUtils.h"
#include "gnc-book-p.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "gnc-module.h"
#include "gnc-session-p.h"
static short module = MOD_IO;
/* ---------------------------------------------------------------------- */
static void
gnc_session_clear_error (GNCSession *session)
{
session->last_err = ERR_BACKEND_NO_ERR;
g_free(session->error_message);
session->error_message = NULL;
}
void
gnc_session_push_error (GNCSession *session, GNCBackendError err,
const char *message)
{
if (!session) return;
g_free (session->error_message);
session->last_err = err;
session->error_message = g_strdup (message);
}
GNCBackendError
gnc_session_get_error (GNCSession * session)
{
if (!session) return ERR_BACKEND_NO_BACKEND;
return session->last_err;
}
static const char *
get_default_error_message(GNCBackendError err)
{
return "";
}
const char *
gnc_session_get_error_message(GNCSession *session)
{
if(!session) return "";
if(!session->error_message)
return get_default_error_message(session->last_err);
return session->error_message;
}
GNCBackendError
gnc_session_pop_error (GNCSession * session)
{
GNCBackendError err;
if (!session) return ERR_BACKEND_NO_BACKEND;
err = session->last_err;
gnc_session_clear_error(session);
return err;
}
/* ---------------------------------------------------------------------- */
static void
gnc_session_init (GNCSession *session)
{
if (!session) return;
session->book = gnc_book_new ();
session->book_id = NULL;
session->fullpath = NULL;
session->logpath = NULL;
session->backend = NULL;
gnc_session_clear_error (session);
}
GNCSession *
gnc_session_new (void)
{
GNCSession *session = g_new0(GNCSession, 1);
gnc_session_init(session);
return session;
}
GNCBook *
gnc_session_get_book (GNCSession *session)
{
if (!session) return NULL;
return session->book;
}
void
gnc_session_set_book (GNCSession *session, GNCBook *book)
{
if (!session) return;
/* Do not free the old book here unless you also fix
* all the other uses of gnc_session_set_book! */
if (session->book == book)
return;
session->book = book;
}
Backend *
gnc_session_get_backend (GNCSession *session)
{
if (!session) return NULL;
return session->backend;
}
const char *
gnc_session_get_file_path (GNCSession *session)
{
if (!session) return NULL;
return session->fullpath;
}
const char *
gnc_session_get_url (GNCSession *session)
{
if (!session) return NULL;
return session->book_id;
}
static void
gnc_session_int_backend_load_error(GNCSession *session,
char *message, char *dll_err)
{
PWARN (message, dll_err ? dll_err : "");
g_free(session->fullpath);
session->fullpath = NULL;
g_free(session->logpath);
session->logpath = NULL;
g_free(session->book_id);
session->book_id = NULL;
gnc_session_push_error (session, ERR_BACKEND_NO_BACKEND, NULL);
}
/* FIXME : reinstate better error messages with gnc_module errors */
static void
gnc_session_load_backend(GNCSession * session, char * backend_name)
{
GNCModule mod;
Backend *(* be_new_func)(void);
char * mod_name = g_strdup_printf("gnucash/backend/%s", backend_name);
/* FIXME: this needs to be smarter with version numbers. */
mod = gnc_module_load(mod_name, 0);
if(mod)
{
be_new_func = gnc_module_lookup(mod, "gnc_backend_new");
if(be_new_func)
{
session->backend = be_new_func();
}
else
{
gnc_session_int_backend_load_error(session, " can't find backend_new ",
"");
}
}
else
{
gnc_session_int_backend_load_error(session, " failed to load '%s' backend",
backend_name);
}
g_free(mod_name);
}
gboolean
gnc_session_begin (GNCSession *session, const char * book_id,
gboolean ignore_lock, gboolean create_if_nonexistent)
{
int rc;
if (!session) return FALSE;
ENTER (" ignore_lock=%d, book-id=%s", ignore_lock,
book_id ? book_id : "(null)");
/* clear the error condition of previous errors */
gnc_session_clear_error (session);
/* check to see if this session is already open */
if (gnc_session_get_url(session))
{
gnc_session_push_error (session, ERR_BACKEND_LOCKED, NULL);
LEAVE("bad book url");
return FALSE;
}
/* seriously invalid */
if (!book_id)
{
gnc_session_push_error (session, ERR_BACKEND_NO_BACKEND, NULL);
LEAVE("bad book_id");
return FALSE;
}
/* Store the sessionid URL */
session->book_id = g_strdup (book_id);
session->fullpath = xaccResolveURL(book_id);
if (!session->fullpath)
{
gnc_session_push_error (session, ERR_FILEIO_FILE_NOT_FOUND, NULL);
LEAVE("bad fullpath");
return FALSE; /* ouch */
}
PINFO ("filepath=%s", session->fullpath ? session->fullpath : "(null)");
session->logpath = xaccResolveFilePath(session->fullpath);
PINFO ("logpath=%s", session->logpath ? session->logpath : "(null)");
/* check to see if this is a type we know how to handle */
if (!g_strncasecmp(book_id, "file:", 5) ||
*session->fullpath == '/')
{
gnc_session_load_backend(session, "file" );
}
#if 0
/* load different backend based on URL. We should probably
* dynamically load these based on some config file ... */
else if ((!g_strncasecmp(book_id, "http://", 7)) ||
(!g_strncasecmp(book_id, "https://", 8)))
{
/* create the backend */
session->backend = xmlendNew();
}
#endif
else if (!g_strncasecmp(book_id, "postgres://", 11))
{
gnc_session_load_backend(session, "postgres");
}
else if (!g_strncasecmp(book_id, "rpc://", 6))
{
gnc_session_load_backend(session, "rpc");
}
/* if there's a begin method, call that. */
if (session->backend && session->backend->session_begin)
{
int err;
(session->backend->session_begin)(session->backend, session,
gnc_session_get_url(session), ignore_lock,
create_if_nonexistent);
PINFO("Run session_begin on backend");
err = xaccBackendGetError(session->backend);
if (err != ERR_BACKEND_NO_ERR)
{
g_free(session->fullpath);
session->fullpath = NULL;
g_free(session->logpath);
session->logpath = NULL;
g_free(session->book_id);
session->book_id = NULL;
gnc_session_push_error (session, err, NULL);
LEAVE("backend error");
return FALSE;
}
}
LEAVE(" ");
return TRUE;
}
/* ---------------------------------------------------------------------- */
gboolean
gnc_session_load (GNCSession *session)
{
GNCBackendError backend_err;
Backend *be;
if (!session) return FALSE;
if (!gnc_session_get_url(session)) return FALSE;
ENTER ("book_id=%s", gnc_session_get_url(session)
? gnc_session_get_url(session) : "(null)");
/* At this point, we should are supposed to have a valid book
* id and a lock on the file. */
xaccLogDisable();
gnc_book_destroy (session->book);
session->book = gnc_book_new ();
xaccLogSetBaseName(session->logpath);
xaccLogEnable();
gnc_session_clear_error (session);
/* This code should be sufficient to initialize *any* backend,
* whether http, postgres, or anything else that might come along.
* Basically, the idea is that by now, a backend has already been
* created & set up. At this point, we only need to get the
* top-level account group out of the backend, and that is a
* generic, backend-independent operation.
*/
be = session->backend;
/* Starting the session should result in a bunch of accounts
* and currencies being downloaded, but probably no transactions;
* The GUI will need to do a query for that.
*/
if (be)
{
xaccLogDisable();
if (be->book_load)
{
xaccLogSetBaseName(session->logpath);
be->book_load (be);
gnc_session_push_error (session, xaccBackendGetError(be), NULL);
}
if (be->price_load)
{
be->price_load (be);
gnc_session_push_error(session, xaccBackendGetError(be), NULL);
}
gnc_book_set_backend (session->book, be);
/* we just got done loading, it can't possibly be dirty !! */
gnc_book_mark_saved (session->book);
xaccLogEnable();
}
if (!gnc_book_get_group (session->book))
{
LEAVE("topgroup NULL");
return FALSE;
}
if (!gnc_book_get_pricedb (session->book))
{
LEAVE("pricedb NULL");
return FALSE;
}
if (gnc_session_get_error(session) != ERR_BACKEND_NO_ERR)
{
LEAVE("error from backend %d", gnc_session_get_error(session));
return FALSE;
}
LEAVE ("book_id=%s", gnc_session_get_url(session)
? gnc_session_get_url(session) : "(null)");
return TRUE;
}
gboolean
gnc_session_save_may_clobber_data (GNCSession *session)
{
/* FIXME: Make sure this doesn't need more sophisticated semantics
* in the face of special file, devices, pipes, symlinks, etc. */
struct stat statbuf;
if (!session) return FALSE;
if (!session->fullpath) return FALSE;
if (stat(session->fullpath, &statbuf) == 0) return TRUE;
return FALSE;
}
static gboolean
save_error_handler(Backend *be, GNCSession *session)
{
int err;
err = xaccBackendGetError(be);
if (ERR_BACKEND_NO_ERR != err)
{
gnc_session_push_error (session, err, NULL);
/* we close the backend here ... isn't this a bit harsh ??? */
if (be->session_end)
{
(be->session_end)(be);
}
return TRUE;
}
return FALSE;
}
void
gnc_session_save (GNCSession *session)
{
Backend *be;
if (!session) return;
ENTER ("book_id=%s", gnc_session_get_url(session)
? gnc_session_get_url(session) : "(null)");
/* if there is a backend, and the backend is reachablele
* (i.e. we can communicate with it), then synchronize with
* the backend. If we cannot contact the backend (e.g.
* because we've gone offline, the network has crashed, etc.)
* then give the user the option to save to disk.
*/
be = session->backend;
if (be)
{
/* if invoked as SaveAs(), then backend not yet set */
gnc_book_set_backend (session->book, be);
if (be->sync_all)
{
(be->sync_all)(be, session->book);
if (save_error_handler(be, session))
return;
}
else
{
if (be->sync_group && gnc_book_get_group (session->book))
{
(be->sync_group)(be, gnc_book_get_group (session->book));
if (save_error_handler(be, session))
return;
}
if (be->sync_price && gnc_book_get_pricedb (session->book))
{
(be->sync_price)(be, gnc_book_get_pricedb (session->book));
if(save_error_handler(be, session))
return;
}
}
return;
}
/* if the fullpath doesn't exist, either the user failed to initialize,
* or the lockfile was never obtained. Either way, we can't write. */
gnc_session_clear_error (session);
if (!session->fullpath)
{
gnc_session_push_error (session, ERR_BACKEND_MISC, NULL);
return;
}
LEAVE(" ");
}
/* ---------------------------------------------------------------------- */
void
gnc_session_end (GNCSession *session)
{
if (!session) return;
ENTER ("book_id=%s", gnc_session_get_url(session)
? gnc_session_get_url(session) : "(null)");
/* close down the backend first */
if (session->backend && session->backend->session_end)
{
(session->backend->session_end)(session->backend);
}
gnc_session_clear_error (session);
g_free (session->fullpath);
session->fullpath = NULL;
g_free (session->logpath);
session->logpath = NULL;
g_free (session->book_id);
session->book_id = NULL;
LEAVE(" ");
}
void
gnc_session_destroy (GNCSession *session)
{
if (!session) return;
ENTER ("book_id=%s", gnc_session_get_url(session)
? gnc_session_get_url(session) : "(null)");
xaccLogDisable();
gnc_session_end (session);
/* destroy the backend */
if (session->backend && session->backend->destroy_backend)
{
session->backend->destroy_backend(session->backend);
}
else
{
g_free(session->backend);
}
gnc_book_set_backend (session->book, NULL);
gnc_book_destroy (session->book);
session->book = NULL;
xaccLogEnable();
g_free (session);
LEAVE(" ");
}
gboolean
gnc_session_events_pending (GNCSession *session)
{
if (!session) return FALSE;
if (!session->backend) return FALSE;
if (!session->backend->events_pending) return FALSE;
return session->backend->events_pending (session->backend);
}
gboolean
gnc_session_process_events (GNCSession *session)
{
if (!session) return FALSE;
if (!session->backend) return FALSE;
if (!session->backend->process_events) return FALSE;
return session->backend->process_events (session->backend);
}
/* ---------------------------------------------------------------------- */
/*
* If $HOME/.gnucash/data directory doesn't exist, then create it.
*/
static void
MakeHomeDir (void)
{
int rc;
struct stat statbuf;
char *home;
char *path;
char *data;
/* Punt. Can't figure out where home is. */
home = getenv ("HOME");
if (!home) return;
path = g_strconcat(home, "/.gnucash", NULL);
rc = stat (path, &statbuf);
if (rc)
{
/* assume that the stat failed only because the dir is absent,
* and not because its read-protected or other error.
* Go ahead and make it. Don't bother much with checking mkdir
* for errors; seems pointless. */
mkdir (path, S_IRWXU); /* perms = S_IRWXU = 0700 */
}
data = g_strconcat (path, "/data", NULL);
rc = stat (data, &statbuf);
if (rc)
mkdir (data, S_IRWXU);
g_free (path);
g_free (data);
}
/* ---------------------------------------------------------------------- */
/* XXX hack alert -- we should be yanking this out of some config file */
static char * searchpaths[] =
{
"/usr/share/gnucash/data/",
"/usr/local/share/gnucash/data/",
"/usr/share/gnucash/accounts/",
"/usr/local/share/gnucash/accounts/",
NULL,
};
typedef gboolean (*pathGenerator)(char *pathbuf, int which);
static gboolean
xaccAddEndPath(char *pathbuf, const char *ending, int len)
{
if(len + strlen(pathbuf) >= PATH_MAX)
return FALSE;
strcat (pathbuf, ending);
return TRUE;
}
static gboolean
xaccCwdPathGenerator(char *pathbuf, int which)
{
if(which != 0)
{
return FALSE;
}
else
{
/* try to find a file by this name in the cwd ... */
if (getcwd (pathbuf, PATH_MAX) == NULL)
return FALSE;
strcat (pathbuf, "/");
return TRUE;
}
}
static gboolean
xaccDataPathGenerator(char *pathbuf, int which)
{
char *path;
if(which != 0)
{
return FALSE;
}
else
{
path = getenv ("HOME");
if (!path)
return FALSE;
if (PATH_MAX <= (strlen (path) + 20))
return FALSE;
strcpy (pathbuf, path);
strcat (pathbuf, "/.gnucash/data/");
return TRUE;
}
}
static gboolean
xaccUserPathPathGenerator(char *pathbuf, int which)
{
char *path = NULL;
if(searchpaths[which] == NULL)
{
return FALSE;
}
else
{
path = searchpaths[which];
if (PATH_MAX <= strlen(path))
return FALSE;
strcpy (pathbuf, path);
return TRUE;
}
}
char *
xaccResolveFilePath (const char * filefrag)
{
struct stat statbuf;
char pathbuf[PATH_MAX];
pathGenerator gens[4];
char *filefrag_dup;
int namelen;
int i;
/* seriously invalid */
if (!filefrag)
{
PERR("filefrag is NULL");
return NULL;
}
ENTER ("filefrag=%s", filefrag);
/* ---------------------------------------------------- */
/* OK, now we try to find or build an absolute file path */
/* check for an absolute file path */
if (*filefrag == '/')
return g_strdup (filefrag);
if (!g_strncasecmp(filefrag, "file:", 5))
{
char *ret = g_new(char, strlen(filefrag) - 5 + 1);
strcpy(ret, filefrag + 5);
return ret;
}
/* get conservative on the length so that sprintf(getpid()) works ... */
/* strlen ("/.LCK") + sprintf (%x%d) */
namelen = strlen (filefrag) + 25;
gens[0] = xaccCwdPathGenerator;
gens[1] = xaccDataPathGenerator;
gens[2] = xaccUserPathPathGenerator;
gens[3] = NULL;
for (i = 0; gens[i] != NULL; i++)
{
int j;
for(j = 0; gens[i](pathbuf, j) ; j++)
{
if(xaccAddEndPath(pathbuf, filefrag, namelen))
{
int rc = stat (pathbuf, &statbuf);
if ((!rc) && (S_ISREG(statbuf.st_mode)))
{
return (g_strdup (pathbuf));
}
}
}
}
/* OK, we didn't find the file. */
/* make sure that the gnucash home dir exists. */
MakeHomeDir();
filefrag_dup = g_strdup (filefrag);
/* Replace '/' with ',' for non file backends */
if (strstr (filefrag, "://"))
{
char *p;
p = strchr (filefrag_dup, '/');
while (p) {
*p = ',';
p = strchr (filefrag_dup, '/');
}
}
/* Lets try creating a new file in $HOME/.gnucash/data */
if (xaccDataPathGenerator(pathbuf, 0))
{
if(xaccAddEndPath(pathbuf, filefrag_dup, namelen))
{
g_free (filefrag_dup);
return (g_strdup (pathbuf));
}
}
/* OK, we still didn't find the file */
/* Lets try creating a new file in the cwd */
if (xaccCwdPathGenerator(pathbuf, 0))
{
if(xaccAddEndPath(pathbuf, filefrag_dup, namelen))
{
g_free (filefrag_dup);
return (g_strdup (pathbuf));
}
}
g_free (filefrag_dup);
return NULL;
}
/* ---------------------------------------------------------------------- */
char *
xaccResolveURL (const char * pathfrag)
{
/* seriously invalid */
if (!pathfrag) return NULL;
/* At this stage of checking, URL's are always, by definition,
* resolved. If there's an error connecting, we'll find out later.
*
* FIXME -- we should probably use ghttp_uri_validate
* to make sure hte uri is in good form...
*/
if (!g_strncasecmp (pathfrag, "http://", 7) ||
!g_strncasecmp (pathfrag, "https://", 8) ||
!g_strncasecmp (pathfrag, "postgres://", 11) ||
!g_strncasecmp (pathfrag, "rpc://", 6))
{
return g_strdup(pathfrag);
}
if (!g_strncasecmp (pathfrag, "file:", 5)) {
return (xaccResolveFilePath (pathfrag+5));
}
return (xaccResolveFilePath (pathfrag));
}
/* ---------------------------------------------------------------------- */
/* this should go in a separate binary to create a rpc server */
void
gnc_run_rpc_server (void)
{
char * dll_err;
void * dll_handle;
int (*rpc_run)(short);
int ret;
/* open and resolve all symbols now (we don't want mystery
* failure later) */
dll_handle = dlopen ("libgnc_rpc.so", RTLD_NOW);
if (! dll_handle)
{
dll_err = dlerror();
PWARN (" can't load library: %s\n", dll_err ? dll_err : "");
return;
}
rpc_run = dlsym (dll_handle, "rpc_server_run");
dll_err = dlerror();
if (dll_err)
{
dll_err = dlerror();
PWARN (" can't find symbol: %s\n", dll_err ? dll_err : "");
return;
}
ret = (*rpc_run)(0);
/* XXX How do we force an exit? */
}

188
src/engine/gnc-session.h Normal file
View File

@@ -0,0 +1,188 @@
/********************************************************************\
* gnc-session.h -- session access (connection to backend) *
* *
* 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 *
* *
\********************************************************************/
/*
* FILE:
* gnc-session.h
*
* FUNCTION:
* Encapsulate a connection to a GnuCash backend.
*
* This class provides several important services:
*
* 1) Prevents multiple users from editing the same file at the same
* time, thus avoiding lost data due to race conditions. Thus
* an open session implies that the associated file is locked.
*
* 2) Provides a search path for the file to be edited. This should
* simplify install & maintenance problems for naive users who
* may not have a good grasp on what a file system is, or where
* they want to keep their data files.
*
* The current implementations assumes the use of files and file
* locks; however, the API was designed to be general enough to
* allow the use of generic URL's, and/or implementation on top
* of SQL or other database/persistant object technology.
*
* HISTORY:
* Created by Linas Vepstas December 1998
* Copyright (c) 1998, 1999 Linas Vepstas
* Copyright (c) 2000 Dave Peticolas
*/
#ifndef GNC_SESSION_H
#define GNC_SESSION_H
#include "gnc-book.h"
/** TYPES **********************************************************/
typedef struct gnc_session_struct GNCSession;
/** PROTOTYPES ******************************************************/
GNCSession * gnc_session_new (void);
void gnc_session_destroy (GNCSession *session);
/* The gnc_session_begin () method begins a new session. It takes as an argument
* the book id. The book id must be a string in the form of a URI/URL.
* In the current implementation, the following URL's are supported
* -- File URI of the form
* "file:/home/somewhere/somedir/file.xac"
* The path part must be a valid path. The file-part must be
* a valid old-style-xacc or new-style-gnucash-format file. Paths
* may be relative or absolute. If the path is relative; that is,
* if the argument is "file:somefile.xac" then a sequence of
* search paths are checked for a file of this name.
*
* -- Postgres URI of the form
* "postgres://hostname.com/dbname"
* See the sql subdirectory for more info.
*
* The 'ignore_lock' argument, if set to TRUE, will cause this routine
* to ignore any file locks that it finds. If set to FALSE, then
* file locks will be tested and obeyed.
*
* If the file exists, can be opened and read, and a lock can be obtained
* then a lock will be obtained and the function returns TRUE.
*
* If the file/database doesn't exist, and the create_if_nonexistent
* flag is set to TRUE, then the database is created.
*
* Otherwise the function returns FALSE.
*/
gboolean gnc_session_begin (GNCSession *session, const char * book_id,
gboolean ignore_lock, gboolean create_if_nonexistent);
/* The gnc_session_load() method loads the data associated with the session.
* The function returns TRUE on success.
*/
gboolean gnc_session_load (GNCSession *session);
/* The gnc_session_get_error() routine can be used to obtain the reason
* for any failure. Calling this routine returns the current error.
*
* The gnc_session_pop_error() routine can be used to obtain the reason
* for any failure. Calling this routine resets the error value.
*
* This routine allows an implementation of multiple error values,
* e.g. in a stack, where this routine pops the top value. The current
* implementation has a stack that is one-deep.
*
* See Backend.h for a listing of returned errors.
*/
GNCBackendError gnc_session_get_error (GNCSession *session);
const char * gnc_session_get_error_message(GNCSession *session);
GNCBackendError gnc_session_pop_error (GNCSession *session);
GNCBook * gnc_session_get_book (GNCSession *session);
void gnc_session_set_book (GNCSession *session, GNCBook *book);
/* The gnc_session_get_file_path() routine returns the fully-qualified file
* path for the session. That is, if a relative or partial filename
* was for the session, then it had to have been fully resolved to
* open the session. This routine returns the result of this resolution.
* The path is always guarenteed to reside in the local file system,
* even if the session itself was opened as a URL. (currently, the
* filepath is derived from the url by substituting commas for
* slashes).
*
* The gnc_session_get_url() routine returns the url that was opened.
* URL's for local files take the form of
* file:/some/where/some/file.gml
*/
const char * gnc_session_get_file_path (GNCSession *session);
const char * gnc_session_get_url (GNCSession *session);
/*
* The gnc_session_not_saved() subroutine will return TRUE
* if any data in the session hasn't been saved to long-term storage.
*/
gboolean gnc_session_not_saved(GNCSession *session);
/* FIXME: This isn't as thorough as we might want it to be... */
gboolean gnc_session_save_may_clobber_data (GNCSession *session);
/* The gnc_session_save() method will commit all changes that have been
* made to the session. In the current implementation, this is nothing
* more than a write to the file of the current AccountGroup of the
* session.
*
* The gnc_session_end() method will release the session lock. It will *not*
* save the account group to a file. Thus, this method acts as an "abort"
* or "rollback" primitive.
*/
void gnc_session_save (GNCSession *session);
void gnc_session_end (GNCSession *session);
/* The gnc_session_events_pending() method will return TRUE if the backend
* has pending events which must be processed to bring the engine
* up to date with the backend.
*
* The gnc_session_process_events() method will process any events indicated
* by the gnc_session_events_pending() method. It returns TRUE if the
* engine was modified while engine events were suspended.
*/
gboolean gnc_session_events_pending (GNCSession *session);
gboolean gnc_session_process_events (GNCSession *session);
/* The xaccResolveFilePath() routine is a utility that will accept
* a fragmentary filename as input, and resolve it into a fully
* qualified path in the file system, i.e. a path that begins with
* a leading slash. First, the current working directory is
* searched for the file. Next, the directory $HOME/.gnucash/data,
* and finally, a list of other (configurable) paths. If the file
* is not found, then the path $HOME/.gnucash/data is used. If
* $HOME is not defined, then the current working directory is
* used.
*/
char * xaccResolveFilePath (const char * filefrag);
char * xaccResolveURL (const char * pathfrag);
/* Run the RPC Server */
void gnc_run_rpc_server (void);
#endif /* GNC_SESSION_H */

View File

@@ -313,7 +313,7 @@
"#include <GNCIdP.h>\n"
"#include <Query.h>\n"
"#include <Backend.h>\n"
"#include <gnc-book.h>\n"
"#include <gnc-session.h>\n"
"#include <gnc-engine-util.h>\n"
"#include <date.h>\n"
"#include <engine-helpers.h>\n"
@@ -336,6 +336,8 @@
(gw:wrap-non-native-type mod '<gnc:AccountGroup*>
"AccountGroup*" "const AccountGroup*")
(gw:wrap-non-native-type mod '<gnc:Book*> "GNCBook*" "const GNCBook*")
(gw:wrap-non-native-type mod '<gnc:Session*>
"GNCSession*" "const GNCSession**")
(gw:wrap-non-native-type mod '<gnc:Split*> "Split*" "const Split*")
(gw:wrap-non-native-type mod '<gnc:Transaction*>
@@ -434,6 +436,7 @@
(gw:enum-add-value! wt "CLEARED_CLEARED" 'cleared-match-cleared)
(gw:enum-add-value! wt "CLEARED_RECONCILED" 'cleared-match-reconciled)
(gw:enum-add-value! wt "CLEARED_FROZEN" 'cleared-match-frozen)
(gw:enum-add-value! wt "CLEARED_VOIDED" 'cleared-match-voided)
#t)
(let ((wt (gw:wrap-enumeration mod '<gnc:balance-match-how>
@@ -1412,48 +1415,61 @@ when no longer needed.")
(gw:wrap-function
mod
'gnc:book-new '<gnc:Book*> "gnc_book_new" '()
"Create a new book.")
'gnc:session-new
'<gnc:Session*>
"gnc_session_new" '()
"Create a new session.")
(gw:wrap-function
mod
'gnc:book-destroy '<gw:void> "gnc_book_destroy" '((<gnc:Book*> book))
"Destroy the given book.")
'gnc:session-destroy
'<gw:void>
"gnc_session_destroy"
'((<gnc:Session*> session))
"Destroy the given session.")
(gw:wrap-function
mod
'gnc:book-begin
'gnc:session-get-book
'<gnc:Book*>
"gnc_session_get_book"
'((<gnc:Session*> session))
"Get the book of the given session.")
(gw:wrap-function
mod
'gnc:session-begin
'<gw:bool>
"gnc_book_begin"
'((<gnc:Book*> book)
"gnc_session_begin"
'((<gnc:Session*> session)
((<gw:m-chars-caller-owned> gw:const) id)
(<gw:bool> ignore-lock?)
(<gw:bool> create-if-nonexistent?))
"Setup the book for use.")
"Setup the session for use.")
(gw:wrap-function
mod
'gnc:book-load
'gnc:session-load
'<gw:bool>
"gnc_book_load"
'((<gnc:Book*> book))
"Load the data associated with the given book.")
"gnc_session_load"
'((<gnc:Session*> session))
"Load the data associated with the given session.")
(gw:wrap-function
mod
'gnc:book-save
'gnc:session-save
'<gw:void>
"gnc_book_save"
'((<gnc:Book*> book))
"Save the data in the book.")
"gnc_session_save"
'((<gnc:Session*> session))
"Save the data in the session.")
(gw:wrap-function
mod
'gnc:book-end
'gnc:session-end
'<gw:void>
"gnc_book_end"
'((<gnc:Book*> book))
"Indicate you're finished with the book.")
"gnc_session_end"
'((<gnc:Session*> session))
"Indicate you're finished with the session.")
(gw:wrap-function
mod
@@ -1489,18 +1505,18 @@ when no longer needed.")
(gw:wrap-function
mod
'gnc:book-get-error
'gnc:session-get-error
'<gnc:BackendError>
"gnc_book_get_error"
'((<gnc:Book*> book))
"gnc_session_get_error"
'((<gnc:Session*> session))
"Check for a pending error.")
(gw:wrap-function
mod
'gnc:book-pop-error
'gnc:session-pop-error
'<gnc:BackendError>
"gnc_book_pop_error"
'((<gnc:Book*> book))
"gnc_session_pop_error"
'((<gnc:Session*> session))
"Remove an error, if any, from the error stack.")
(gw:wrap-function

View File

@@ -732,7 +732,7 @@ get_random_query(void)
case PR_CLEARED:
xaccQueryAddClearedMatch
(q,
get_random_int_in_range (1, CLEARED_FROZEN),
get_random_int_in_range (1, CLEARED_VOIDED),
get_random_queryop ());
break;

View File

@@ -5,7 +5,7 @@
#include "test-stuff.h"
#include "gnc-engine-util.h"
#include "gnc-book.h"
#include "gnc-session.h"
struct test_strings_struct
{

View File

@@ -79,7 +79,7 @@ gnc_mdi_child_set_title (GNCMDIChildInfo *childwin)
if (!childwin || !childwin->app)
return;
filename = gnc_book_get_url (gnc_get_current_book ());
filename = gnc_session_get_url (gnc_get_current_session ());
if (!filename)
filename = _("<no file>");

View File

@@ -608,25 +608,25 @@ gnc_ui_destroy (void)
static gboolean
gnc_ui_check_events (gpointer not_used)
{
GNCBook *book;
GNCSession *session;
gboolean force;
if (gtk_main_level() != 1)
return TRUE;
book = gnc_get_current_book ();
if (!book)
session = gnc_get_current_session ();
if (!session)
return TRUE;
if (gnc_gui_refresh_suspended ())
return TRUE;
if (!gnc_book_events_pending (book))
if (!gnc_session_events_pending (session))
return TRUE;
gnc_suspend_gui_refresh ();
force = gnc_book_process_events (book);
force = gnc_session_process_events (session);
gnc_resume_gui_refresh ();

View File

@@ -55,13 +55,14 @@
(let ((group (qif-io:acct-table-make-gnc-group
acct-table qiffile commodity)))
;; write the file
(let ((book (gnc:book-new))
(name (simple-format #f "file:~A.gnc" filename)))
(let* ((session (gnc:session-new))
(book (gnc:session-get-book session))
(name (simple-format #f "file:~A.gnc" filename)))
(simple-format #t "using book name='~A'\n" name)
(gnc:book-set-group book group)
(gnc:book-begin book name #t #t)
(gnc:book-save book)
(gnc:book-end book)))))
(gnc:session-begin session name #t #t)
(gnc:session-save book)
(gnc:session-end book)))))
0)
(define (run-test)

View File

@@ -692,18 +692,19 @@ Run 'update-finance-quote' as root to install them.") "\n")))
prices)))))))
(define (gnc:add-quotes-to-book-at-url url)
(let* ((book (gnc:url->loaded-book url #f #f))
(let* ((session (gnc:url->loaded-session url #f #f))
(book (gnc:session-get-book session))
(quote-ok? (and book (gnc:book-add-quotes book))))
(if (not quote-ok?) (gnc:msg "book-add-quotes failed"))
(and book (gnc:book-save book))
(and session (gnc:session-save session))
(if (not (eq? 'no-err
(gw:enum-<gnc:BackendError>-val->sym
(gnc:book-get-error book) #f)))
(gnc:session-get-error session) #f)))
(set! quote-ok? #f))
(if (not quote-ok?)
(gnc:msg "book-save failed " (gnc:book-get-error book)))
(and book (gnc:book-destroy book))
(gnc:msg "book-save failed " (gnc:session-get-error session)))
(and session (gnc:session-destroy session))
quote-ok?))
; (define (get-1-quote exchange . items)