[Gnucash-changes] Remove the file-path utilities from qofsession.c and move them into a

Linas Vepstas linas at cvs.gnucash.org
Sun Jun 13 16:01:32 EDT 2004


Log Message:
-----------
Remove the file-path utilities from qofsession.c and move them into
a temp file 'gnc-filepath-utils.c' which is used by both the 
file backend and by the app-file GUI routines.  Some of the file-resolution
logic was oved to the file backend.

Modified Files:
--------------
    gnucash/src/app-file:
        gnc-file.c
    gnucash/src/backend/file:
        gnc-backend-file.c
        gnc-backend-file.h
    gnucash/src/engine:
        Makefile.am
        gw-engine-spec.scm
        qofbackend-p.h
        qofbackend.c
        qofsession-p.h
        qofsession.c
        qofsession.h
    gnucash/src/engine/test:
        test-resolve-file-path.c

Revision Data
-------------
Index: gnc-file.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/app-file/gnc-file.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -Lsrc/app-file/gnc-file.c -Lsrc/app-file/gnc-file.c -u -r1.34 -r1.35
--- src/app-file/gnc-file.c
+++ src/app-file/gnc-file.c
@@ -36,6 +36,7 @@
 #include "gnc-file-dialog.h"
 #include "gnc-file-history.h"
 #include "gnc-file-p.h"
+#include "gnc-filepath-utils.h"
 #include "gnc-gui-query.h"
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
@@ -43,6 +44,7 @@
 #include "qofbook.h"
 #include "qofsession.h"
 #include "messages.h"
+#include "TransLog.h"
 
 /** GLOBALS *********************************************************/
 /* This static indicates the debugging module that this .o belongs to.  */
@@ -287,7 +289,9 @@
               SCM_BOOL_F));
 
   gnc_close_gui_component_by_session (session);
+  xaccLogDisable();
   qof_session_destroy (session);
+  xaccLogEnable();
 
   /* start a new book */
   qof_session_get_current_session ();
@@ -375,7 +379,9 @@
               gw_wcp_assimilate_ptr (current_session,
                                      scm_c_eval_string("<gnc:Session*>")) :
               SCM_BOOL_F));
+  xaccLogDisable();
   qof_session_destroy (current_session);
+  xaccLogEnable();
 
   /* load the accounts from the users datafile */
   /* but first, check to make sure we've got a session going. */
@@ -462,6 +468,11 @@
   {
     AccountGroup *new_group;
 
+    char * logpath = xaccResolveFilePath(newfile);
+    PINFO ("logpath=%s", logpath ? logpath : "(null)");
+    xaccLogSetBaseName (logpath);
+    xaccLogDisable();
+
     if (file_percentage_func) {
       file_percentage_func(_("Reading file..."), 0.0);
       qof_session_load (new_session, file_percentage_func);
@@ -469,6 +480,7 @@
     } else {
       qof_session_load (new_session, NULL);
     }
+    xaccLogEnable();
 
     /* check for i/o error, put up appropriate error dialog */
     io_err = qof_session_get_error (new_session);
@@ -490,7 +502,9 @@
   /* going down -- abandon ship */
   if (uh_oh) 
   {
+    xaccLogDisable();
     qof_session_destroy (new_session);
+    xaccLogEnable();
 
     /* well, no matter what, I think it's a good idea to have a
      * topgroup around.  For example, early in the gnucash startup
@@ -645,7 +659,9 @@
     ok = qof_session_export (new_session, current_session, NULL);
   }
   gnc_unset_busy_cursor (NULL);
+  xaccLogDisable();
   qof_session_destroy (new_session);
+  xaccLogEnable();
   gnc_engine_resume_events();
 
   if (!ok)
@@ -801,7 +817,9 @@
   if (ERR_BACKEND_NO_ERR != io_err) 
   {
     show_session_error (io_err, newfile);
+    xaccLogDisable();
     qof_session_destroy (new_session);
+    xaccLogEnable();
     g_free (newfile);
     return;
   }
@@ -809,7 +827,9 @@
   /* if we got to here, then we've successfully gotten a new session */
   /* close up the old file session (if any) */
   qof_session_swap_data (session, new_session);
+  xaccLogDisable();
   qof_session_destroy (session);
+  xaccLogEnable();
   session = NULL;
 
   /* XXX At this point, we should really mark the data in the new session
@@ -859,7 +879,9 @@
               gw_wcp_assimilate_ptr (session, scm_c_eval_string("<gnc:Session*>")) :
               SCM_BOOL_F));
   
+  xaccLogDisable();
   qof_session_destroy (session);
+  xaccLogEnable();
 
   qof_session_get_current_session ();
 
Index: gnc-backend-file.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/file/gnc-backend-file.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lsrc/backend/file/gnc-backend-file.h -Lsrc/backend/file/gnc-backend-file.h -u -r1.1 -r1.2
--- src/backend/file/gnc-backend-file.h
+++ src/backend/file/gnc-backend-file.h
@@ -57,4 +57,6 @@
     GNC_BOOK_XML2_FILE,
 } QofBookFileType;
 
+QofBackend * libgncmod_backend_file_LTX_gnc_backend_new(void);
+
 #endif /* GNC_BACKEND_FILE_H_ */
Index: gnc-backend-file.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/backend/file/gnc-backend-file.c,v
retrieving revision 1.39
retrieving revision 1.40
diff -Lsrc/backend/file/gnc-backend-file.c -Lsrc/backend/file/gnc-backend-file.c -u -r1.39 -r1.40
--- src/backend/file/gnc-backend-file.c
+++ src/backend/file/gnc-backend-file.c
@@ -46,6 +46,7 @@
 #include "gnc-date.h"
 #include "gnc-trace.h"
 #include "gnc-engine-util.h"
+#include "gnc-filepath-utils.h"
 
 #include "io-gncxml.h"
 #include "io-gncbin.h"
@@ -63,18 +64,6 @@
 static int file_retention_days = 0;
 static gboolean file_compression = FALSE;
 
-static void gnc_file_be_load_from_file(QofBackend *, QofBook *);
-
-static gboolean gnc_file_be_get_file_lock (FileBackend *be);
-static gboolean gnc_file_be_write_to_file(FileBackend *be, QofBook *,
-                                          const gchar *filepath,
-                                          gboolean make_backup);
-static void gnc_file_be_write_accounts_to_file(QofBackend *be,
-                                               QofBook *book);
-static void gnc_file_be_remove_old_files(FileBackend *be);
-
-QofBackend * libgncmod_backend_file_LTX_gnc_backend_new(void);
-
 void
 gnc_file_be_set_retention_days (int days)
 {
@@ -89,6 +78,106 @@
 
 /* ================================================================= */
 
+static gboolean
+gnc_file_be_get_file_lock (FileBackend *be)
+{
+    struct stat statbuf;
+    char pathbuf[PATH_MAX];
+    char *path = NULL;
+    int rc;
+    QofBackendError be_err;
+
+    rc = stat (be->lockfile, &statbuf);
+    if (!rc)
+    {
+        /* oops .. file is locked by another user  .. */
+        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+        return FALSE;
+    }
+
+    be->lockfd = open (be->lockfile, O_RDWR | O_CREAT | O_EXCL , 0);
+    if (be->lockfd < 0)
+    {
+        /* oops .. we can't create the lockfile .. */
+        switch (errno) {
+        case EACCES:
+        case EROFS:
+        case ENOSPC:
+          be_err = ERR_BACKEND_READONLY;
+          break;
+        default:
+          be_err = ERR_BACKEND_LOCKED;
+          break;
+        }
+        qof_backend_set_error ((QofBackend*)be, be_err);
+        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, be->lockfile);
+    path = strrchr (pathbuf, '.');
+    sprintf (path, ".%lx.%d.LNK", gethostid(), getpid());
+
+    rc = link (be->lockfile, pathbuf);
+    if (rc)
+    {
+        /* If hard links aren't supported, just allow the lock. */
+        if (errno == EOPNOTSUPP || errno == EPERM)
+        {
+            be->linkfile = NULL;
+            return TRUE;
+        }
+
+        /* Otherwise, something else is wrong. */
+        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+        unlink (pathbuf);
+        close (be->lockfd);
+        unlink (be->lockfile);
+        return FALSE;
+    }
+
+    rc = stat (be->lockfile, &statbuf);
+    if (rc)
+    {
+        /* oops .. stat failed!  This can't happen! */
+        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+        unlink (pathbuf);
+        close (be->lockfd);
+        unlink (be->lockfile);
+        return FALSE;
+    }
+
+    if (statbuf.st_nlink != 2)
+    {
+        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
+        unlink (pathbuf);
+        close (be->lockfd);
+        unlink (be->lockfile);
+        return FALSE;
+    }
+
+    be->linkfile = g_strdup (pathbuf);
+
+    return TRUE;
+}
+
+/* ================================================================= */
+
 static void
 file_session_begin(QofBackend *be_start, QofSession *session, 
                    const char *book_id,
@@ -100,8 +189,14 @@
     ENTER (" ");
 
     /* Make sure the directory is there */
-    be->dirname = g_strdup (qof_session_get_file_path (session));
+    be->dirname = xaccResolveFilePath(book_id);
+    if (NULL == be->dirname)
+    {
+        qof_backend_set_error (be_start, ERR_FILEIO_FILE_NOT_FOUND);
+        return;
+    }
     be->fullpath = g_strdup (be->dirname);
+    be->be.fullpath = be->fullpath;
     p = strrchr (be->dirname, '/');
     if (p && p != be->dirname)
     {
@@ -180,281 +275,103 @@
     g_free(be);
 }
 
-static void
-file_sync_all(QofBackend* be, QofBook *book)
+/* ================================================================= */
+/* Write the financial data in a book to a file, returning FALSE on
+   error and setting the error_result to indicate what went wrong if
+   it's not NULL.  This function does not manage file locks in any
+   way.
+
+   If make_backup is true, write out a time-stamped copy of the file
+   into the same directory as the indicated file, with a filename of
+   "file.YYYYMMDDHHMMSS.xac" where YYYYMMDDHHMMSS is replaced with the
+   current year/month/day/hour/minute/second. */
+
+static gboolean
+copy_file(const char *orig, const char *bkup)
 {
-    FileBackend *fbe = (FileBackend *) be;
-    ENTER ("book=%p, primary=%p", book, fbe->primary_book);
+    static int buf_size = 1024;
+    char buf[buf_size];
+    int orig_fd;
+    int bkup_fd;
+    ssize_t count_write;
+    ssize_t count_read;
 
-    /* We make an important assumption here, that we might want to change
-     * in the future: when the user says 'save', we really save the one,
-     * the only, the current open book, and nothing else.  We do this
-     * because we assume that any other books that we are dealing with
-     * are 'read-only', non-editable, because they are closed books.
-     * If we ever want to have more than one book open read-write,
-     * this will have to change.
-     */
-    if (NULL == fbe->primary_book) fbe->primary_book = book;
-    if (book != fbe->primary_book) return;
+    orig_fd = open(orig, O_RDONLY);
+    if(orig_fd == -1)
+    {
+        return FALSE;
+    }
+    bkup_fd = creat(bkup, 0600);
+    if(bkup_fd == -1)
+    {
+        close(orig_fd);
+        return FALSE;
+    }
 
-    gnc_file_be_write_to_file (fbe, book, fbe->fullpath, TRUE);
-    gnc_file_be_remove_old_files (fbe);
-    LEAVE ("book=%p", book);
-}
+    do
+    {
+        count_read = read(orig_fd, buf, buf_size);
+        if(count_read == -1 && errno != EINTR)
+        {
+            close(orig_fd);
+            close(bkup_fd);
+            return FALSE;
+        }
+
+        if(count_read > 0)
+        {
+            count_write = write(bkup_fd, buf, count_read);
+            if(count_write == -1)
+            {
+                close(orig_fd);
+                close(bkup_fd);
+                return FALSE;
+            }
+        }
+    } while(count_read > 0);
 
+    close(orig_fd);
+    close(bkup_fd);
+    
+    return TRUE;
+}
+        
 /* ================================================================= */
-/* Routines to deal with the creation of multiple books.
- * The core design assumption here is that the book
- * begin-edit/commit-edit routines are used solely to write out
- * closed accounting periods to files.  They're not currently
- * designed to do anything other than this. (Although they could be).
- */
 
-static char *
-build_period_filepath (FileBackend *fbe, QofBook *book)
+static gboolean
+gnc_int_link_or_make_backup(FileBackend *be, const char *orig, const char *bkup)
 {
-    int len;
-    char *str, *p, *q;
-
-    len = strlen (fbe->fullpath) + GUID_ENCODING_LENGTH + 14;
-    str = g_new (char, len);
-    strcpy (str, fbe->fullpath);
+    int err_ret = link(orig, bkup);
+    if(err_ret != 0)
+    {
+        if(errno == EPERM || errno == EOPNOTSUPP)
+        {
+            err_ret = copy_file(orig, bkup);
+        }
 
-    /* XXX it would be nice for the user if we made the book 
-     * closing date and/or title part of the file-name. */
-    p = strrchr (str, '/');
-    p++;
-    p = stpcpy (p, "book-");
-    p = guid_to_string_buff (qof_book_get_guid(book), p);
-    p = stpcpy (p, "-");
-    q = strrchr (fbe->fullpath, '/');
-    q++;
-    p = stpcpy (p, q);
-    p = stpcpy (p, ".gml");
+        if(!err_ret)
+        {
+            qof_backend_set_error((QofBackend*)be, ERR_FILEIO_BACKUP_ERROR);
+            PWARN ("unable to make file backup from %s to %s: %s", 
+                    orig, bkup, strerror(errno) ? strerror(errno) : ""); 
+            return FALSE;
+        }
+    }
 
-    return str;
+    return TRUE;
 }
 
-static void
-file_begin_edit (QofBackend *be, QofInstance *inst)
-{
-    if (0) build_period_filepath(0, 0);
-#if BORKEN_FOR_NOW
-    FileBackend *fbe = (FileBackend *) be;
-    QofBook *book = gp;
-    const char * filepath;
+/* ================================================================= */
 
-    QofIdTypeConst typ = QOF_ENTITY(inst)->e_type;
-    if (strcmp (GNC_ID_PERIOD, typ)) return;
-    filepath = build_period_filepath(fbe, book);
-    PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
+static gboolean
+is_gzipped_file(const gchar *name)
+{
+    unsigned char buf[2];
+    int fd = open(name, O_RDONLY);
 
-    if (NULL == fbe->primary_book)
+    if(fd == 0)
     {
-        PERR ("You should have saved the data "
-              "at least once before closing the books!\n");
-    }
-    /* XXX To be anal about it, we should really be checking to see
-     * if there already is a file with this book GUID, and disallowing
-     * further progress.  This is because we are not allowed to 
-     * modify books that are closed (They should be treated as 
-     * 'read-only').
-     */
-#endif
-}
-
-static void
-file_rollback_edit (QofBackend *be, QofInstance *inst)
-{
-#if BORKEN_FOR_NOW
-    QofBook *book = gp;
-
-    if (strcmp (GNC_ID_PERIOD, typ)) return;
-    PINFO ("book=%p", book);
-#endif
-}
-
-static void
-file_commit_edit (QofBackend *be, QofInstance *inst)
-{
-#if BORKEN_FOR_NOW
-    FileBackend *fbe = (FileBackend *) be;
-    QofBook *book = gp;
-    const char * filepath;
-
-    if (strcmp (GNC_ID_PERIOD, typ)) return;
-    filepath = build_period_filepath(fbe, book);
-    PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
-    gnc_file_be_write_to_file(fbe, book, filepath, FALSE);
-
-    /* We want to force a save of the current book at this point,
-     * because if we don't, and the user forgets to do so, then
-     * there'll be the same transactions in the closed book,
-     * and also in the current book. */
-    gnc_file_be_write_to_file (fbe, fbe->primary_book, fbe->fullpath, TRUE);
-#endif
-}
-
-/* ================================================================= */
-
-QofBackend *
-libgncmod_backend_file_LTX_gnc_backend_new(void)
-{
-    FileBackend *fbe;
-    QofBackend *be;
-    
-    fbe = g_new0(FileBackend, 1);
-    be = (QofBackend*)fbe;
-    qof_backend_init(be);
-    
-    be->session_begin = file_session_begin;
-    be->session_end = file_session_end;
-    be->destroy_backend = file_destroy_backend;
-
-    be->load = gnc_file_be_load_from_file;
-
-    /* The file backend treats accounting periods transactionally. */
-    be->begin = file_begin_edit;
-    be->commit = file_commit_edit;
-    be->rollback = file_rollback_edit;
-
-    /* The file backend always loads all data ... */
-    be->compile_query = NULL;
-    be->free_query = NULL;
-    be->run_query = NULL;
-    be->price_lookup = NULL;
-
-    be->counter = NULL;
-
-    /* The file backend will never be multi-user... */
-    be->events_pending = NULL;
-    be->process_events = NULL;
-
-    be->sync = file_sync_all;
-    be->export = gnc_file_be_write_accounts_to_file;
-
-    fbe->dirname = NULL;
-    fbe->fullpath = NULL;
-    fbe->lockfile = NULL;
-    fbe->linkfile = NULL;
-    fbe->lockfd = -1;
-
-    fbe->primary_book = NULL;
-
-    return be;
-}
-
-/* ================================================================= */
-
-static gboolean
-gnc_file_be_get_file_lock (FileBackend *be)
-{
-    struct stat statbuf;
-    char pathbuf[PATH_MAX];
-    char *path = NULL;
-    int rc;
-    QofBackendError be_err;
-
-    rc = stat (be->lockfile, &statbuf);
-    if (!rc)
-    {
-        /* oops .. file is locked by another user  .. */
-        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
-        return FALSE;
-    }
-
-    be->lockfd = open (be->lockfile, O_RDWR | O_CREAT | O_EXCL , 0);
-    if (be->lockfd < 0)
-    {
-        /* oops .. we can't create the lockfile .. */
-        switch (errno) {
-        case EACCES:
-        case EROFS:
-        case ENOSPC:
-          be_err = ERR_BACKEND_READONLY;
-          break;
-        default:
-          be_err = ERR_BACKEND_LOCKED;
-          break;
-        }
-        qof_backend_set_error ((QofBackend*)be, be_err);
-        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, be->lockfile);
-    path = strrchr (pathbuf, '.');
-    sprintf (path, ".%lx.%d.LNK", gethostid(), getpid());
-
-    rc = link (be->lockfile, pathbuf);
-    if (rc)
-    {
-        /* If hard links aren't supported, just allow the lock. */
-        if (errno == EOPNOTSUPP || errno == EPERM)
-        {
-            be->linkfile = NULL;
-            return TRUE;
-        }
-
-        /* Otherwise, something else is wrong. */
-        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
-        unlink (pathbuf);
-        close (be->lockfd);
-        unlink (be->lockfile);
-        return FALSE;
-    }
-
-    rc = stat (be->lockfile, &statbuf);
-    if (rc)
-    {
-        /* oops .. stat failed!  This can't happen! */
-        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
-        unlink (pathbuf);
-        close (be->lockfd);
-        unlink (be->lockfile);
-        return FALSE;
-    }
-
-    if (statbuf.st_nlink != 2)
-    {
-        qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_LOCKED);
-        unlink (pathbuf);
-        close (be->lockfd);
-        unlink (be->lockfile);
-        return FALSE;
-    }
-
-    be->linkfile = g_strdup (pathbuf);
-
-    return TRUE;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static gboolean
-is_gzipped_file(const gchar *name)
-{
-    unsigned char buf[2];
-    int fd = open(name, O_RDONLY);
-
-    if(fd == 0)
-    {
-        return FALSE;
+        return FALSE;
     }
 
     if(read(fd, buf, 2) != 2)
@@ -485,183 +402,179 @@
 }
 
 
-/* Load financial data from a file into the book, automtically
-   detecting the format of the file, if possible.  Return FALSE on
-   error, and set the error parameter to indicate what went wrong if
-   it's not NULL.  This function does not manage file locks in any
-   way. */
-
-static void
-gnc_file_be_load_from_file (QofBackend *bend, QofBook *book)
+static gboolean
+gnc_file_be_backup_file(FileBackend *be)
 {
-    QofBackendError error = ERR_BACKEND_NO_ERR;
-    gboolean rc;
-    FileBackend *be = (FileBackend *) bend;
+    gboolean bkup_ret;
+    char *timestamp;
+    char *backup;
+    const char *datafile;
+    struct stat statbuf;
+    int rc;
 
-    be->primary_book = book;
+    datafile = be->fullpath;
+    
+    rc = stat (datafile, &statbuf);
+    if (rc)
+      return (errno == ENOENT);
 
-    switch (gnc_file_be_determine_file_type(be->fullpath))
+    if(gnc_file_be_determine_file_type(datafile) == GNC_BOOK_BIN_FILE)
     {
-    case GNC_BOOK_XML2_FILE:
-        rc = qof_session_load_from_xml_file_v2 (be, book);
-        if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
-        break;
+        /* make a more permament safer backup */
+        const char *back = "-binfmt.bkup";
+        char *bin_bkup = g_new(char, strlen(datafile) + strlen(back) + 1);
+        strcpy(bin_bkup, datafile);
+        strcat(bin_bkup, back);
+        bkup_ret = gnc_int_link_or_make_backup(be, datafile, bin_bkup);
+        g_free(bin_bkup);
+        if(!bkup_ret)
+        {
+            return FALSE;
+        }
+    }
 
-    case GNC_BOOK_XML1_FILE:
-        rc = qof_session_load_from_xml_file (book, be->fullpath);
-        if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
-        break;
+    timestamp = xaccDateUtilGetStampNow ();
+    backup = g_new (char, strlen (datafile) + strlen (timestamp) + 6);
+    strcpy (backup, datafile);
+    strcat (backup, ".");
+    strcat (backup, timestamp);
+    strcat (backup, ".xac");
+    g_free (timestamp);
 
-    case GNC_BOOK_BIN_FILE:
-        /* presume it's an old-style binary file */
-        qof_session_load_from_binfile(book, be->fullpath);
-        error = gnc_get_binfile_io_error();
-        break;
+    bkup_ret = gnc_int_link_or_make_backup(be, datafile, backup);
+    g_free(backup);
 
-    default:
-        PWARN("File not any known type");
-        error = ERR_FILEIO_UNKNOWN_FILE_TYPE;
-        break;
-    }
-
-    if(error != ERR_BACKEND_NO_ERR) 
-    {
-        qof_backend_set_error(bend, error);
-    }
-
-    /* We just got done loading, it can't possibly be dirty !! */
-    qof_book_mark_saved (book);
+    return bkup_ret;
 }
 
-/* ---------------------------------------------------------------------- */
-/* Write the financial data in a book to a file, returning FALSE on
-   error and setting the error_result to indicate what went wrong if
-   it's not NULL.  This function does not manage file locks in any
-   way.
-
-   If make_backup is true, write out a time-stamped copy of the file
-   into the same directory as the indicated file, with a filename of
-   "file.YYYYMMDDHHMMSS.xac" where YYYYMMDDHHMMSS is replaced with the
-   current year/month/day/hour/minute/second. */
-
+/* ================================================================= */
+ 
 static gboolean
-copy_file(const char *orig, const char *bkup)
+gnc_file_be_write_to_file(FileBackend *fbe, 
+                          QofBook *book, 
+                          const gchar *datafile,
+                          gboolean make_backup)
 {
-    static int buf_size = 1024;
-    char buf[buf_size];
-    int orig_fd;
-    int bkup_fd;
-    ssize_t count_write;
-    ssize_t count_read;
+    QofBackend *be = &fbe->be;
+    char *tmp_name;
+    struct stat statbuf;
+    int rc;
+    QofBackendError be_err;
 
-    orig_fd = open(orig, O_RDONLY);
-    if(orig_fd == -1)
-    {
-        return FALSE;
-    }
-    bkup_fd = creat(bkup, 0600);
-    if(bkup_fd == -1)
+    ENTER (" book=%p file=%s", book, datafile);
+
+    /* If the book is 'clean', recently saved, then don't save again. */
+    /* XXX this is currently broken due to faulty 'Save As' logic. */
+    /* if (FALSE == qof_book_not_saved (book)) return FALSE; */
+
+    tmp_name = g_new(char, strlen(datafile) + 12);
+    strcpy(tmp_name, datafile);
+    strcat(tmp_name, ".tmp-XXXXXX");
+
+    if(!mktemp(tmp_name))
     {
-        close(orig_fd);
+        qof_backend_set_error(be, ERR_BACKEND_MISC);
         return FALSE;
     }
-
-    do
+  
+    if(make_backup)
     {
-        count_read = read(orig_fd, buf, buf_size);
-        if(count_read == -1 && errno != EINTR)
+        if(!gnc_file_be_backup_file(fbe))
         {
-            close(orig_fd);
-            close(bkup_fd);
             return FALSE;
         }
-
-        if(count_read > 0)
+    }
+  
+    if(gnc_book_write_to_xml_file_v2(book, tmp_name, file_compression)) 
+    {
+        /* Record the file's permissions before unlinking it */
+        rc = stat(datafile, &statbuf);
+        if(rc == 0)
         {
-            count_write = write(bkup_fd, buf, count_read);
-            if(count_write == -1)
+            /* Use the permissions from the original data file */
+            if(chmod(tmp_name, statbuf.st_mode) != 0)
             {
-                close(orig_fd);
-                close(bkup_fd);
+                qof_backend_set_error(be, ERR_BACKEND_PERM);
+                PWARN("unable to chmod filename %s: %s",
+                        datafile ? datafile : "(null)", 
+                        strerror(errno) ? strerror(errno) : ""); 
+#if VFAT_DOESNT_SUCK  /* chmod always fails on vfat fs */
+                g_free(tmp_name);
+                return FALSE;
+#endif
+            }
+            if(chown(tmp_name, statbuf.st_uid, statbuf.st_gid) != 0)
+            {
+                qof_backend_set_error(be, ERR_BACKEND_PERM);
+                PWARN("unable to chown filename %s: %s",
+                        datafile ? datafile : "(null)", 
+                        strerror(errno) ? strerror(errno) : ""); 
+#if VFAT_DOESNT_SUCK /* chown always fails on vfat fs */
+                g_free(tmp_name);
                 return FALSE;
+#endif
             }
         }
-    } while(count_read > 0);
-
-    close(orig_fd);
-    close(bkup_fd);
-    
-    return TRUE;
-}
-        
-static gboolean
-gnc_int_link_or_make_backup(FileBackend *be, const char *orig, const char *bkup)
-{
-    int err_ret = link(orig, bkup);
-    if(err_ret != 0)
-    {
-        if(errno == EPERM || errno == EOPNOTSUPP)
+        if(unlink(datafile) != 0 && errno != ENOENT)
         {
-            err_ret = copy_file(orig, bkup);
+            qof_backend_set_error(be, ERR_FILEIO_BACKUP_ERROR);
+            PWARN("unable to unlink filename %s: %s",
+                  datafile ? datafile : "(null)", 
+                  strerror(errno) ? strerror(errno) : ""); 
+            g_free(tmp_name);
+            return FALSE;
         }
-
-        if(!err_ret)
+        if(!gnc_int_link_or_make_backup(fbe, tmp_name, datafile))
         {
-            qof_backend_set_error((QofBackend*)be, ERR_FILEIO_BACKUP_ERROR);
-            PWARN ("unable to make file backup from %s to %s: %s", 
-                    orig, bkup, strerror(errno) ? strerror(errno) : ""); 
+            qof_backend_set_error(be, ERR_FILEIO_BACKUP_ERROR);
+            g_free(tmp_name);
             return FALSE;
         }
-    }
-
-    return TRUE;
-}
-
-static gboolean
-gnc_file_be_backup_file(FileBackend *be)
-{
-    gboolean bkup_ret;
-    char *timestamp;
-    char *backup;
-    const char *datafile;
-    struct stat statbuf;
-    int rc;
-
-    datafile = be->fullpath;
-    
-    rc = stat (datafile, &statbuf);
-    if (rc)
-      return (errno == ENOENT);
+        if(unlink(tmp_name) != 0)
+        {
+            qof_backend_set_error(be, ERR_BACKEND_PERM);
+            PWARN("unable to unlink temp filename %s: %s", 
+                   tmp_name ? tmp_name : "(null)", 
+                   strerror(errno) ? strerror(errno) : ""); 
+            g_free(tmp_name);
+            return FALSE;
+        }
+        g_free(tmp_name);
 
-    if(gnc_file_be_determine_file_type(datafile) == GNC_BOOK_BIN_FILE)
+        /* Since we successfully saved the book, 
+         * we should mark it clean. */
+        qof_book_mark_saved (book);
+        LEAVE (" sucessful save of book=%p to file=%s", book, datafile);
+        return TRUE;
+    }
+    else
     {
-        /* make a more permament safer backup */
-        const char *back = "-binfmt.bkup";
-        char *bin_bkup = g_new(char, strlen(datafile) + strlen(back) + 1);
-        strcpy(bin_bkup, datafile);
-        strcat(bin_bkup, back);
-        bkup_ret = gnc_int_link_or_make_backup(be, datafile, bin_bkup);
-        g_free(bin_bkup);
-        if(!bkup_ret)
+        if(unlink(tmp_name) != 0)
         {
-            return FALSE;
+            switch (errno) {
+            case ENOENT:     /* tmp_name doesn't exist?  Assume "RO" error */
+            case EACCES:
+            case EPERM:
+            case EROFS:
+              be_err = ERR_BACKEND_READONLY;
+              break;
+            default:
+              be_err = ERR_BACKEND_MISC;
+            }
+            qof_backend_set_error(be, be_err);
+            PWARN("unable to unlink temp_filename %s: %s", 
+                   tmp_name ? tmp_name : "(null)", 
+                   strerror(errno) ? strerror(errno) : ""); 
+            /* already in an error just flow on through */
         }
+        g_free(tmp_name);
+        return FALSE;
     }
-
-    timestamp = xaccDateUtilGetStampNow ();
-    backup = g_new (char, strlen (datafile) + strlen (timestamp) + 6);
-    strcpy (backup, datafile);
-    strcat (backup, ".");
-    strcat (backup, timestamp);
-    strcat (backup, ".xac");
-    g_free (timestamp);
-
-    bkup_ret = gnc_int_link_or_make_backup(be, datafile, backup);
-    g_free(backup);
-
-    return bkup_ret;
+    return TRUE;
 }
 
+/* ================================================================= */
+
 static int
 gnc_file_be_select_files (const struct dirent *d)
 {
@@ -766,132 +679,189 @@
     closedir (dir);
 }
 
-/* ---------------------------------------------------------------------- */
-    
-static gboolean
-gnc_file_be_write_to_file(FileBackend *fbe, 
-                          QofBook *book, 
-                          const gchar *datafile,
-                          gboolean make_backup)
+static void
+file_sync_all(QofBackend* be, QofBook *book)
 {
-    QofBackend *be = &fbe->be;
-    char *tmp_name;
-    struct stat statbuf;
-    int rc;
-    QofBackendError be_err;
-
-    ENTER (" book=%p file=%s", book, datafile);
+    FileBackend *fbe = (FileBackend *) be;
+    ENTER ("book=%p, primary=%p", book, fbe->primary_book);
 
-    /* If the book is 'clean', recently saved, then don't save again. */
-    /* XXX this is currently broken due to faulty 'Save As' logic. */
-    /* if (FALSE == qof_book_not_saved (book)) return FALSE; */
+    /* We make an important assumption here, that we might want to change
+     * in the future: when the user says 'save', we really save the one,
+     * the only, the current open book, and nothing else.  We do this
+     * because we assume that any other books that we are dealing with
+     * are 'read-only', non-editable, because they are closed books.
+     * If we ever want to have more than one book open read-write,
+     * this will have to change.
+     */
+    if (NULL == fbe->primary_book) fbe->primary_book = book;
+    if (book != fbe->primary_book) return;
 
-    tmp_name = g_new(char, strlen(datafile) + 12);
-    strcpy(tmp_name, datafile);
-    strcat(tmp_name, ".tmp-XXXXXX");
+    gnc_file_be_write_to_file (fbe, book, fbe->fullpath, TRUE);
+    gnc_file_be_remove_old_files (fbe);
+    LEAVE ("book=%p", book);
+}
 
-    if(!mktemp(tmp_name))
-    {
-        qof_backend_set_error(be, ERR_BACKEND_MISC);
-        return FALSE;
-    }
-  
-    if(make_backup)
+/* ================================================================= */
+/* Routines to deal with the creation of multiple books.
+ * The core design assumption here is that the book
+ * begin-edit/commit-edit routines are used solely to write out
+ * closed accounting periods to files.  They're not currently
+ * designed to do anything other than this. (Although they could be).
+ */
+
+static char *
+build_period_filepath (FileBackend *fbe, QofBook *book)
+{
+    int len;
+    char *str, *p, *q;
+
+    len = strlen (fbe->fullpath) + GUID_ENCODING_LENGTH + 14;
+    str = g_new (char, len);
+    strcpy (str, fbe->fullpath);
+
+    /* XXX it would be nice for the user if we made the book 
+     * closing date and/or title part of the file-name. */
+    p = strrchr (str, '/');
+    p++;
+    p = stpcpy (p, "book-");
+    p = guid_to_string_buff (qof_book_get_guid(book), p);
+    p = stpcpy (p, "-");
+    q = strrchr (fbe->fullpath, '/');
+    q++;
+    p = stpcpy (p, q);
+    p = stpcpy (p, ".gml");
+
+    return str;
+}
+
+static void
+file_begin_edit (QofBackend *be, QofInstance *inst)
+{
+    if (0) build_period_filepath(0, 0);
+#if BORKEN_FOR_NOW
+    FileBackend *fbe = (FileBackend *) be;
+    QofBook *book = gp;
+    const char * filepath;
+
+    QofIdTypeConst typ = QOF_ENTITY(inst)->e_type;
+    if (strcmp (GNC_ID_PERIOD, typ)) return;
+    filepath = build_period_filepath(fbe, book);
+    PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
+
+    if (NULL == fbe->primary_book)
     {
-        if(!gnc_file_be_backup_file(fbe))
-        {
-            return FALSE;
-        }
+        PERR ("You should have saved the data "
+              "at least once before closing the books!\n");
     }
-  
-    if(gnc_book_write_to_xml_file_v2(book, tmp_name, file_compression)) 
-    {
-        /* Record the file's permissions before unlinking it */
-        rc = stat(datafile, &statbuf);
-        if(rc == 0)
-        {
-            /* Use the permissions from the original data file */
-            if(chmod(tmp_name, statbuf.st_mode) != 0)
-            {
-                qof_backend_set_error(be, ERR_BACKEND_PERM);
-                PWARN("unable to chmod filename %s: %s",
-                        datafile ? datafile : "(null)", 
-                        strerror(errno) ? strerror(errno) : ""); 
-#if VFAT_DOESNT_SUCK  /* chmod always fails on vfat fs */
-                g_free(tmp_name);
-                return FALSE;
+    /* XXX To be anal about it, we should really be checking to see
+     * if there already is a file with this book GUID, and disallowing
+     * further progress.  This is because we are not allowed to 
+     * modify books that are closed (They should be treated as 
+     * 'read-only').
+     */
 #endif
-            }
-            if(chown(tmp_name, statbuf.st_uid, statbuf.st_gid) != 0)
-            {
-                qof_backend_set_error(be, ERR_BACKEND_PERM);
-                PWARN("unable to chown filename %s: %s",
-                        datafile ? datafile : "(null)", 
-                        strerror(errno) ? strerror(errno) : ""); 
-#if VFAT_DOESNT_SUCK /* chown always fails on vfat fs */
-                g_free(tmp_name);
-                return FALSE;
+}
+
+static void
+file_rollback_edit (QofBackend *be, QofInstance *inst)
+{
+#if BORKEN_FOR_NOW
+    QofBook *book = gp;
+
+    if (strcmp (GNC_ID_PERIOD, typ)) return;
+    PINFO ("book=%p", book);
 #endif
-            }
-        }
-        if(unlink(datafile) != 0 && errno != ENOENT)
-        {
-            qof_backend_set_error(be, ERR_FILEIO_BACKUP_ERROR);
-            PWARN("unable to unlink filename %s: %s",
-                  datafile ? datafile : "(null)", 
-                  strerror(errno) ? strerror(errno) : ""); 
-            g_free(tmp_name);
-            return FALSE;
-        }
-        if(!gnc_int_link_or_make_backup(fbe, tmp_name, datafile))
-        {
-            qof_backend_set_error(be, ERR_FILEIO_BACKUP_ERROR);
-            g_free(tmp_name);
-            return FALSE;
-        }
-        if(unlink(tmp_name) != 0)
-        {
-            qof_backend_set_error(be, ERR_BACKEND_PERM);
-            PWARN("unable to unlink temp filename %s: %s", 
-                   tmp_name ? tmp_name : "(null)", 
-                   strerror(errno) ? strerror(errno) : ""); 
-            g_free(tmp_name);
-            return FALSE;
-        }
-        g_free(tmp_name);
+}
 
-        /* Since we successfully saved the book, 
-         * we should mark it clean. */
-        qof_book_mark_saved (book);
-        LEAVE (" sucessful save of book=%p to file=%s", book, datafile);
-        return TRUE;
+static void
+file_commit_edit (QofBackend *be, QofInstance *inst)
+{
+#if BORKEN_FOR_NOW
+    FileBackend *fbe = (FileBackend *) be;
+    QofBook *book = gp;
+    const char * filepath;
+
+    if (strcmp (GNC_ID_PERIOD, typ)) return;
+    filepath = build_period_filepath(fbe, book);
+    PINFO (" ====================== book=%p filepath=%s\n", book, filepath);
+    gnc_file_be_write_to_file(fbe, book, filepath, FALSE);
+
+    /* We want to force a save of the current book at this point,
+     * because if we don't, and the user forgets to do so, then
+     * there'll be the same transactions in the closed book,
+     * and also in the current book. */
+    gnc_file_be_write_to_file (fbe, fbe->primary_book, fbe->fullpath, TRUE);
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+
+/* Load financial data from a file into the book, automtically
+   detecting the format of the file, if possible.  Return FALSE on
+   error, and set the error parameter to indicate what went wrong if
+   it's not NULL.  This function does not manage file locks in any
+   way. */
+
+static void
+gnc_file_be_load_from_file (QofBackend *bend, QofBook *book)
+{
+    QofBackendError error = ERR_BACKEND_NO_ERR;
+    gboolean rc;
+    FileBackend *be = (FileBackend *) bend;
+
+    be->primary_book = book;
+
+    switch (gnc_file_be_determine_file_type(be->fullpath))
+    {
+    case GNC_BOOK_XML2_FILE:
+        rc = qof_session_load_from_xml_file_v2 (be, book);
+        if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
+        break;
+
+    case GNC_BOOK_XML1_FILE:
+        rc = qof_session_load_from_xml_file (book, be->fullpath);
+        if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
+        break;
+
+    case GNC_BOOK_BIN_FILE:
+        /* presume it's an old-style binary file */
+        qof_session_load_from_binfile(book, be->fullpath);
+        error = gnc_get_binfile_io_error();
+        break;
+
+    default:
+        PWARN("File not any known type");
+        error = ERR_FILEIO_UNKNOWN_FILE_TYPE;
+        break;
     }
-    else
+
+    if(error != ERR_BACKEND_NO_ERR) 
     {
-        if(unlink(tmp_name) != 0)
-        {
-            switch (errno) {
-            case ENOENT:     /* tmp_name doesn't exist?  Assume "RO" error */
-            case EACCES:
-            case EPERM:
-            case EROFS:
-              be_err = ERR_BACKEND_READONLY;
-              break;
-            default:
-              be_err = ERR_BACKEND_MISC;
-            }
-            qof_backend_set_error(be, be_err);
-            PWARN("unable to unlink temp_filename %s: %s", 
-                   tmp_name ? tmp_name : "(null)", 
-                   strerror(errno) ? strerror(errno) : ""); 
-            /* already in an error just flow on through */
-        }
-        g_free(tmp_name);
-        return FALSE;
+        qof_backend_set_error(bend, error);
     }
-    return TRUE;
+
+    /* We just got done loading, it can't possibly be dirty !! */
+    qof_book_mark_saved (book);
 }
 
+/* ---------------------------------------------------------------------- */
+
+static gboolean
+gnc_file_be_save_may_clobber_data (QofBackend *bend)
+{
+  struct stat statbuf;
+
+  if (!bend->fullpath) return FALSE;
+
+  /* FIXME: Make sure this doesn't need more sophisticated semantics
+   * in the face of special file, devices, pipes, symlinks, etc. */
+  if (stat(bend->fullpath, &statbuf) == 0) return TRUE;
+                                                                                
+  return FALSE;
+}
+
+
 static void
 gnc_file_be_write_accounts_to_file(QofBackend *be, QofBook *book)
 {
@@ -901,4 +871,54 @@
     gnc_book_write_accounts_to_xml_file_v2(be, book, datafile);
 }
 
+/* ================================================================= */
+
+QofBackend *
+libgncmod_backend_file_LTX_gnc_backend_new(void)
+{
+    FileBackend *fbe;
+    QofBackend *be;
+    
+    fbe = g_new0(FileBackend, 1);
+    be = (QofBackend*)fbe;
+    qof_backend_init(be);
+    
+    be->session_begin = file_session_begin;
+    be->session_end = file_session_end;
+    be->destroy_backend = file_destroy_backend;
+
+    be->load = gnc_file_be_load_from_file;
+    be->save_may_clobber_data = gnc_file_be_save_may_clobber_data;
+
+    /* The file backend treats accounting periods transactionally. */
+    be->begin = file_begin_edit;
+    be->commit = file_commit_edit;
+    be->rollback = file_rollback_edit;
+
+    /* The file backend always loads all data ... */
+    be->compile_query = NULL;
+    be->free_query = NULL;
+    be->run_query = NULL;
+    be->price_lookup = NULL;
+
+    be->counter = NULL;
+
+    /* The file backend will never be multi-user... */
+    be->events_pending = NULL;
+    be->process_events = NULL;
+
+    be->sync = file_sync_all;
+    be->export = gnc_file_be_write_accounts_to_file;
+
+    fbe->dirname = NULL;
+    fbe->fullpath = NULL;
+    fbe->lockfile = NULL;
+    fbe->linkfile = NULL;
+    fbe->lockfd = -1;
+
+    fbe->primary_book = NULL;
+
+    return be;
+}
+
 /* ========================== END OF FILE ===================== */
Index: gw-engine-spec.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gw-engine-spec.scm,v
retrieving revision 1.72
retrieving revision 1.73
diff -Lsrc/engine/gw-engine-spec.scm -Lsrc/engine/gw-engine-spec.scm -u -r1.72 -r1.73
--- src/engine/gw-engine-spec.scm
+++ src/engine/gw-engine-spec.scm
@@ -21,6 +21,7 @@
  ws
  (lambda (wrapset client-wrapset)
    (list
+    "#include <config.h>\n"
     "#include <glib.h>\n"
     "#include <guid.h>\n"
     "#include <Group.h>\n"
Index: qofsession.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -Lsrc/engine/qofsession.c -Lsrc/engine/qofsession.c -u -r1.10 -r1.11
--- src/engine/qofsession.c
+++ src/engine/qofsession.c
@@ -59,13 +59,8 @@
 #include "qofsession-p.h"
 
 /* Some gnucash-specific code */
-#ifdef GNUCASH
+#ifdef GNUCASH_MAJOR_VERSION
 #include "gnc-module.h"
-#include "TransLog.h"
-#else
-#define xaccLogSetBaseName(x)
-#define xaccLogEnable()
-#define xaccLogDisable()
 #endif /* GNUCASH */
 
 static QofSession * current_session = NULL;
@@ -172,8 +167,6 @@
 
   session->books = g_list_append (NULL, qof_book_new ());
   session->book_id = NULL;
-  session->fullpath = NULL;
-  session->logpath = NULL;
   session->backend = NULL;
 
   qof_session_clear_error (session);
@@ -265,7 +258,8 @@
 qof_session_get_file_path (QofSession *session)
 {
    if (!session) return NULL;
-   return session->fullpath;
+   if (!session->backend) return NULL;
+   return session->backend->fullpath;
 }
 
 const char *
@@ -277,7 +271,7 @@
 
 /* ====================================================================== */
 
-#ifdef GNUCASH 
+#ifdef GNUCASH_MAJOR_VERSION 
 
 static void
 qof_session_int_backend_load_error(QofSession *session,
@@ -285,12 +279,6 @@
 {
     PWARN ("%s %s", 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;
 
@@ -437,57 +425,30 @@
     LEAVE("push error missing book_id");
     return;
   }
-  /* Store the sessionid URL  */
-  session->book_id = g_strdup (book_id);
 
-  /* XXX we should probably move this resolve function to the
-   * file backend.  I think the idea would be to open the backend
-   * and then ask it if it can contact it's storage media (disk,
-   * network, server, etc.) and abort if it can't.  Mal-formed
-   * file URL's would be handled the same way!
-   */
-  /* ResolveURL tries to find the file in the file system. */
-  session->fullpath = xaccResolveURL(book_id);
-  if (!session->fullpath)
-  {
-    qof_session_push_error (session, ERR_FILEIO_FILE_NOT_FOUND, NULL);
-    LEAVE("push error: can't resolve file path");
-    return;  
-  }
-  PINFO ("filepath=%s", session->fullpath ? session->fullpath : "(null)");
-
-  session->logpath = xaccResolveFilePath(session->fullpath);
-  PINFO ("logpath=%s", session->logpath ? session->logpath : "(null)");
+  /* Store the session URL  */
+  session->book_id = g_strdup (book_id);
 
   /* destroy the old backend */
   qof_session_destroy_backend(session);
 
-  /* check to see if this is a type we know how to handle */
-  if (!g_strncasecmp(book_id, "file:", 5) ||
-      *session->fullpath == '/')
+  /* Look for somthing of the form of "file:/", "http://" or 
+   * "postgres://". Everything before the colon is the access 
+   * method.  Load the first backend found for that access method.
+   */
+  char * p = strchr (book_id, ':');
+  if (p)
   {
-    qof_session_load_backend(session, "file" ); 
+    char * access_method = g_strdup (book_id);
+    p = strchr (access_method, ':');
+    *p = 0;
+    qof_session_load_backend(session, access_method);
+    g_free (access_method);
   }
   else
   {
-    /* Look for somthing of the form of "http://" or 
-     * "postgres://". Everything before the colon is the access 
-     * method.  Load the first backend found for that access method.
-     */
-    char * p = strchr (book_id, ':');
-    if (p)
-    {
-      char * access_method = g_strdup (book_id);
-      p = strchr (access_method, ':');
-      *p = 0;
-      qof_session_load_backend(session, access_method);
-      g_free (access_method);
-    }
-    else
-    {
-       /* If no colon found, assume it must be a file-path */
-       qof_session_load_backend(session, "file"); 
-    }
+     /* If no colon found, assume it must be a file-path */
+     qof_session_load_backend(session, "file"); 
   }
 
   /* No backend was found. That's bad. */
@@ -513,10 +474,6 @@
       msg = qof_backend_get_message(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;
           qof_session_push_error (session, err, msg);
@@ -560,8 +517,6 @@
   session->books = g_list_append (NULL, newbook);
   PINFO ("new book=%p", newbook);
 
-  xaccLogSetBaseName(session->logpath);
-
   qof_session_clear_error (session);
 
   /* This code should be sufficient to initialize *any* backend,
@@ -580,7 +535,6 @@
    */
   if (be)
   {
-      xaccLogDisable();
       be->percentage = percentage_func;
 
       if (be->load) 
@@ -588,7 +542,6 @@
           be->load (be, newbook);
           qof_session_push_error (session, qof_backend_get_error(be), NULL);
       }
-      xaccLogEnable();
   }
 
   err = qof_session_get_error(session);
@@ -597,24 +550,20 @@
       (err != ERR_SQL_DB_TOO_OLD))
   {
       /* Something broke, put back the old stuff */
-      xaccLogDisable();
       qof_book_set_backend (newbook, NULL);
       qof_book_destroy (newbook);
       g_list_free (session->books);
       session->books = oldbooks;
       LEAVE("error from backend %d", qof_session_get_error(session));
-      xaccLogEnable();
       return;
   }
 
-  xaccLogDisable();
   for (node=oldbooks; node; node=node->next)
   {
      QofBook *ob = node->data;
      qof_book_set_backend (ob, NULL);
      qof_book_destroy (ob);
   }
-  xaccLogEnable();
 
   LEAVE ("sess = %p, book_id=%s", session, qof_session_get_url(session)
          ? qof_session_get_url(session) : "(null)");
@@ -625,19 +574,11 @@
 gboolean
 qof_session_save_may_clobber_data (QofSession *session)
 {
-  struct stat statbuf;
-
   if (!session) return FALSE;
-  if (!session->fullpath) return FALSE;
-
-  /* FIXME: This should really be sent to the backend.  The stat is
-   * correct only for the file backend */
-
-  /* FIXME: Make sure this doesn't need more sophisticated semantics
-   * in the face of special file, devices, pipes, symlinks, etc. */
-  if (stat(session->fullpath, &statbuf) == 0) return TRUE;
+  if (!session->backend) return FALSE;
+  if (!session->backend->save_may_clobber_data) return FALSE;
 
-  return FALSE;
+  return (*(session->backend->save_may_clobber_data)) (session->backend);
 }
 
 static gboolean
@@ -717,7 +658,11 @@
 }
 
 /* ====================================================================== */
-/* XXX what does this function do ?? */
+/* XXX This exports the list of accounts to a file.  It does not export
+ * any transactions.  Its a place-holder until full book-closing is implemented.
+ */
+
+#ifdef GNUCASH_MAJOR_VERSION
 
 gboolean
 qof_session_export (QofSession *tmp_session,
@@ -752,6 +697,7 @@
 
   return TRUE;
 }
+#endif /* GNUCASH_MAJOR_VERSION */
 
 /* ====================================================================== */
 
@@ -771,12 +717,6 @@
 
   qof_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;
 
@@ -794,7 +734,6 @@
          qof_session_get_url(session)
          ? qof_session_get_url(session) : "(null)");
 
-  xaccLogDisable();
   qof_session_end (session);
 
   /* destroy the backend */
@@ -811,8 +750,6 @@
   if (session == current_session)
     current_session = NULL;
 
-  xaccLogEnable();
-
   g_free (session);
 
   LEAVE ("sess=%p", session);
@@ -874,273 +811,14 @@
 }
 
 /* ====================================================================== */
-/* 
- * 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 the 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));
-  }
-
-  return (xaccResolveFilePath (pathfrag));
-}
-
-/* ====================================================================== */
+#ifdef GNUCASH_MAJOR_VERSION
 
 /* this should go in a separate binary to create a rpc server */
 
 void
 gnc_run_rpc_server (void)
 {
-#ifdef GNUCASH
   const char * dll_err;
   void * dll_handle;
   int (*rpc_run)(short);
@@ -1173,7 +851,7 @@
   ret = (*rpc_run)(0);
 
   /* XXX How do we force an exit? */
-#endif /* GNUCASH */
 }
+#endif /* GNUCASH_MAJOR_VERSION */
 
 /* =================== END OF FILE ====================================== */
Index: qofbackend.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofbackend.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lsrc/engine/qofbackend.c -Lsrc/engine/qofbackend.c -u -r1.4 -r1.5
--- src/engine/qofbackend.c
+++ src/engine/qofbackend.c
@@ -130,9 +130,12 @@
     be->error_msg = NULL;
     be->percentage = NULL;
 
+#ifdef GNUCASH_MAJOR_VERSION
     /* XXX remove these */
+    be->fullpath = NULL;
     be->price_lookup = NULL;
     be->export = NULL;
+#endif
 }
 
 /************************* END OF FILE ********************************/
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/Makefile.am,v
retrieving revision 1.121
retrieving revision 1.122
diff -Lsrc/engine/Makefile.am -Lsrc/engine/Makefile.am -u -r1.121 -r1.122
--- src/engine/Makefile.am
+++ src/engine/Makefile.am
@@ -30,6 +30,7 @@
   gnc-engine-util.c \
   gnc-engine.c \
   gnc-event.c \
+  gnc-filepath-utils.c \
   gnc-lot.c \
   gnc-numeric.c \
   gnc-pricedb.c \
@@ -83,6 +84,7 @@
   gnc-engine-util.h \
   gnc-engine.h \
   gnc-event.h \
+  gnc-filepath-utils.h \
   gnc-numeric.h \
   gnc-pricedb.h \
   gnc-session.h \
Index: qofbackend-p.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofbackend-p.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -Lsrc/engine/qofbackend-p.h -Lsrc/engine/qofbackend-p.h -u -r1.5 -r1.6
--- src/engine/qofbackend-p.h
+++ src/engine/qofbackend-p.h
@@ -1,8 +1,6 @@
 /********************************************************************\
  * qofbackend-p.h -- private api for data storage backend           *
  *                                                                  *
- * Copyright (c) 2000, 2001 Linas Vepstas <linas at linas.org>         *
- *                                                                  *
  * 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   *
@@ -34,6 +32,10 @@
    
    The callbacks will be called at the appropriate times during 
    a book session to allow the backend to store the data as needed.
+
+   @file qofbackend-p.h
+   @brief private api for data storage backend
+   @author Copyright (c) 2000,2001,2004 Linas Vepstas <linas at linas.org> 
 @{ */
 
 #ifndef QOF_BACKEND_P_H
@@ -242,7 +244,6 @@
 
 struct QofBackend_s
 {
-
   void (*session_begin) (QofBackend *be,
                          QofSession *session,
                          const char *book_id, 
@@ -265,14 +266,24 @@
 
   gint64 (*counter) (QofBackend *, const char *counter_name);
 
-  gboolean (*events_pending) (QofBackend *be);
-  gboolean (*process_events) (QofBackend *be);
+  gboolean (*events_pending) (QofBackend *);
+  gboolean (*process_events) (QofBackend *);
 
   QofBePercentageFunc percentage;
 
+  /** Document Me !!! what is this supposed to do ?? */
+  gboolean (*save_may_clobber_data) (QofBackend *);
+
   QofBackendError last_err;
   char * error_msg;
 
+  /** XXX the file backend resolves the if to a fully-qualified file
+   *  path.  This holds the filepath and communicates it to the GUI.
+   *  This is temprary scaffolding and should be removed.  Deprecated.
+   */
+  char * fullpath;
+
+#ifdef GNUCASH_MAJOR_VERSION
   /** XXX price_lookup should be removed during the redesign
    * of the SQL backend... prices can now be queried using
    * the generic query mechanism.
@@ -285,12 +296,15 @@
 
   /** XXX Export should really _NOT_ be here, but is left here for now.
    * I'm not sure where this should be going to. It should be
-   * removed ASAP. 
+   * removed ASAP.   This is a temporary hack-around until period-closing
+   * is fully implemented.
    */
   void (*export) (QofBackend *, QofBook *);
+#endif
+
 };
 
-/** Let the ssytem know about a new provider of backends.  This function
+/** Let the sytem know about a new provider of backends.  This function
  *  is typically called by the provider library at library load time.
  *  This function allows the backend library to tell the QOF infrastructure
  *  that it can handle URL's of a certain type.  Note that a single
@@ -299,26 +313,21 @@
  */
 void qof_backend_register_provider (QofBackendProvider *);
 
-/**
- * The qof_backend_set_error() routine pushes an error code onto the error
- *   stack. (FIXME: the stack is 1 deep in current implementation).
+/** The qof_backend_set_error() routine pushes an error code onto the error
+ *  stack. (FIXME: the stack is 1 deep in current implementation).
  */
 void qof_backend_set_error (QofBackend *be, QofBackendError err);
 
-/**
- * The qof_backend_get_error() routine pops an error code off the error
- *   stack.
+/** The qof_backend_get_error() routine pops an error code off the error stack.
  */
 QofBackendError qof_backend_get_error (QofBackend *be);
-/** 
- * The qof_backend_set_message() assigns a string to the backend error
- *   message.
+
+/** The qof_backend_set_message() assigns a string to the backend error message.
  */
 void qof_backend_set_message(QofBackend *be, const char *format, ...);
 
-/**
- * The qof_backend_get_message() pops the error message string from
- *   the Backend.  This string should be freed with g_free().
+/** The qof_backend_get_message() pops the error message string from
+ *  the Backend.  This string should be freed with g_free().
  */
 char * qof_backend_get_message(QofBackend *be);
 
Index: qofsession.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession.h,v
retrieving revision 1.6
retrieving revision 1.7
diff -Lsrc/engine/qofsession.h -Lsrc/engine/qofsession.h -u -r1.6 -r1.7
--- src/engine/qofsession.h
+++ src/engine/qofsession.h
@@ -156,11 +156,6 @@
 void qof_session_load (QofSession *session,
 		       QofPercentageFunc percentage_func);
 
-/** XXX session_export really doesn't belong here */
-gboolean qof_session_export (QofSession *tmp_session,
-			     QofSession *real_session,
-			     QofPercentageFunc percentage_func);
-
 /** @name Session Errors 
  @{ */
 /** The qof_session_get_error() routine can be used to obtain the reason
@@ -251,22 +246,20 @@
 gboolean qof_session_process_events (QofSession *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);
-
+#ifdef GNUCASH_MAJOR_VERSION
 /** Run the RPC Server 
  *  @deprecated  will go away */
 void gnc_run_rpc_server (void);
 
+/** XXX session_export really doesn't belong here .
+ * This functino exports the list of accounts to a file.  Its a stop-gap 
+ * measure until full book-closing is implemented.
+ */
+gboolean qof_session_export (QofSession *tmp_session,
+			     QofSession *real_session,
+			     QofPercentageFunc percentage_func);
+
+#endif /* GNUCASH_MJOR_VERSION */
+
 #endif /* QOF_SESSION_H */
 /** @} */
Index: qofsession-p.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession-p.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -Lsrc/engine/qofsession-p.h -Lsrc/engine/qofsession-p.h -u -r1.3 -r1.4
--- src/engine/qofsession-p.h
+++ src/engine/qofsession-p.h
@@ -53,9 +53,6 @@
   QofBackendError last_err;
   char *error_message;
 
-  char *fullpath;
-  char *logpath;
-
   /* ---------------------------------------------------- */
   /* Pointer to the backend that is actually used to move data
    * between the persistant store and the local engine.  */
Index: test-resolve-file-path.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/test/test-resolve-file-path.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lsrc/engine/test/test-resolve-file-path.c -Lsrc/engine/test/test-resolve-file-path.c -u -r1.4 -r1.5
--- src/engine/test/test-resolve-file-path.c
+++ src/engine/test/test-resolve-file-path.c
@@ -5,6 +5,7 @@
 
 #include "test-stuff.h"
 #include "gnc-engine-util.h"
+#include "gnc-filepath-utils.h"
 #include "qofsession.h"
 
 struct test_strings_struct


More information about the gnucash-changes mailing list