From 17531066a18c77165e74182f1726c66bc705ac06 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 13 Jun 2004 20:01:29 +0000 Subject: [PATCH] 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. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@10063 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/app-file/gnc-file.c | 22 + src/backend/file/gnc-backend-file.c | 922 ++++++++++++----------- src/backend/file/gnc-backend-file.h | 2 + src/engine/Makefile.am | 2 + src/engine/gw-engine-spec.scm | 1 + src/engine/qofbackend-p.h | 47 +- src/engine/qofbackend.c | 3 + src/engine/qofsession-p.h | 3 - src/engine/qofsession.c | 382 +--------- src/engine/qofsession.h | 29 +- src/engine/test/test-resolve-file-path.c | 1 + 11 files changed, 571 insertions(+), 843 deletions(-) diff --git a/src/app-file/gnc-file.c b/src/app-file/gnc-file.c index c80f308c8e..07630d443c 100644 --- a/src/app-file/gnc-file.c +++ b/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 @@ gnc_file_new (void) 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 @@ gnc_post_file_open (const char * filename) gw_wcp_assimilate_ptr (current_session, scm_c_eval_string("")) : 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 @@ gnc_post_file_open (const char * filename) { 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 @@ gnc_post_file_open (const char * filename) } 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 @@ gnc_post_file_open (const char * filename) /* 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 @@ gnc_file_export_file(const char * newfile) 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 @@ gnc_file_save_as (void) 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 @@ gnc_file_save_as (void) /* 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 @@ gnc_file_quit (void) gw_wcp_assimilate_ptr (session, scm_c_eval_string("")) : SCM_BOOL_F)); + xaccLogDisable(); qof_session_destroy (session); + xaccLogEnable(); qof_session_get_current_session (); diff --git a/src/backend/file/gnc-backend-file.c b/src/backend/file/gnc-backend-file.c index c45b74e46f..3356cbf153 100644 --- a/src/backend/file/gnc-backend-file.c +++ b/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 short module = MOD_BACKEND; 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,263 +78,6 @@ gnc_file_be_set_compression (gboolean compress) /* ================================================================= */ -static void -file_session_begin(QofBackend *be_start, QofSession *session, - const char *book_id, - gboolean ignore_lock, gboolean create_if_nonexistent) -{ - FileBackend *be = (FileBackend*) be_start; - char *p; - - ENTER (" "); - - /* Make sure the directory is there */ - be->dirname = g_strdup (qof_session_get_file_path (session)); - be->fullpath = g_strdup (be->dirname); - p = strrchr (be->dirname, '/'); - if (p && p != be->dirname) - { - struct stat statbuf; - int rc; - - *p = '\0'; - rc = stat (be->dirname, &statbuf); - if (rc != 0 || !S_ISDIR(statbuf.st_mode)) - { - qof_backend_set_error (be_start, ERR_FILEIO_FILE_NOT_FOUND); - g_free (be->fullpath); be->fullpath = NULL; - g_free (be->dirname); be->dirname = NULL; - return; - } - rc = stat (be->fullpath, &statbuf); - if (rc == 0 && S_ISDIR(statbuf.st_mode)) - { - qof_backend_set_error (be_start, ERR_FILEIO_UNKNOWN_FILE_TYPE); - g_free (be->fullpath); be->fullpath = NULL; - g_free (be->dirname); be->dirname = NULL; - return; - } - } - - /* ---------------------------------------------------- */ - /* We should now have a fully resolved path name. - * Lets see if we can get a lock on it. */ - - be->lockfile = g_strconcat(be->fullpath, ".LCK", NULL); - - if (!ignore_lock && !gnc_file_be_get_file_lock (be)) - { - g_free (be->lockfile); be->lockfile = NULL; - return; - } - - LEAVE (" "); - return; -} - -/* ================================================================= */ - -static void -file_session_end(QofBackend *be_start) -{ - FileBackend *be = (FileBackend*)be_start; - ENTER (" "); - - if (be->linkfile) - unlink (be->linkfile); - - if (be->lockfd > 0) - close (be->lockfd); - - if (be->lockfile) - unlink (be->lockfile); - - g_free (be->dirname); - be->dirname = NULL; - - g_free (be->fullpath); - be->fullpath = NULL; - - g_free (be->lockfile); - be->lockfile = NULL; - - g_free (be->linkfile); - be->linkfile = NULL; - LEAVE (" "); -} - -static void -file_destroy_backend(QofBackend *be) -{ - g_free(be); -} - -static void -file_sync_all(QofBackend* be, QofBook *book) -{ - FileBackend *fbe = (FileBackend *) be; - ENTER ("book=%p, primary=%p", book, fbe->primary_book); - - /* 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; - - gnc_file_be_write_to_file (fbe, book, fbe->fullpath, TRUE); - gnc_file_be_remove_old_files (fbe); - LEAVE ("book=%p", book); -} - -/* ================================================================= */ -/* 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) - { - 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) { @@ -444,96 +176,106 @@ gnc_file_be_get_file_lock (FileBackend *be) 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; - } - - if(read(fd, buf, 2) != 2) - { - return FALSE; - } - - if(buf[0] == 037 && buf[1] == 0213) - { - return TRUE; - } - - return FALSE; -} - -static QofBookFileType -gnc_file_be_determine_file_type(const char *path) -{ - if(gnc_is_xml_data_file_v2(path)) { - return GNC_BOOK_XML2_FILE; - } else if(gnc_is_xml_data_file(path)) { - return GNC_BOOK_XML1_FILE; - } else if(is_gzipped_file(path)) { - return GNC_BOOK_XML2_FILE; - } else { - return GNC_BOOK_BIN_FILE; - } -} - - -/* 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) +file_session_begin(QofBackend *be_start, QofSession *session, + const char *book_id, + gboolean ignore_lock, gboolean create_if_nonexistent) { - QofBackendError error = ERR_BACKEND_NO_ERR; - gboolean rc; - FileBackend *be = (FileBackend *) bend; + FileBackend *be = (FileBackend*) be_start; + char *p; - be->primary_book = book; + ENTER (" "); - switch (gnc_file_be_determine_file_type(be->fullpath)) + /* Make sure the directory is there */ + be->dirname = xaccResolveFilePath(book_id); + if (NULL == be->dirname) { - case GNC_BOOK_XML2_FILE: - rc = qof_session_load_from_xml_file_v2 (be, book); - if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR; - break; + 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) + { + struct stat statbuf; + int rc; - 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; + *p = '\0'; + rc = stat (be->dirname, &statbuf); + if (rc != 0 || !S_ISDIR(statbuf.st_mode)) + { + qof_backend_set_error (be_start, ERR_FILEIO_FILE_NOT_FOUND); + g_free (be->fullpath); be->fullpath = NULL; + g_free (be->dirname); be->dirname = NULL; + return; + } + rc = stat (be->fullpath, &statbuf); + if (rc == 0 && S_ISDIR(statbuf.st_mode)) + { + qof_backend_set_error (be_start, ERR_FILEIO_UNKNOWN_FILE_TYPE); + g_free (be->fullpath); be->fullpath = NULL; + g_free (be->dirname); be->dirname = NULL; + return; + } } - if(error != ERR_BACKEND_NO_ERR) + /* ---------------------------------------------------- */ + /* We should now have a fully resolved path name. + * Lets see if we can get a lock on it. */ + + be->lockfile = g_strconcat(be->fullpath, ".LCK", NULL); + + if (!ignore_lock && !gnc_file_be_get_file_lock (be)) { - qof_backend_set_error(bend, error); + g_free (be->lockfile); be->lockfile = NULL; + return; } - /* We just got done loading, it can't possibly be dirty !! */ - qof_book_mark_saved (book); + LEAVE (" "); + return; } -/* ---------------------------------------------------------------------- */ +/* ================================================================= */ + +static void +file_session_end(QofBackend *be_start) +{ + FileBackend *be = (FileBackend*)be_start; + ENTER (" "); + + if (be->linkfile) + unlink (be->linkfile); + + if (be->lockfd > 0) + close (be->lockfd); + + if (be->lockfile) + unlink (be->lockfile); + + g_free (be->dirname); + be->dirname = NULL; + + g_free (be->fullpath); + be->fullpath = NULL; + + g_free (be->lockfile); + be->lockfile = NULL; + + g_free (be->linkfile); + be->linkfile = NULL; + LEAVE (" "); +} + +static void +file_destroy_backend(QofBackend *be) +{ + g_free(be); +} + +/* ================================================================= */ /* 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 @@ -594,6 +336,8 @@ copy_file(const char *orig, const char *bkup) return TRUE; } +/* ================================================================= */ + static gboolean gnc_int_link_or_make_backup(FileBackend *be, const char *orig, const char *bkup) { @@ -617,6 +361,47 @@ gnc_int_link_or_make_backup(FileBackend *be, const char *orig, const char *bkup) 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; + } + + if(read(fd, buf, 2) != 2) + { + return FALSE; + } + + if(buf[0] == 037 && buf[1] == 0213) + { + return TRUE; + } + + return FALSE; +} + +static QofBookFileType +gnc_file_be_determine_file_type(const char *path) +{ + if(gnc_is_xml_data_file_v2(path)) { + return GNC_BOOK_XML2_FILE; + } else if(gnc_is_xml_data_file(path)) { + return GNC_BOOK_XML1_FILE; + } else if(is_gzipped_file(path)) { + return GNC_BOOK_XML2_FILE; + } else { + return GNC_BOOK_BIN_FILE; + } +} + + static gboolean gnc_file_be_backup_file(FileBackend *be) { @@ -662,112 +447,8 @@ gnc_file_be_backup_file(FileBackend *be) return bkup_ret; } -static int -gnc_file_be_select_files (const struct dirent *d) -{ - int len = strlen(d->d_name) - 4; - - if (len <= 0) - return(0); - - return((strcmp(d->d_name + len, ".LNK") == 0) || - (strcmp(d->d_name + len, ".xac") == 0) || - (strcmp(d->d_name + len, ".log") == 0)); -} - -static void -gnc_file_be_remove_old_files(FileBackend *be) -{ - struct dirent *dent; - DIR *dir; - struct stat lockstatbuf, statbuf; - int pathlen; - time_t now; - - if (stat (be->lockfile, &lockstatbuf) != 0) - return; - pathlen = strlen(be->fullpath); - - /* - * Clean up any lockfiles from prior crashes, and clean up old - * data and log files. Scandir will do a fist pass on the - * filenames and cull the directory down to just files with the - * appropriate extensions. Pity you can't pass user data into - * scandir... - */ - - /* - * Unfortunately scandir() is not portable, so re-write this - * function without it. Note that this version will be even a bit - * faster because it does not have to sort, malloc, or anything - * else that scandir did, and it only performs a single pass - * through the directory rather than one pass through the - * directory and then one pass over the 'matching' files. -- - * warlord@MIT.EDU 2002-05-06 - */ - - dir = opendir (be->dirname); - if (!dir) - return; - - now = time(NULL); - while((dent = readdir(dir)) != NULL) { - char *name; - int len; - - if (gnc_file_be_select_files (dent) == 0) - continue; - - name = g_strconcat(be->dirname, "/", dent->d_name, NULL); - len = strlen(name) - 4; - - /* Is this file associated with the current data file */ - if (strncmp(name, be->fullpath, pathlen) == 0) - { - if ((safe_strcmp(name + len, ".LNK") == 0) && - /* Is a lock file. Skip the active lock file */ - (safe_strcmp(name, be->linkfile) != 0) && - /* Only delete lock files older than the active one */ - (stat(name, &statbuf) == 0) && - (statbuf.st_mtime 0) - { - time_t file_time; - struct tm file_tm; - int days; - const char* res; - - PINFO ("file retention = %d days", file_retention_days); - - /* Is the backup file old enough to delete */ - memset(&file_tm, 0, sizeof(file_tm)); - res = strptime(name+pathlen+1, "%Y%m%d%H%M%S", &file_tm); - file_time = mktime(&file_tm); - days = (int)(difftime(now, file_time) / 86400); - - /* Make sure this file actually has a date before unlinking */ - if (res && res != name+pathlen+1 && - /* We consumed some but not all of the filename */ - file_time > 0 && - /* we actually have a reasonable time and it is old enough */ - days > file_retention_days) - { - PINFO ("unlink stale (%d days old) file: %s", days, name); - unlink(name); - } - } - } - g_free(name); - } - closedir (dir); -} - -/* ---------------------------------------------------------------------- */ - +/* ================================================================= */ + static gboolean gnc_file_be_write_to_file(FileBackend *fbe, QofBook *book, @@ -892,6 +573,295 @@ gnc_file_be_write_to_file(FileBackend *fbe, return TRUE; } +/* ================================================================= */ + +static int +gnc_file_be_select_files (const struct dirent *d) +{ + int len = strlen(d->d_name) - 4; + + if (len <= 0) + return(0); + + return((strcmp(d->d_name + len, ".LNK") == 0) || + (strcmp(d->d_name + len, ".xac") == 0) || + (strcmp(d->d_name + len, ".log") == 0)); +} + +static void +gnc_file_be_remove_old_files(FileBackend *be) +{ + struct dirent *dent; + DIR *dir; + struct stat lockstatbuf, statbuf; + int pathlen; + time_t now; + + if (stat (be->lockfile, &lockstatbuf) != 0) + return; + pathlen = strlen(be->fullpath); + + /* + * Clean up any lockfiles from prior crashes, and clean up old + * data and log files. Scandir will do a fist pass on the + * filenames and cull the directory down to just files with the + * appropriate extensions. Pity you can't pass user data into + * scandir... + */ + + /* + * Unfortunately scandir() is not portable, so re-write this + * function without it. Note that this version will be even a bit + * faster because it does not have to sort, malloc, or anything + * else that scandir did, and it only performs a single pass + * through the directory rather than one pass through the + * directory and then one pass over the 'matching' files. -- + * warlord@MIT.EDU 2002-05-06 + */ + + dir = opendir (be->dirname); + if (!dir) + return; + + now = time(NULL); + while((dent = readdir(dir)) != NULL) { + char *name; + int len; + + if (gnc_file_be_select_files (dent) == 0) + continue; + + name = g_strconcat(be->dirname, "/", dent->d_name, NULL); + len = strlen(name) - 4; + + /* Is this file associated with the current data file */ + if (strncmp(name, be->fullpath, pathlen) == 0) + { + if ((safe_strcmp(name + len, ".LNK") == 0) && + /* Is a lock file. Skip the active lock file */ + (safe_strcmp(name, be->linkfile) != 0) && + /* Only delete lock files older than the active one */ + (stat(name, &statbuf) == 0) && + (statbuf.st_mtime 0) + { + time_t file_time; + struct tm file_tm; + int days; + const char* res; + + PINFO ("file retention = %d days", file_retention_days); + + /* Is the backup file old enough to delete */ + memset(&file_tm, 0, sizeof(file_tm)); + res = strptime(name+pathlen+1, "%Y%m%d%H%M%S", &file_tm); + file_time = mktime(&file_tm); + days = (int)(difftime(now, file_time) / 86400); + + /* Make sure this file actually has a date before unlinking */ + if (res && res != name+pathlen+1 && + /* We consumed some but not all of the filename */ + file_time > 0 && + /* we actually have a reasonable time and it is old enough */ + days > file_retention_days) + { + PINFO ("unlink stale (%d days old) file: %s", days, name); + unlink(name); + } + } + } + g_free(name); + } + closedir (dir); +} + +static void +file_sync_all(QofBackend* be, QofBook *book) +{ + FileBackend *fbe = (FileBackend *) be; + ENTER ("book=%p, primary=%p", book, fbe->primary_book); + + /* 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; + + gnc_file_be_write_to_file (fbe, book, fbe->fullpath, TRUE); + gnc_file_be_remove_old_files (fbe); + LEAVE ("book=%p", book); +} + +/* ================================================================= */ +/* 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) + { + 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 +} + +/* ---------------------------------------------------------------------- */ + + +/* 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; + } + + 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); +} + +/* ---------------------------------------------------------------------- */ + +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_file_be_write_accounts_to_file(QofBackend *be, QofBook *book) 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 ===================== */ diff --git a/src/backend/file/gnc-backend-file.h b/src/backend/file/gnc-backend-file.h index bf2835acea..ae94e7af83 100644 --- a/src/backend/file/gnc-backend-file.h +++ b/src/backend/file/gnc-backend-file.h @@ -57,4 +57,6 @@ typedef enum GNC_BOOK_XML2_FILE, } QofBookFileType; +QofBackend * libgncmod_backend_file_LTX_gnc_backend_new(void); + #endif /* GNC_BACKEND_FILE_H_ */ diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index 2a741265e9..bd5de04d75 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -30,6 +30,7 @@ libgncmod_engine_la_SOURCES = \ 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 @@ gncinclude_HEADERS = \ gnc-engine-util.h \ gnc-engine.h \ gnc-event.h \ + gnc-filepath-utils.h \ gnc-numeric.h \ gnc-pricedb.h \ gnc-session.h \ diff --git a/src/engine/gw-engine-spec.scm b/src/engine/gw-engine-spec.scm index 20ffce3a39..da378f3476 100644 --- a/src/engine/gw-engine-spec.scm +++ b/src/engine/gw-engine-spec.scm @@ -21,6 +21,7 @@ ws (lambda (wrapset client-wrapset) (list + "#include \n" "#include \n" "#include \n" "#include \n" diff --git a/src/engine/qofbackend-p.h b/src/engine/qofbackend-p.h index 171913f902..8fbb88e371 100644 --- a/src/engine/qofbackend-p.h +++ b/src/engine/qofbackend-p.h @@ -1,8 +1,6 @@ /********************************************************************\ * qofbackend-p.h -- private api for data storage backend * * * - * Copyright (c) 2000, 2001 Linas Vepstas * - * * * 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 @{ */ #ifndef QOF_BACKEND_P_H @@ -242,7 +244,6 @@ struct QofBackendProvider_s struct QofBackend_s { - void (*session_begin) (QofBackend *be, QofSession *session, const char *book_id, @@ -265,14 +266,24 @@ struct QofBackend_s 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 @@ struct QofBackend_s /** 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 @@ struct QofBackend_s */ 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); diff --git a/src/engine/qofbackend.c b/src/engine/qofbackend.c index 3508076dfa..11ef422451 100644 --- a/src/engine/qofbackend.c +++ b/src/engine/qofbackend.c @@ -130,9 +130,12 @@ qof_backend_init(QofBackend *be) 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 ********************************/ diff --git a/src/engine/qofsession-p.h b/src/engine/qofsession-p.h index 72fe3529f7..dff606fbd2 100644 --- a/src/engine/qofsession-p.h +++ b/src/engine/qofsession-p.h @@ -53,9 +53,6 @@ struct _QofSession 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. */ diff --git a/src/engine/qofsession.c b/src/engine/qofsession.c index 032c731919..8eb41b8726 100644 --- a/src/engine/qofsession.c +++ b/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 @@ qof_session_init (QofSession *session) 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 @@ const char * 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 @@ qof_session_get_url (QofSession *session) /* ====================================================================== */ -#ifdef GNUCASH +#ifdef GNUCASH_MAJOR_VERSION static void qof_session_int_backend_load_error(QofSession *session, @@ -285,12 +279,6 @@ qof_session_int_backend_load_error(QofSession *session, { 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 @@ qof_session_begin (QofSession *session, const char * book_id, LEAVE("push error missing book_id"); return; } - /* Store the sessionid URL */ + + /* Store the session 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)"); - /* 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 @@ qof_session_begin (QofSession *session, const char * book_id, 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 @@ qof_session_load (QofSession *session, 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 @@ qof_session_load (QofSession *session, */ if (be) { - xaccLogDisable(); be->percentage = percentage_func; if (be->load) @@ -588,7 +542,6 @@ qof_session_load (QofSession *session, 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 @@ qof_session_load (QofSession *session, (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 @@ qof_session_load (QofSession *session, gboolean qof_session_save_may_clobber_data (QofSession *session) { - struct stat statbuf; - if (!session) return FALSE; - if (!session->fullpath) return FALSE; + if (!session->backend) return FALSE; + if (!session->backend->save_may_clobber_data) 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; - - return FALSE; + return (*(session->backend->save_may_clobber_data)) (session->backend); } static gboolean @@ -717,7 +658,11 @@ qof_session_save (QofSession *session, } /* ====================================================================== */ -/* 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 @@ qof_session_export (QofSession *tmp_session, return TRUE; } +#endif /* GNUCASH_MAJOR_VERSION */ /* ====================================================================== */ @@ -771,12 +717,6 @@ qof_session_end (QofSession *session) 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_destroy (QofSession *session) qof_session_get_url(session) ? qof_session_get_url(session) : "(null)"); - xaccLogDisable(); qof_session_end (session); /* destroy the backend */ @@ -811,8 +750,6 @@ qof_session_destroy (QofSession *session) if (session == current_session) current_session = NULL; - xaccLogEnable(); - g_free (session); LEAVE ("sess=%p", session); @@ -874,273 +811,14 @@ qof_session_process_events (QofSession *session) } /* ====================================================================== */ -/* - * 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 @@ gnc_run_rpc_server (void) ret = (*rpc_run)(0); /* XXX How do we force an exit? */ -#endif /* GNUCASH */ } +#endif /* GNUCASH_MAJOR_VERSION */ /* =================== END OF FILE ====================================== */ diff --git a/src/engine/qofsession.h b/src/engine/qofsession.h index b39df6809e..f0458f95e6 100644 --- a/src/engine/qofsession.h +++ b/src/engine/qofsession.h @@ -156,11 +156,6 @@ typedef void (*QofPercentageFunc) (const char *message, double percent); 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_events_pending (QofSession *session); 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 */ /** @} */ diff --git a/src/engine/test/test-resolve-file-path.c b/src/engine/test/test-resolve-file-path.c index 0f52dbeedf..58857b8cf2 100644 --- a/src/engine/test/test-resolve-file-path.c +++ b/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