mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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:
@@ -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 ();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/* ============================================================= */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 '()))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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? */
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
83
src/engine/gnc-session-p.h
Normal file
83
src/engine/gnc-session-p.h
Normal 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
887
src/engine/gnc-session.c
Normal 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
188
src/engine/gnc-session.h
Normal 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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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>");
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user