/********************************************************************\ * file-utils.c -- simple file utilities * * Copyright (C) 1997 Robin D. Clark * * Copyright (C) 1998 Rob Browning * * Copyright (C) 1998-2000 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 * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, write to the Free Software * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * \********************************************************************/ #include #include //#include //#include #include "gnc-state.h" //#include "gnc-engine.h" #include "gnc-filepath-utils.h" #include "gnc-gkeyfile-utils.h" #include "gnc-uri-utils.h" #include "qof.h" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = G_LOG_DOMAIN; /* Absolute path to the state file for the current book * Before 2.4.1, this file didn't have an extension. * The code will look for such pre 2.4.0 file if no post 2.4.1 * version is found. If there is an old style file, save the * name here as well. The old style state file will then be * converted into a new style one the next time state is saved. */ static gchar* state_file_name = NULL; static gchar* state_file_name_pre_241 = NULL; /* State file data for current book */ static GKeyFile *state_file = NULL; /* Determine which file name to use for the state file. This name is based * the current book's uri and guid. * * The state files will be searched for in the books directory in GnuCash' * private configuration directory. This configuration directory is * platform dependent and can be overridden with environment variable * DOT_GNUCASH_DIR. On linux for example this is ~/.gnucash by default. * * The URL is used to compute the base name of the state file and the * guid is used to differentiate when the user has multiple data files * with the same name. * * As of GnuCash 2.4.1 state files will have their own extension to * differentiate them from data files saved by the user. New state * files will always be created with such an extension. But GnuCash * will continue to search for state files without an extension if * no proper state file with extension is found. */ static void gnc_state_set_base (const QofSession *session) { gchar *basename, *original = NULL, *filename, *file_guid; gchar *sf_extension = NULL; const gchar *uri; gchar guid_string[GUID_ENCODING_LENGTH+1]; QofBook *book; const GncGUID *guid; GKeyFile *key_file = NULL; gint i; /* Reset filenames possibly found in a previous run */ g_free (state_file_name); g_free (state_file_name_pre_241); state_file_name = NULL; state_file_name_pre_241 = NULL; uri = qof_session_get_url (session); ENTER("session %p (%s)", session, uri ? uri : "(null)"); if (!strlen (uri)) { LEAVE("no uri, nothing to do"); return; } /* Get the book GncGUID */ book = qof_session_get_book(session); guid = qof_entity_get_guid(QOF_INSTANCE(book)); guid_to_string_buff(guid, guid_string); if (gnc_uri_targets_local_fs (uri)) { /* The book_uri is a true file, use its basename. */ gchar *path = gnc_uri_get_path (uri); basename = g_path_get_basename (path); g_free (path); } else { /* The book_uri is composed of database connection parameters. */ gchar* scheme = NULL; gchar* host = NULL; gchar* dbname = NULL; gchar* username = NULL; gchar* password = NULL; gint portnum = 0; gnc_uri_get_components (uri, &scheme, &host, &portnum, &username, &password, &dbname); basename = g_strjoin ("_", scheme, host, username, dbname, NULL); g_free (scheme); g_free (host); g_free (username); g_free (password); g_free (dbname); } DEBUG ("Basename %s", basename); original = gnc_build_book_path (basename); g_free (basename); DEBUG ("Original %s", original); sf_extension = g_strdup (STATE_FILE_EXT); i = 1; while (1) { if (i == 1) filename = g_strconcat (original, sf_extension, NULL); else filename = g_strdup_printf ("%s_%d%s", original, i, sf_extension); DEBUG ("Trying %s", filename); key_file = gnc_key_file_load_from_file (filename, TRUE, FALSE, NULL); DEBUG ("Result %p", key_file); if (!key_file) { DEBUG ("No key file by that name"); if (g_strcmp0 (sf_extension, STATE_FILE_EXT) == 0) { DEBUG ("Trying old state file names for compatibility"); i = 1; g_free (sf_extension); sf_extension = g_strdup (""); /* Regardless of whether or not an old state file is found, * the currently tested name should be used for the future * state file. */ state_file_name = filename; continue; } /* No old style file found. We'll return with the new file name * we set earlier, and no existing key file. */ g_free (filename); break; } file_guid = g_key_file_get_string (key_file, STATE_FILE_TOP, STATE_FILE_BOOK_GUID, NULL); DEBUG ("File GncGUID is %s", file_guid ? file_guid : ""); if (g_strcmp0 (guid_string, file_guid) == 0) { DEBUG ("Matched !!!"); /* Save the found file for later use. Which name to save to * depends on whether it was an old or new style file name */ if (g_strcmp0 (sf_extension, STATE_FILE_EXT) == 0) state_file_name = filename; else state_file_name_pre_241 = filename; g_free (file_guid); break; } DEBUG ("Clean up this pass"); g_free (file_guid); g_key_file_free (key_file); g_free (filename); i++; } DEBUG("Clean up"); g_free(sf_extension); g_free(original); if (key_file != NULL) g_key_file_free (key_file); LEAVE (); } GKeyFile *gnc_state_load (const QofSession *session) { /* Drop possible previous state_file first */ if (state_file) { g_key_file_free (state_file); state_file = NULL; } gnc_state_set_base (session); if (state_file_name_pre_241) state_file = gnc_key_file_load_from_file (state_file_name_pre_241, TRUE, TRUE, NULL); else if (state_file_name) state_file = gnc_key_file_load_from_file (state_file_name, TRUE, TRUE, NULL); return gnc_state_get_current (); } void gnc_state_save (const QofSession *session) { GError *error = NULL; if (!strlen (qof_session_get_url(session))) { DEBUG("No file associated with session - skip state saving"); return; } gnc_state_set_base (session); /* Write it all out to disk */ if (state_file_name) gnc_key_file_save_to_file(state_file_name, state_file, &error); else PWARN ("No state file name set, can't save state"); if (error) { PERR ("Error: Cannot open state file %s", error->message); g_error_free (error); } } GKeyFile *gnc_state_get_current (void) { if (!state_file) { PINFO ("No pre-existing state found, creating new one"); state_file = g_key_file_new (); } return state_file; } gint gnc_state_drop_sections_for (const gchar *partial_name) { gchar **groups; gint found_count = 0, dropped_count = 0; gsize i, num_groups; GError *error = NULL; if (!state_file) { PWARN ("No pre-existing state found, ignoring drop request"); return 0; } ENTER(""); groups = g_key_file_get_groups (state_file, &num_groups); for (i = 0; i < num_groups; i++) { if (g_strstr_len (groups[i], -1, partial_name)) { DEBUG ("Section \"%s\" matches \"%s\", removing", groups[i], partial_name); found_count++; if (!g_key_file_remove_group (state_file, groups[i], &error)) { PWARN ("Warning: unable to remove section %s.\n %s", groups[i], error->message); g_error_free (error); } else dropped_count++; } } g_strfreev (groups); LEAVE("Found %i sections matching \"%s\", successfully removed %i", found_count, partial_name, dropped_count); return dropped_count; }