mirror of
https://github.com/Gnucash/gnucash.git
synced 2026-08-10 04:58:26 -05:00
Change Sessions to GNCBooks, which will represent a set of gnucash
data, including account hierarchy, budgets, recurring transactions, etc. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3154 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
+147
-184
@@ -21,28 +21,26 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include <guile/gh.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Destroy.h"
|
||||
#include "FileBox.h"
|
||||
#include "FileDialog.h"
|
||||
#include "FileIO.h"
|
||||
#include "Group.h"
|
||||
#include "messages.h"
|
||||
#include "Session.h"
|
||||
#include "TransLog.h"
|
||||
#include "Destroy.h"
|
||||
#include "file-history.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "gnc-ui.h"
|
||||
#include "file-history.h"
|
||||
#include "messages.h"
|
||||
|
||||
/* This static indicates the debugging module that this .o belongs to. */
|
||||
static short module = MOD_GUI;
|
||||
|
||||
/** GLOBALS *********************************************************/
|
||||
static Session *current_session = NULL;
|
||||
static AccountGroup *topgroup = NULL; /* the current top of the hierarchy */
|
||||
static GNCBook *current_book = NULL;
|
||||
|
||||
/* ======================================================== */
|
||||
|
||||
@@ -55,7 +53,7 @@ file_not_found_msg (void)
|
||||
/* ======================================================== */
|
||||
|
||||
static gboolean
|
||||
show_file_error (GNCFileIOError io_error, char *newfile)
|
||||
show_file_error (GNCFileIOError io_error, const char *newfile)
|
||||
{
|
||||
gboolean uh_oh = FALSE;
|
||||
char *buf = NULL;
|
||||
@@ -113,9 +111,9 @@ show_file_error (GNCFileIOError io_error, char *newfile)
|
||||
/* ======================================================== */
|
||||
|
||||
static gboolean
|
||||
show_session_error(Session *session, char *newfile)
|
||||
show_book_error(GNCBook *book, const char *newfile)
|
||||
{
|
||||
int norr = xaccSessionGetError (session);
|
||||
int norr = gnc_book_get_error (book);
|
||||
gboolean uh_oh = FALSE;
|
||||
char *buf = NULL;
|
||||
|
||||
@@ -147,38 +145,30 @@ show_session_error(Session *session, char *newfile)
|
||||
void
|
||||
gncFileNew (void)
|
||||
{
|
||||
Session *sess;
|
||||
AccountGroup *grp;
|
||||
GNCBook *book;
|
||||
AccountGroup *group;
|
||||
|
||||
/* If user attempts to start a new session before saving
|
||||
* results of the last one, prompt them to clean up their
|
||||
* act. */
|
||||
/* If user attempts to start a new session before saving results of
|
||||
* the last one, prompt them to clean up their act. */
|
||||
if (!gncFileQuerySave ())
|
||||
return;
|
||||
|
||||
sess = current_session;
|
||||
grp = xaccSessionGetGroup (sess);
|
||||
/* if session not yet started ... */
|
||||
if (!grp) grp = topgroup;
|
||||
book = gncGetCurrentBook ();
|
||||
group = gnc_book_get_group (book);
|
||||
|
||||
/* when starting new everything, destroy old stuff first */
|
||||
/* destroy open windows first, before destroying the group itself */
|
||||
xaccGroupWindowDestroy (grp);
|
||||
xaccGroupWindowDestroy (group);
|
||||
|
||||
/* close any ongoing file sessions, if any */
|
||||
xaccSessionEnd (sess);
|
||||
xaccSessionDestroy (sess);
|
||||
current_session = NULL;
|
||||
topgroup = NULL;
|
||||
|
||||
/* disable logging while we move over to the new set of accounts to
|
||||
* edit; the mass deletion of accounts and transactions during
|
||||
* switchover is not something we want to keep in a journal. */
|
||||
/* close any ongoing file sessions, and free the accounts.
|
||||
* disable logging so we don't get all that junk. */
|
||||
xaccLogDisable();
|
||||
xaccFreeAccountGroup (grp);
|
||||
gnc_book_destroy (book);
|
||||
current_book = NULL;
|
||||
xaccLogEnable();
|
||||
grp = xaccMallocAccountGroup();
|
||||
topgroup = grp;
|
||||
|
||||
/* start a new book */
|
||||
gncGetCurrentBook ();
|
||||
}
|
||||
|
||||
/* ======================================================== */
|
||||
@@ -186,16 +176,14 @@ gncFileNew (void)
|
||||
gboolean
|
||||
gncFileQuerySave (void)
|
||||
{
|
||||
Session *sess;
|
||||
AccountGroup *grp;
|
||||
GNCBook *book;
|
||||
AccountGroup *group;
|
||||
gncUIWidget app;
|
||||
|
||||
sess = current_session;
|
||||
grp = xaccSessionGetGroup (sess);
|
||||
/* if session not yet started ... */
|
||||
if (!grp) grp = topgroup;
|
||||
book = gncGetCurrentBook ();
|
||||
group = gnc_book_get_group (book);
|
||||
|
||||
app = gnc_get_ui_data();
|
||||
app = gnc_get_ui_data ();
|
||||
|
||||
/* If user wants to mess around before finishing business with
|
||||
* the old file, give em a chance to figure out what's up.
|
||||
@@ -203,7 +191,7 @@ gncFileQuerySave (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 ( xaccGroupNotSaved (grp) )
|
||||
while (xaccGroupNotSaved (group))
|
||||
{
|
||||
GNCVerifyResult result;
|
||||
const char *message = _("Changes have been made since the last "
|
||||
@@ -252,15 +240,17 @@ gncLockFailHandler (const char *file)
|
||||
static void
|
||||
gncPostFileOpen (const char * filename)
|
||||
{
|
||||
Session *newsess;
|
||||
AccountGroup *oldgrp;
|
||||
GNCBook *new_book;
|
||||
AccountGroup *old_group;
|
||||
gboolean uh_oh = FALSE;
|
||||
AccountGroup *newgrp;
|
||||
AccountGroup *new_group;
|
||||
char * newfile;
|
||||
|
||||
if (!filename) return;
|
||||
|
||||
newfile = xaccResolveFilePath (filename);
|
||||
if (!newfile) {
|
||||
if (!newfile)
|
||||
{
|
||||
char *buf = g_strdup_printf (file_not_found_msg(), filename);
|
||||
gnc_error_dialog (buf);
|
||||
g_free(buf);
|
||||
@@ -269,47 +259,43 @@ gncPostFileOpen (const char * filename)
|
||||
|
||||
/* -------------- BEGIN CORE SESSION CODE ------------- */
|
||||
/* -- this code is almost identical in FileOpen and FileSaveAs -- */
|
||||
oldgrp = xaccSessionGetGroup (current_session);
|
||||
/* if session not yet started ... */
|
||||
if (!oldgrp) oldgrp = topgroup;
|
||||
old_group = gnc_book_get_group (gncGetCurrentBook ());
|
||||
|
||||
/* load the accounts from the users datafile */
|
||||
/* but first, check to make sure we've got a session going ... */
|
||||
newsess = xaccMallocSession ();
|
||||
/* but first, check to make sure we've got a book going. */
|
||||
new_book = gnc_book_new ();
|
||||
|
||||
/* disable logging while we move over to the new set of accounts to
|
||||
* edit; the mass deletetion of accounts and transactions during
|
||||
* switchover is not something we want to keep in a journal. */
|
||||
gnc_set_busy_cursor(NULL);
|
||||
xaccLogDisable();
|
||||
newgrp = NULL;
|
||||
if(xaccSessionBeginFile(newsess, newfile, gncLockFailHandler)) {
|
||||
if(xaccSessionLoad(newsess)) {
|
||||
newgrp = xaccSessionGetGroup(newsess);
|
||||
}
|
||||
gnc_set_busy_cursor (NULL);
|
||||
xaccLogDisable ();
|
||||
new_group = NULL;
|
||||
if (gnc_book_begin_file (new_book, newfile, gncLockFailHandler))
|
||||
{
|
||||
if (gnc_book_load (new_book))
|
||||
new_group = gnc_book_get_group (new_book);
|
||||
}
|
||||
xaccLogEnable();
|
||||
gnc_unset_busy_cursor(NULL);
|
||||
xaccLogEnable ();
|
||||
gnc_unset_busy_cursor (NULL);
|
||||
|
||||
/* check for session errors, put up appropriate dialog */
|
||||
uh_oh = show_session_error (newsess, newfile);
|
||||
/* check for book errors, put up appropriate dialog */
|
||||
uh_oh = show_book_error (new_book, newfile);
|
||||
|
||||
if (!uh_oh)
|
||||
{
|
||||
GNCFileIOError io_err = xaccSessionGetFileError(newsess);
|
||||
GNCFileIOError io_err = gnc_book_get_file_error (new_book);
|
||||
|
||||
/* check for i/o error, put up appropriate error message */
|
||||
uh_oh = show_file_error(io_err, newfile);
|
||||
if (uh_oh)
|
||||
{
|
||||
xaccFreeAccountGroup (newgrp);
|
||||
newgrp = NULL;
|
||||
}
|
||||
new_group = NULL;
|
||||
|
||||
/* Umm, came up empty-handed, i.e. the file was not found. */
|
||||
/* This is almost certainly not what the user wanted. */
|
||||
if (!uh_oh && !newgrp && (io_err == ERR_FILEIO_NONE))
|
||||
if (!uh_oh && !new_group && (io_err == ERR_FILEIO_NONE))
|
||||
{
|
||||
char *buf = g_strdup_printf (file_not_found_msg(), newfile);
|
||||
char *buf = g_strdup_printf (file_not_found_msg(), newfile);
|
||||
gnc_error_dialog (buf);
|
||||
g_free (buf);
|
||||
uh_oh = TRUE;
|
||||
@@ -319,50 +305,46 @@ gncPostFileOpen (const char * filename)
|
||||
/* going down -- abandon ship */
|
||||
if (uh_oh)
|
||||
{
|
||||
xaccSessionEnd (newsess);
|
||||
xaccSessionDestroy (newsess);
|
||||
gnc_book_destroy (new_book);
|
||||
|
||||
/* well, no matter what, I think its a good idea to have a
|
||||
* topgroup around. For example, early in the gnucash startup
|
||||
* sequence, the user opens a file; if this open fails for any
|
||||
* reason, we don't want to leave them high & dry without a
|
||||
* topgroup, because if the user continues, then bad things will
|
||||
* happen. */
|
||||
gncGetCurrentBook ();
|
||||
|
||||
/* well, no matter what, I think its a good idea to have
|
||||
* a topgroup around. For example, early in the gnucash startup
|
||||
* sequence, the user opens a file; if this open fails for any
|
||||
* reason, we don't want to leave them high & dry without a topgroup,
|
||||
* because if the user continues, then bad things will happen.
|
||||
*/
|
||||
if (NULL == topgroup)
|
||||
{
|
||||
topgroup = xaccMallocAccountGroup();
|
||||
}
|
||||
free (newfile);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if we got to here, then we've successfully gotten a new session */
|
||||
/* close up the old file session (if any) */
|
||||
xaccSessionEnd (current_session);
|
||||
xaccSessionDestroy (current_session);
|
||||
current_session = newsess;
|
||||
xaccLogDisable();
|
||||
xaccLogSetBaseName (newfile);
|
||||
|
||||
xaccGroupWindowDestroy (old_group);
|
||||
gnc_book_destroy (current_book);
|
||||
current_book = new_book;
|
||||
|
||||
xaccLogEnable();
|
||||
|
||||
/* --------------- END CORE SESSION CODE -------------- */
|
||||
|
||||
/* clean up old stuff, and then we're outta here. */
|
||||
xaccLogDisable();
|
||||
xaccLogSetBaseName (newfile);
|
||||
gnc_history_add_file (newfile);
|
||||
/* destroy open windows first, before destroying the group itself */
|
||||
xaccGroupWindowDestroy (oldgrp);
|
||||
xaccFreeAccountGroup (oldgrp);
|
||||
topgroup = newgrp;
|
||||
xaccLogEnable();
|
||||
|
||||
free (newfile);
|
||||
|
||||
/* run a file-opened hook. For now, the main thing it will do
|
||||
/* run a file-opened hook. For now, the main thing it will do
|
||||
* is notice if legacy currencies are being imported. */
|
||||
{
|
||||
SCM run_danglers = gh_eval_str("gnc:hook-run-danglers");
|
||||
SCM hook = gh_eval_str("gnc:*file-opened-hook*");
|
||||
SCM filename;
|
||||
|
||||
if(newfile) {
|
||||
|
||||
if (newfile)
|
||||
{
|
||||
filename = gh_str02scm(newfile);
|
||||
gh_call2(run_danglers, hook, filename);
|
||||
}
|
||||
@@ -386,8 +368,7 @@ gncFileOpen (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. */
|
||||
if (topgroup == NULL)
|
||||
topgroup = xaccMallocAccountGroup();
|
||||
gncGetCurrentBook ();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -402,22 +383,23 @@ gncFileOpenFile (const char * newfile)
|
||||
}
|
||||
|
||||
/* ======================================================== */
|
||||
static int been_here_before = 0;
|
||||
static gboolean been_here_before = FALSE;
|
||||
|
||||
void
|
||||
gncFileSave (void)
|
||||
{
|
||||
AccountGroup *newgrp = NULL;
|
||||
char * newfile;
|
||||
GNCFileIOError io_err;
|
||||
int norr, uh_oh = 0;
|
||||
const char * newfile;
|
||||
GNCBook *book;
|
||||
int uh_oh = 0;
|
||||
int norr;
|
||||
|
||||
/* hack alert -- Somehow make sure all in-progress edits get committed! */
|
||||
|
||||
/* if no session exists, then we don't have a filename/path
|
||||
* to save to. Get one now. */
|
||||
if ((NULL == current_session) ||
|
||||
(NULL == xaccSessionGetGroup (current_session)))
|
||||
/* If we don't have a filename/path to save to get one. */
|
||||
book = gncGetCurrentBook ();
|
||||
|
||||
if (!gnc_book_get_file_path (book))
|
||||
{
|
||||
gncFileSaveAs();
|
||||
return;
|
||||
@@ -425,19 +407,19 @@ gncFileSave (void)
|
||||
|
||||
/* use the current session to save to file */
|
||||
gnc_set_busy_cursor(NULL);
|
||||
xaccSessionSave (current_session);
|
||||
gnc_book_save (book);
|
||||
gnc_unset_busy_cursor(NULL);
|
||||
|
||||
/* Make sure everything's OK - disk could be full, file could have
|
||||
become read-only etc. */
|
||||
norr = xaccSessionGetError (current_session);
|
||||
norr = gnc_book_get_error (book);
|
||||
if (norr)
|
||||
{
|
||||
const char *format = _("There was an error writing the file\n %s"
|
||||
"\n\n%s");
|
||||
char *message;
|
||||
|
||||
newfile = xaccSessionGetFilePath(current_session);
|
||||
newfile = gnc_book_get_file_path (book);
|
||||
if (newfile == NULL)
|
||||
newfile = "";
|
||||
|
||||
@@ -447,9 +429,9 @@ gncFileSave (void)
|
||||
|
||||
if (been_here_before) return;
|
||||
|
||||
been_here_before = 1;
|
||||
been_here_before = TRUE;
|
||||
gncFileSaveAs(); /* been_here prevents infinite recuirsion */
|
||||
been_here_before = 0;
|
||||
been_here_before = FALSE;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -457,21 +439,16 @@ gncFileSave (void)
|
||||
/* check for i/o error, put up appropriate error message.
|
||||
* NOTE: the file-writing routines never set the file io
|
||||
* error code, so this seems to be unneccesary. */
|
||||
io_err = xaccSessionGetFileError(current_session);
|
||||
newfile = xaccSessionGetFilePath(current_session);
|
||||
gnc_history_add_file(newfile);
|
||||
io_err = gnc_book_get_file_error (book);
|
||||
newfile = gnc_book_get_file_path (book);
|
||||
gnc_history_add_file (newfile);
|
||||
|
||||
uh_oh = show_file_error (io_err, newfile);
|
||||
if (uh_oh)
|
||||
{
|
||||
xaccFreeAccountGroup (newgrp);
|
||||
newgrp = NULL;
|
||||
}
|
||||
|
||||
/* going down -- abandon ship */
|
||||
if (uh_oh) return;
|
||||
|
||||
xaccGroupMarkSaved (topgroup);
|
||||
xaccGroupMarkSaved (gnc_book_get_group (book));
|
||||
}
|
||||
|
||||
/* ======================================================== */
|
||||
@@ -479,81 +456,80 @@ gncFileSave (void)
|
||||
void
|
||||
gncFileSaveAs (void)
|
||||
{
|
||||
Session *newsess;
|
||||
AccountGroup *oldgrp;
|
||||
AccountGroup *group;
|
||||
GNCBook *new_book;
|
||||
GNCBook *book;
|
||||
const char *filename;
|
||||
char *newfile;
|
||||
char * oldfile;
|
||||
const char *oldfile;
|
||||
gboolean uh_oh = FALSE;
|
||||
|
||||
filename = fileBox(_("Save"), "*.gnc");
|
||||
if (!filename) return;
|
||||
|
||||
/* check to see if the user did something silly,
|
||||
* like specifying the same file as the current file ...
|
||||
* if so, then just do that, instead of the below,
|
||||
* which assumes a truly new name was given.
|
||||
*/
|
||||
/* Check to see if the user specified the same file as the current
|
||||
* file. If so, then just do that, instead of the below, which
|
||||
* assumes a truly new name was given. */
|
||||
newfile = xaccResolveFilePath (filename);
|
||||
if (!newfile) {
|
||||
if (!newfile)
|
||||
{
|
||||
char *buf = g_strdup_printf (file_not_found_msg(), filename);
|
||||
gnc_error_dialog (buf);
|
||||
g_free (buf);
|
||||
return;
|
||||
}
|
||||
oldfile = xaccSessionGetFilePath (current_session);
|
||||
if (oldfile && (strcmp(oldfile, newfile) == 0)) {
|
||||
|
||||
book = gncGetCurrentBook ();
|
||||
oldfile = gnc_book_get_file_path (book);
|
||||
if (oldfile && (strcmp(oldfile, newfile) == 0))
|
||||
{
|
||||
free (newfile);
|
||||
gncFileSave ();
|
||||
return;
|
||||
}
|
||||
|
||||
/* -- this session code is NOT identical in FileOpen and FileSaveAs -- */
|
||||
oldgrp = xaccSessionGetGroup (current_session);
|
||||
/* if session not yet started ... */
|
||||
if (!oldgrp) oldgrp = topgroup;
|
||||
|
||||
/* create a new session ... */
|
||||
newsess = xaccMallocSession ();
|
||||
group = gnc_book_get_group (book);
|
||||
|
||||
/* disable logging while we move over to the new set of accounts to
|
||||
* edit; the mass deletetion of accounts and transactions during
|
||||
* switchover is not something we want to keep in a journal. */
|
||||
xaccLogDisable();
|
||||
xaccSessionBeginFile(newsess, newfile, gncLockFailHandler);
|
||||
xaccLogEnable();
|
||||
* switchover is not something we want to keep in a journal. */
|
||||
xaccLogDisable ();
|
||||
new_book = gnc_book_new ();
|
||||
gnc_book_begin_file (new_book, newfile, gncLockFailHandler);
|
||||
xaccLogEnable ();
|
||||
|
||||
/* check for session errors (e.g. file locked by another user) */
|
||||
uh_oh = show_session_error (newsess, newfile);
|
||||
uh_oh = show_book_error (new_book, newfile);
|
||||
|
||||
/* No check for file errors since we didn't read a file... */
|
||||
|
||||
/* going down -- abandon ship */
|
||||
if (uh_oh) {
|
||||
xaccSessionDestroy (newsess);
|
||||
if (uh_oh)
|
||||
{
|
||||
xaccLogDisable ();
|
||||
gnc_book_destroy (new_book);
|
||||
xaccLogEnable ();
|
||||
|
||||
/* well, no matter what, I think its a good idea to have
|
||||
* a topgroup around. For example, early in the gnucash startup
|
||||
* sequence, the user opens a file ... if this open fails for any
|
||||
* reason, we don't want to leave them high & dry without a topgroup,
|
||||
* because if user continues, then bad things will happen ...
|
||||
*/
|
||||
if(NULL == topgroup) {
|
||||
topgroup = xaccMallocAccountGroup();
|
||||
}
|
||||
free (newfile);
|
||||
gnc_refresh_main_window();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* if we got to here, then we've successfully gotten a new session */
|
||||
/* close up the old file session (if any) */
|
||||
xaccSessionDestroy (current_session);
|
||||
current_session = newsess;
|
||||
xaccLogDisable ();
|
||||
gnc_book_set_group (book, NULL);
|
||||
gnc_book_destroy (book);
|
||||
current_book = new_book;
|
||||
xaccLogEnable ();
|
||||
|
||||
/* --------------- END CORE SESSION CODE -------------- */
|
||||
|
||||
/* oops ... file already exists ... ask user what to do... */
|
||||
if(xaccSessionSaveMayClobberData(newsess)) {
|
||||
if (gnc_book_save_may_clobber_data (new_book))
|
||||
{
|
||||
const char *format = _("The file \n %s\n already exists.\n"
|
||||
"Are you sure you want to overwrite it?");
|
||||
char *tmpmsg;
|
||||
@@ -567,20 +543,17 @@ gncFileSaveAs (void)
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
/* Whoa-ok. Blow away the previous file.
|
||||
* Do not disable logging ... we want to capture the
|
||||
* old file in the log, just in case the user later
|
||||
* decides it was all a big mistake. */
|
||||
xaccSessionSetGroup (newsess, NULL);
|
||||
/* xaccLogDisable(); no don't disable, keep logging on */
|
||||
/* Whoa-ok. Blow away the previous file. Do not disable
|
||||
* logging. We want to capture the old file in the log, just in
|
||||
* case the user later decides it was all a big mistake. */
|
||||
}
|
||||
|
||||
/* OK, save the data to the file ... */
|
||||
xaccLogSetBaseName (newfile);
|
||||
xaccSessionSetGroup (newsess, oldgrp);
|
||||
gnc_book_set_group (new_book, group);
|
||||
gncFileSave ();
|
||||
free (newfile);
|
||||
|
||||
free (newfile);
|
||||
gnc_refresh_main_window();
|
||||
}
|
||||
|
||||
@@ -589,21 +562,16 @@ gncFileSaveAs (void)
|
||||
void
|
||||
gncFileQuit (void)
|
||||
{
|
||||
AccountGroup *grp;
|
||||
GNCBook *book;
|
||||
|
||||
grp = xaccSessionGetGroup (current_session);
|
||||
/* if session not yet started ... */
|
||||
if (!grp) grp = topgroup;
|
||||
book = gncGetCurrentBook ();
|
||||
|
||||
/* disable logging; the mass deletetion of accounts and transactions
|
||||
* during shutdown is not something we want to keep in a journal. */
|
||||
xaccLogDisable();
|
||||
|
||||
xaccSessionEnd (current_session);
|
||||
xaccSessionDestroy (current_session);
|
||||
current_session = NULL;
|
||||
xaccFreeAccountGroup (grp);
|
||||
topgroup = NULL;
|
||||
gnc_book_destroy (book);
|
||||
current_book = NULL;
|
||||
}
|
||||
|
||||
/* ======================================================== */
|
||||
@@ -611,27 +579,22 @@ gncFileQuit (void)
|
||||
AccountGroup *
|
||||
gncGetCurrentGroup (void)
|
||||
{
|
||||
AccountGroup *grp;
|
||||
grp = xaccSessionGetGroup (current_session);
|
||||
if (grp) return grp;
|
||||
GNCBook *book;
|
||||
|
||||
/* If we are here, then no session has yet been started ... */
|
||||
grp = topgroup;
|
||||
if (grp) return grp;
|
||||
book = gncGetCurrentBook ();
|
||||
|
||||
/* if we are here, then topgroup not yet initialized ... */
|
||||
xaccLogEnable();
|
||||
topgroup = xaccMallocAccountGroup();
|
||||
|
||||
return topgroup;
|
||||
return gnc_book_get_group (book);
|
||||
}
|
||||
|
||||
/* ======================================================== */
|
||||
|
||||
Session *
|
||||
gncGetCurrentSession (void)
|
||||
GNCBook *
|
||||
gncGetCurrentBook (void)
|
||||
{
|
||||
return (current_session);
|
||||
if (!current_book)
|
||||
current_book = gnc_book_new ();
|
||||
|
||||
return current_book;
|
||||
}
|
||||
|
||||
/********************* END OF FILE **********************************/
|
||||
|
||||
+2
-2
@@ -122,7 +122,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "Group.h"
|
||||
#include "Session.h"
|
||||
#include "gnc-book.h"
|
||||
|
||||
void gncFileNew (void);
|
||||
void gncFileOpen (void);
|
||||
@@ -138,6 +138,6 @@ void gncFileQuit (void);
|
||||
|
||||
AccountGroup *gncGetCurrentGroup (void);
|
||||
|
||||
Session *gncGetCurrentSession (void);
|
||||
GNCBook *gncGetCurrentBook (void);
|
||||
|
||||
#endif /* __GNC_FILE_DIALOG_H__ */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* back-ends (which will probably be sql databases).
|
||||
*
|
||||
* The callbacks will be called at the appropriate times during
|
||||
* a session to allow the backend to store the data as needed.
|
||||
* a book session to allow the backend to store the data as needed.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
#define __XACC_BACKEND_P_H__
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Account.h"
|
||||
#include "Group.h"
|
||||
#include "Session.h"
|
||||
#include "Transaction.h"
|
||||
#include "gnc-book.h"
|
||||
|
||||
typedef struct _backend Backend;
|
||||
|
||||
@@ -31,8 +32,8 @@ typedef struct _backend Backend;
|
||||
|
||||
struct _backend
|
||||
{
|
||||
AccountGroup * (*session_begin) (Session *, const char * sessionid);
|
||||
int (*session_end) (Session *);
|
||||
AccountGroup * (*book_begin) (GNCBook *, const char * book_id);
|
||||
int (*book_end) (GNCBook *);
|
||||
int (*account_begin_edit) (Backend *, Account *, int defer);
|
||||
int (*account_commit_edit) (Backend *, Account *);
|
||||
int (*trans_begin_edit) (Backend *, Transaction *, int defer);
|
||||
@@ -60,7 +61,7 @@ Backend * xaccTransactionGetBackend (Transaction *);
|
||||
*/
|
||||
void xaccGroupSetBackend (AccountGroup *, Backend *);
|
||||
Backend * xaccGroupGetBackend (AccountGroup *);
|
||||
Backend * xaccSessionGetBackend (Session *);
|
||||
Backend * xaccGNCBookGetBackend (GNCBook *book);
|
||||
|
||||
|
||||
#endif /* __XACC_BACKEND_P_H__ */
|
||||
|
||||
+2
-2
@@ -144,8 +144,8 @@ xaccFreeAccountGroup( AccountGroup *grp )
|
||||
|
||||
xaccAccountGroupBeginEdit (grp, 1);
|
||||
|
||||
for( i=0; i<grp->numAcc; i++ )
|
||||
xaccFreeAccount( grp->account[i] );
|
||||
for(i = 0; i < grp->numAcc; i++ )
|
||||
xaccFreeAccount(grp->account[i]);
|
||||
|
||||
g_free(grp->account);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ libgncengine_la_SOURCES = \
|
||||
Group.c \
|
||||
Query.c \
|
||||
Scrub.c \
|
||||
Session.c \
|
||||
Transaction.c \
|
||||
TransLog.c \
|
||||
date.c \
|
||||
@@ -25,7 +24,8 @@ libgncengine_la_SOURCES = \
|
||||
io-gncxml-w.c \
|
||||
md5.c \
|
||||
kvp_frame.c \
|
||||
gnc-commodity.c\
|
||||
gnc-book.c \
|
||||
gnc-commodity.c \
|
||||
gnc-numeric.c \
|
||||
gnc-engine.c \
|
||||
gnc-engine-util.c
|
||||
@@ -45,7 +45,6 @@ noinst_HEADERS = \
|
||||
GroupP.h \
|
||||
Query.h \
|
||||
Scrub.h \
|
||||
Session.h \
|
||||
TransLog.h \
|
||||
Transaction.h \
|
||||
TransactionP.h \
|
||||
@@ -56,6 +55,7 @@ noinst_HEADERS = \
|
||||
io-gncxml.h \
|
||||
md5.h \
|
||||
kvp_frame.h \
|
||||
gnc-book.h \
|
||||
gnc-commodity.h \
|
||||
gnc-numeric.h \
|
||||
gnc-engine.h \
|
||||
|
||||
@@ -1,639 +0,0 @@
|
||||
/********************************************************************\
|
||||
* 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:
|
||||
* Session.c
|
||||
*
|
||||
* FUNCTION:
|
||||
* Provide wrappers for initiating/concluding a file-editing session.
|
||||
*
|
||||
* HISTORY:
|
||||
* Created by Linas Vepstas December 1998
|
||||
* Copyright (c) 1998-2000 Linas Vepstas
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "BackendP.h"
|
||||
#include "FileIO.h"
|
||||
#include "Group.h"
|
||||
#include "Session.h"
|
||||
#include "gnc-engine-util.h"
|
||||
|
||||
|
||||
struct _session {
|
||||
AccountGroup *topgroup;
|
||||
|
||||
/* the requested session id, in the form or a URI, such as
|
||||
* file:/some/where, or sql:server.host.com:555
|
||||
*/
|
||||
char *sessionid;
|
||||
|
||||
/* if session failed, this records the failure reason
|
||||
* (file not found, etc).
|
||||
* the standard errno values are used.
|
||||
*/
|
||||
int errtype;
|
||||
|
||||
/* 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 Session errors... */
|
||||
GNCFileIOError last_file_err;
|
||||
|
||||
/* ---------------------------------------------------- */
|
||||
/* teh following struct members apply only for file-io */
|
||||
/* the fully-resolved path to the file */
|
||||
char *fullpath;
|
||||
|
||||
/* name of lockfile, and filedescr */
|
||||
char * lockfile;
|
||||
char * linkfile;
|
||||
int lockfd;
|
||||
|
||||
/* ---------------------------------------------------- */
|
||||
/* this struct member applies only for SQL i/o */
|
||||
Backend *backend;
|
||||
|
||||
};
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
Session *
|
||||
xaccMallocSession (void)
|
||||
{
|
||||
Session *sess;
|
||||
sess = g_new(Session, 1);
|
||||
|
||||
xaccInitSession (sess);
|
||||
return sess;
|
||||
}
|
||||
|
||||
void
|
||||
xaccInitSession (Session *sess)
|
||||
{
|
||||
if (!sess) return;
|
||||
sess->topgroup = NULL;
|
||||
sess->errtype = 0;
|
||||
sess->last_file_err = ERR_FILEIO_NONE;
|
||||
sess->sessionid = NULL;
|
||||
sess->fullpath = NULL;
|
||||
sess->lockfile = NULL;
|
||||
sess->linkfile = NULL;
|
||||
sess->lockfd = -1;
|
||||
sess->backend = NULL;
|
||||
};
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
int
|
||||
xaccSessionGetError (Session * sess)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (!sess) return EINVAL;
|
||||
retval = sess->errtype;
|
||||
sess->errtype = 0;
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
GNCFileIOError
|
||||
xaccSessionGetFileError (Session * sess) {
|
||||
if (!sess) return ERR_FILEIO_MISC;
|
||||
return sess->last_file_err;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
AccountGroup *
|
||||
xaccSessionGetGroup (Session *sess)
|
||||
{
|
||||
if (!sess) return NULL;
|
||||
return (sess->topgroup);
|
||||
}
|
||||
|
||||
void
|
||||
xaccSessionSetGroup (Session *sess, AccountGroup *grp)
|
||||
{
|
||||
if (!sess) return;
|
||||
sess->topgroup = grp;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
Backend *
|
||||
xaccSessionGetBackend (Session *sess)
|
||||
{
|
||||
if (!sess) return NULL;
|
||||
return (sess->backend);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
char *
|
||||
xaccSessionGetFilePath (Session *sess)
|
||||
{
|
||||
if (!sess) return NULL;
|
||||
return (sess->fullpath);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
gboolean
|
||||
xaccSessionBegin (Session *sess, const char * sid) {
|
||||
|
||||
if(!sess) return FALSE;
|
||||
if(!sid) return FALSE;
|
||||
|
||||
/* clear the error condition of previous errors */
|
||||
sess->errtype = 0;
|
||||
sess->last_file_err = ERR_FILEIO_NONE;
|
||||
|
||||
/* check to see if this session is already open */
|
||||
if (sess->sessionid) {
|
||||
sess->errtype = ETXTBSY;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* seriously invalid */
|
||||
if (!sid) {
|
||||
sess->errtype = EINVAL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check to see if this is a type we know how to handle */
|
||||
if(strncmp(sid, "file:", 5) != 0) {
|
||||
sess->errtype = ENOSYS;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* add 5 to space past 'file:' */
|
||||
return xaccSessionBeginFile(sess, sid+5, NULL);
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
#if 0
|
||||
static AccountGroup *
|
||||
xaccSessionBeginSQL (Session *sess, const char * dbname)
|
||||
{
|
||||
Backend *be = NULL;
|
||||
AccountGroup *grp = NULL;
|
||||
|
||||
if (!sess) return NULL;
|
||||
|
||||
/* #define SQLHACK */
|
||||
#ifdef SQLHACK
|
||||
{
|
||||
/* for testing the sql, just a hack, remove later ... */
|
||||
extern Backend * pgendNew (void);
|
||||
be = pgendNew ();
|
||||
}
|
||||
#endif
|
||||
|
||||
sess->backend = be;
|
||||
|
||||
if (be && be->session_begin) {
|
||||
grp = (be->session_begin) (sess, dbname);
|
||||
}
|
||||
/* comment out until testing done, else clobber file ...*/
|
||||
/* sess->topgroup = grp; */
|
||||
xaccGroupSetBackend (sess->topgroup, be);
|
||||
|
||||
return (sess->topgroup);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
static gboolean
|
||||
xaccSessionGetFileLock (Session *sess)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char pathbuf[PATH_MAX];
|
||||
char *path = NULL;
|
||||
int rc;
|
||||
|
||||
rc = stat (sess->lockfile, &statbuf);
|
||||
if (!rc) {
|
||||
/* oops .. file is all locked up .. */
|
||||
sess->errtype = ETXTBSY;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sess->lockfd = open (sess->lockfile, O_RDWR | O_CREAT | O_EXCL , 0);
|
||||
if (0 > sess->lockfd) {
|
||||
/* oops .. file is all locked up .. */
|
||||
sess->errtype = ETXTBSY;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* OK, now work around some NFS atomic lock race condition
|
||||
* mumbo-jumbo. We do this by linking a unique file, and
|
||||
* then examing the link count. At least that's what the
|
||||
* NFS programmers guide suggests.
|
||||
* Note: the "unique filename" must be unique for the
|
||||
* triplet filename-host-process, otherwise accidental
|
||||
* aliases can occur.
|
||||
*/
|
||||
|
||||
/* apparently, even this code may not work for some NFS
|
||||
* implementations. In the long run, I am told that
|
||||
* ftp.debian.org
|
||||
* /pub/debian/dists/unstable/main/source/libs/liblockfile_0.1-6.tar.gz
|
||||
* provides a better long-term solution.
|
||||
*/
|
||||
|
||||
strcpy (pathbuf, sess->lockfile);
|
||||
path = strrchr (pathbuf, '.');
|
||||
sprintf (path, ".%lx.%d.LNK", gethostid(), getpid());
|
||||
link (sess->lockfile, pathbuf);
|
||||
rc = stat (sess->lockfile, &statbuf);
|
||||
if (rc) {
|
||||
/* oops .. stat failed! This can't happen! */
|
||||
sess->errtype = ETXTBSY;
|
||||
unlink (pathbuf);
|
||||
close (sess->lockfd);
|
||||
unlink (sess->lockfile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (2 != statbuf.st_nlink) {
|
||||
/* oops .. stat failed! This can't happen! */
|
||||
sess->errtype = ETXTBSY;
|
||||
unlink (pathbuf);
|
||||
close (sess->lockfd);
|
||||
unlink (sess->lockfile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sess->linkfile = g_strdup (pathbuf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
gboolean
|
||||
xaccSessionBeginFile (Session *sess, const char * filefrag,
|
||||
SessionLockFailHandler handler) {
|
||||
|
||||
if(!sess) return FALSE;
|
||||
if(!filefrag) return FALSE;
|
||||
|
||||
/* clear the error condition of previous errors */
|
||||
sess->errtype = 0;
|
||||
sess->last_file_err = ERR_FILEIO_NONE;
|
||||
|
||||
/* check to see if this session is already open */
|
||||
if (sess->sessionid) {
|
||||
sess->errtype = ETXTBSY;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* seriously invalid */
|
||||
if (!filefrag) {
|
||||
sess->errtype = EINVAL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------- */
|
||||
/* OK, now we try to find or build an absolute file path */
|
||||
|
||||
sess->fullpath = xaccResolveFilePath (filefrag);
|
||||
if (! (sess->fullpath)) {
|
||||
sess->errtype = ERANGE;
|
||||
return FALSE; /* ouch */
|
||||
}
|
||||
|
||||
/* Store the sessionid URL also ... */
|
||||
sess->sessionid = g_strconcat ("file:", filefrag, NULL);
|
||||
|
||||
/* ---------------------------------------------------- */
|
||||
/* We should now have a fully resolved path name.
|
||||
* Lets see if we can get a lock on it. */
|
||||
|
||||
sess->lockfile = g_strconcat(sess->fullpath, ".LCK", NULL);
|
||||
|
||||
if (!xaccSessionGetFileLock (sess)) {
|
||||
if (!handler || !handler (sess->fullpath)) {
|
||||
g_free (sess->sessionid); sess->sessionid = NULL;
|
||||
g_free (sess->fullpath); sess->fullpath = NULL;
|
||||
g_free (sess->lockfile); sess->lockfile = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef SQLHACK
|
||||
/* for testing the sql, just a hack, remove later ... */
|
||||
/* this should never ever appear here ... */
|
||||
xaccSessionBeginSQL (sess, "postgres://localhost/gnc_bogus");
|
||||
#endif
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
gboolean
|
||||
xaccSessionLoad(Session *sess) {
|
||||
if(!sess) return FALSE;
|
||||
if(!sess->sessionid) return FALSE;
|
||||
|
||||
if(strncmp(sess->sessionid, "file:", 5) == 0) {
|
||||
/* file: */
|
||||
|
||||
if(!sess->lockfile) {
|
||||
sess->errtype = ENOLCK;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* At this point, we should have a valid session id and a lock on
|
||||
the file. */
|
||||
|
||||
sess->errtype = 0;
|
||||
sess->last_file_err = ERR_FILEIO_NONE;
|
||||
sess->topgroup = xaccReadAccountGroupFile (sess->fullpath,
|
||||
&(sess->last_file_err));
|
||||
|
||||
if(!sess->topgroup || (sess->last_file_err != ERR_FILEIO_NONE)) {
|
||||
sess->errtype = EIO;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
} else {
|
||||
sess->errtype = ENOSYS;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
gboolean
|
||||
xaccSessionSaveMayClobberData(Session *s) {
|
||||
/* FIXME: Make sure this doesn't need more sophisticated semantics
|
||||
in the face of special file, devices, pipes, symlinks, etc... */
|
||||
|
||||
struct stat statbuf;
|
||||
|
||||
if(!s) return FALSE;
|
||||
if(!s->fullpath) return FALSE;
|
||||
if(stat(s->fullpath, &statbuf) == 0) return TRUE;
|
||||
return FALSE;
|
||||
|
||||
#ifdef SQLHACK
|
||||
/* for testing the sql, just a hack, remove later ... */
|
||||
/* this should never ever appear here ... */
|
||||
xaccSessionBeginSQL (sess, "postgres://localhost/gnc_bogus");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccSessionSave (Session *sess)
|
||||
{
|
||||
if (!sess) 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. */
|
||||
sess->errtype = 0;
|
||||
sess->last_file_err = ERR_FILEIO_NONE;
|
||||
|
||||
if (!(sess->fullpath)) {
|
||||
sess->errtype = ENOLCK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sess->topgroup) {
|
||||
gboolean write_ok = xaccWriteAccountGroupFile (sess->fullpath,
|
||||
sess->topgroup,
|
||||
TRUE,
|
||||
&(sess->last_file_err));
|
||||
if (!write_ok) sess->errtype = errno;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
xaccSessionEnd (Session *sess)
|
||||
{
|
||||
if (!sess) return;
|
||||
sess->errtype = 0;
|
||||
sess->last_file_err = ERR_FILEIO_NONE;
|
||||
|
||||
if (sess->linkfile) unlink (sess->linkfile);
|
||||
if (0 < sess->lockfd) close (sess->lockfd);
|
||||
if (sess->lockfile) unlink (sess->lockfile);
|
||||
|
||||
g_free (sess->sessionid);
|
||||
sess->sessionid = NULL;
|
||||
|
||||
g_free (sess->fullpath);
|
||||
sess->fullpath = NULL;
|
||||
|
||||
g_free (sess->lockfile);
|
||||
sess->lockfile = NULL;
|
||||
|
||||
g_free (sess->linkfile);
|
||||
sess->linkfile = NULL;
|
||||
|
||||
sess->topgroup = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
xaccSessionDestroy (Session *sess)
|
||||
{
|
||||
if (!sess) return;
|
||||
xaccSessionEnd (sess);
|
||||
g_free (sess);
|
||||
}
|
||||
|
||||
|
||||
/* ============================================================== */
|
||||
/*
|
||||
* 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/",
|
||||
NULL,
|
||||
};
|
||||
|
||||
char *
|
||||
xaccResolveFilePath (const char * filefrag)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char pathbuf[PATH_MAX];
|
||||
char *path = NULL;
|
||||
int namelen, len;
|
||||
int i, rc;
|
||||
|
||||
/* seriously invalid */
|
||||
if (!filefrag) return NULL;
|
||||
|
||||
/* ---------------------------------------------------- */
|
||||
/* OK, now we try to find or build an absolute file path */
|
||||
|
||||
/* check for an absolute file path */
|
||||
if ('/' == *filefrag)
|
||||
return strdup (filefrag);
|
||||
|
||||
/* get conservative on the length so that sprintf(getpid()) works ... */
|
||||
/* strlen ("/.LCK") + sprintf (%x%d) */
|
||||
namelen = strlen (filefrag) + 25;
|
||||
|
||||
for (i=-2; 1 ; i++)
|
||||
{
|
||||
|
||||
switch (i) {
|
||||
case -2: {
|
||||
/* try to find a file by this name in the cwd ... */
|
||||
path = getcwd (pathbuf, PATH_MAX);
|
||||
if (!path) continue;
|
||||
len = strlen (path) + namelen;
|
||||
if (PATH_MAX <= len) continue;
|
||||
strcat (path, "/");
|
||||
break;
|
||||
}
|
||||
case -1: {
|
||||
/* look for something in $HOME/.gnucash/data */
|
||||
path = getenv ("HOME");
|
||||
if (!path) continue;
|
||||
len = strlen (path) + namelen + 20;
|
||||
if (PATH_MAX <= len) continue;
|
||||
strcpy (pathbuf, path);
|
||||
strcat (pathbuf, "/.gnucash/data/");
|
||||
path = pathbuf;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* OK, check the user-configured paths */
|
||||
path = searchpaths[i];
|
||||
if (path) {
|
||||
len = strlen (path) + namelen;
|
||||
if (PATH_MAX <= len) continue;
|
||||
strcpy (pathbuf, path);
|
||||
path = pathbuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!path) break;
|
||||
|
||||
/* lets see if we found the file here ... */
|
||||
/* haral: if !S_ISREG: there is something with that name
|
||||
* but it's not a regular file */
|
||||
strcat (path, filefrag);
|
||||
rc = stat (path, &statbuf);
|
||||
if ((!rc) && (S_ISREG(statbuf.st_mode))) {
|
||||
return (strdup (path));
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure that the gnucash home dir exists. */
|
||||
MakeHomeDir();
|
||||
|
||||
/* OK, we didn't find the file. */
|
||||
/* If the user specified a simple filename (i.e. no slashes in it)
|
||||
* then create the file. But if it has slashes in it, then creating
|
||||
* a bunch of directories seems like a bad idea; more likely, the user
|
||||
* specified a bad filename. So return with error. */
|
||||
if (strchr (filefrag, '/')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lets try creating a new file in $HOME/.gnucash/data */
|
||||
path = getenv ("HOME");
|
||||
if (path) {
|
||||
len = strlen (path) + namelen + 50;
|
||||
if (PATH_MAX > len) {
|
||||
strcpy (pathbuf, path);
|
||||
strcat (pathbuf, "/.gnucash/data/");
|
||||
strcat (pathbuf, filefrag);
|
||||
return (strdup (pathbuf));
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, we still didn't find the file */
|
||||
/* Lets try creating a new file in the cwd */
|
||||
path = getcwd (pathbuf, PATH_MAX);
|
||||
if (path) {
|
||||
len = strlen (path) + namelen;
|
||||
if (PATH_MAX > len) {
|
||||
strcat (path, "/");
|
||||
strcat (path, filefrag);
|
||||
return (strdup (path));
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ==================== END OF FILE ================== */
|
||||
@@ -1,205 +0,0 @@
|
||||
/********************************************************************\
|
||||
* 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:
|
||||
* Session.h
|
||||
*
|
||||
* FUNCTION:
|
||||
* Provide wrappers for initiating/concluding a file-editing session.
|
||||
* 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 ssytem 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
|
||||
*/
|
||||
|
||||
#ifndef __XACC_SESSION_H__
|
||||
#define __XACC_SESSION_H__
|
||||
|
||||
#include "Group.h"
|
||||
#include "FileIO.h"
|
||||
|
||||
/** STRUCTS *********************************************************/
|
||||
typedef struct _session Session;
|
||||
|
||||
/** PROTOTYPES ******************************************************/
|
||||
/*
|
||||
* The xaccMallocSession() routine simply mallocs memory for a session object.
|
||||
* The xaccInitSession() routine initializes memory for a session object.
|
||||
* The xaccSessionDestroy() routine frees the associated memory.
|
||||
* Note that this routine does *not* free the account group!
|
||||
*/
|
||||
Session * xaccMallocSession (void);
|
||||
void xaccInitSession (Session *);
|
||||
void xaccSessionDestroy (Session *);
|
||||
|
||||
/*
|
||||
* The xaccSessionBegin() method begins a new session. It takes as an argument
|
||||
* the sessionid. The session id must be a string in the form of a URI/URL.
|
||||
* In the current implementation, only one type of URI is supported, and
|
||||
* that is the file URI: anything of the form
|
||||
* "file:/home/somewhere/somedir/file.xac"
|
||||
* The path part must be a valid path. The file-part must be a valid
|
||||
* xacc/gnucash-format file. Paths may be relativ 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.
|
||||
*
|
||||
* If the file exists, can be opened and read, then the AccountGroup
|
||||
* will be returned. Otherwise, NULL is returned, and the reason for
|
||||
* the NULL can be gotten with xaccSessionGetError(). Note that NULL
|
||||
* does not always indicate an error ... e.g. if a new file is created,
|
||||
* NULL is returned, and xaccSessionGetError returns 0. That's OK.
|
||||
*
|
||||
* If the file is succesfully opened and read, then a lock will have been
|
||||
* obtained, and all other access to the file will be denied. This feature
|
||||
* is intended to be a brute-force global lock to avoid multiple writers.
|
||||
*
|
||||
* The xaccSessionBeginFile() routine is identical to the xaccSessionBegin()
|
||||
* routine, except that the argument is a filename (i.e. the five
|
||||
* letters "file:" should not be prepended) and there is an additional
|
||||
* function argument. This function is called if xaccSessionBeginFile
|
||||
* fails to obtain a lock for the file. If it returns true, the file
|
||||
* is loaded anyway. If it returns false, or the handler is NULL, a
|
||||
* failed lock attempt will abort the load. The lock fail handler is
|
||||
* passed the filename of the data file being loaded.
|
||||
*
|
||||
* The xaccSessionGetFilePath() 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 xaccSessionGetError() routine can be used to obtain the reason for
|
||||
* any failure. Standard errno values are used. 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.
|
||||
*
|
||||
* Some important error values:
|
||||
* EINVAL -- bad argument
|
||||
* ETXTBSY -- session id is in use; its locked by someone else.
|
||||
* ENOSYS -- unsupported URI type.
|
||||
* ERANGE -- file path too long
|
||||
* ENOLCK -- session not open when SessionSave() was called.
|
||||
*
|
||||
*
|
||||
* The xaccSessionGetGroup() method will return the current top-level
|
||||
* account group.
|
||||
*
|
||||
* The xaccSessionSetGroup() method will set the topgroup to a new value.
|
||||
*
|
||||
* The xaccSessionSave() method will commit all changes that have been
|
||||
* made to the top-level account group. In the current
|
||||
* implementation, this is nothing more than a write to the file of
|
||||
* the current AccountGroup of the session.
|
||||
*
|
||||
* The xaccSessionEnd() 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* EXAMPLE USAGE:
|
||||
* To read, modify and save an existing file:
|
||||
* ------------------------------------------
|
||||
* Session *sess = xaccMallocSession ();
|
||||
* xaccSessionBegin (sess, "file:/some/file.xac");
|
||||
* AccountGroup *grp = xaccSessionGetGroup (sess);
|
||||
* if (grp) {
|
||||
* ... edit the session ...
|
||||
* xaccSessionSave (sess);
|
||||
* } else {
|
||||
* int err = xaccSessionGetError (sess);
|
||||
* printf ("%s\n", strerror (err));
|
||||
* }
|
||||
* xaccSessionEnd (sess);
|
||||
* xaccSessionDestroy (sess);
|
||||
*
|
||||
* To save an existing session to a file:
|
||||
* --------------------------------------
|
||||
* AccountGroup * grp = ...
|
||||
* ... edit accounts, transactions, etc ...
|
||||
* Session *sess = xaccMallocSession ();
|
||||
* xaccSessionBegin (sess, "file:/some/file.xac");
|
||||
* xaccSessionSetGroup (sess, grp);
|
||||
* int err = xaccSessionGetError (sess);
|
||||
* if (0 == err) {
|
||||
* xaccSessionSave (sess);
|
||||
* } else {
|
||||
* printf ("%s\n", strerror (err));
|
||||
* }
|
||||
* xaccSessionEnd (sess);
|
||||
* xaccSessionDestroy (sess);
|
||||
* */
|
||||
|
||||
typedef gboolean (*SessionLockFailHandler)(const char *file);
|
||||
|
||||
/* These do not load anything. */
|
||||
gboolean xaccSessionBegin(Session *, const char * sessionid);
|
||||
gboolean xaccSessionBeginFile(Session *, const char * filename,
|
||||
SessionLockFailHandler handler);
|
||||
|
||||
/* Loads the account group indicated by SessionBegin. */
|
||||
gboolean xaccSessionLoad(Session *);
|
||||
|
||||
int xaccSessionGetError (Session *);
|
||||
|
||||
/* 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 Session errors... This just returns the last
|
||||
FileIO error, but it doesn't clear it. */
|
||||
GNCFileIOError xaccSessionGetFileError (Session *);
|
||||
|
||||
AccountGroup * xaccSessionGetGroup (Session *);
|
||||
void xaccSessionSetGroup (Session *, AccountGroup *topgroup);
|
||||
char * xaccSessionGetFilePath (Session *);
|
||||
|
||||
/* FIXME: This isn't as thorough as we might want it to be... */
|
||||
gboolean xaccSessionSaveMayClobberData(Session *s);
|
||||
|
||||
void xaccSessionSave (Session *);
|
||||
void xaccSessionEnd (Session *);
|
||||
|
||||
char * xaccResolveFilePath (const char * filefrag);
|
||||
|
||||
#endif /* __XACC_SESSION_H__ */
|
||||
/* ==================== END OF FILE ================== */
|
||||
+3
-3
@@ -25,8 +25,8 @@
|
||||
|
||||
#define MAX_HISTORY 4
|
||||
|
||||
void gnc_history_add_file(char *);
|
||||
void gnc_history_update_menu(void);
|
||||
const char * gnc_history_get_last(void);
|
||||
void gnc_history_add_file (const char *filename);
|
||||
void gnc_history_update_menu (void);
|
||||
const char * gnc_history_get_last (void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -99,7 +99,7 @@ __gnc_history_file_cb(GtkWidget *w, char *data)
|
||||
}
|
||||
|
||||
void
|
||||
gnc_history_add_file(char *newfile)
|
||||
gnc_history_add_file(const char *newfile)
|
||||
{
|
||||
int i, max_files;
|
||||
gboolean used_default, matched = FALSE;
|
||||
|
||||
@@ -457,7 +457,7 @@ static void
|
||||
gnc_refresh_main_window_title()
|
||||
{
|
||||
GtkWidget *main_window;
|
||||
Session *session;
|
||||
GNCBook *book;
|
||||
gchar *filename;
|
||||
gchar *title;
|
||||
|
||||
@@ -465,12 +465,9 @@ gnc_refresh_main_window_title()
|
||||
if (main_window == NULL)
|
||||
return;
|
||||
|
||||
session = gncGetCurrentSession();
|
||||
book = gncGetCurrentBook ();
|
||||
|
||||
if (session == NULL)
|
||||
filename = _("Untitled");
|
||||
else
|
||||
filename = xaccSessionGetFilePath(session);
|
||||
filename = gnc_book_get_file_path (book);
|
||||
|
||||
if ((filename == NULL) || (*filename == '\0'))
|
||||
filename = _("Untitled");
|
||||
|
||||
Reference in New Issue
Block a user