Play nicer with recent releases of glib that have tightened what's

considered a valid key in a key/value file. This change will cause
gnucash to rewrite any opened state file into a new version where all
key names fit the new glib requirements.

This code was originally committed to trunk as #15458 on 1/28/07, and
then backed out with #15461 on 1/29/07.  Backward compatibility
support for this code was committed to the 2.0 branch in #15460 and
was released as part of 2.0.5.  Now that 2.0.5 has now been out for
six weeks, this commit puts the original changes back into trunk.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15787 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
David Hampton 2007-04-04 03:40:14 +00:00
parent c1ae1cc0f3
commit 3a612f804f
12 changed files with 135 additions and 41 deletions

View File

@ -23,6 +23,9 @@
#include <glib.h>
#include <glib/gstdio.h>
#ifndef HAVE_GLIB_2_8
#include <gfileutils-2.8.h>
#endif
#include <libguile.h>
#include <errno.h>
#include <fcntl.h>
@ -167,6 +170,63 @@ gnc_getline (gchar **line, FILE *file)
}
/* Update one state file file to fit the new constraints introduced by
* glib 2.12.5. Supposedly its always been illegal to use spaces in
* key names, but it was never a problem with earlier releases of
* glib. Glib 2.12.5 added hard enforcement of this rule, completely
* ignoring any key/value pair where the key name contained an
* "illegal" character. Glib 2.12.7 relented and changed the hard
* failure to a warning, but the point has been made. Spaces in key
* names must go.
*/
static gboolean
gnc_update_state_file_keys(const gchar *filename)
{
const gchar *eol;
gchar *contents, **lines, *line, **kv, **parts, *part, *newkey;
GError *error = NULL;
int i, j;
if (!g_file_get_contents(filename, &contents, NULL, &error)) {
DEBUG("Error reading state file: %s", error->message);
g_error_free(error);
return FALSE;
}
lines = g_strsplit(contents, "\n", -1);
g_free(contents);
/* Strip spaces from non-comment lines, and rewrite the new text
* over top of the old text. The new line is guaranteed to be at
* most the same number of characters as the old. */
for (i = 0, line = lines[i++]; line; line = lines[i++]) {
if ((*line == '\0') || (*line == '#') || (*line == '[')) {
continue;
} else {
kv = g_strsplit(line, "=", 2);
parts = g_strsplit(kv[0], " ", -1);
for (j = 0, part = parts[j++]; part; part = parts[j++])
part[0] = g_ascii_toupper(part[0]);
newkey = g_strjoinv("", parts);
g_sprintf(line, "%s=%s", newkey, kv[1]);
g_free(newkey);
g_strfreev(parts);
g_strfreev(kv);
}
}
contents = g_strjoinv("\n", lines);
if (!g_file_set_contents(filename, contents, -1, &error)) {
DEBUG("Error writing state file: %s", error->message);
g_error_free(error);
g_free(contents);
return FALSE;
}
g_free(contents);
return TRUE;
}
/* Find the state file that corresponds to this URL and guid. The
* URL is used to compute the base name of the file (which will be in
* ~/.gnucash/books) and the guid is used to differentiate when the
@ -179,6 +239,7 @@ gnc_find_state_file (const gchar *url,
gchar *basename, *original = NULL, *filename, *tmp, *file_guid;
GKeyFile *key_file = NULL;
GError *error = NULL;
gboolean do_increment;
gint i;
ENTER("url %s, guid %s", url, guid);
@ -199,9 +260,24 @@ gnc_find_state_file (const gchar *url,
else
filename = g_strdup_printf("%s_%d", original, i);
DEBUG("Trying %s", filename);
key_file = gnc_key_file_load_from_file(filename, FALSE, FALSE);
key_file = gnc_key_file_load_from_file(filename, FALSE, FALSE, &error);
DEBUG("Result %p", key_file);
if (error &&
(error->domain == G_KEY_FILE_ERROR) &&
(error->code == G_KEY_FILE_ERROR_PARSE)) {
/* Handle the case where glib was updated first, and is refusing
* to read old state files. */
if (gnc_update_state_file_keys(filename)) {
DEBUG("Trying %s again", filename);
key_file = gnc_key_file_load_from_file(filename, FALSE, FALSE, NULL);
DEBUG("Result %p", key_file);
}
}
if (error) {
g_error_free(error);
error = NULL;
}
if (!key_file) {
DEBUG("No key file by that name");
break;
@ -209,7 +285,7 @@ gnc_find_state_file (const gchar *url,
file_guid = g_key_file_get_string(key_file,
STATE_FILE_TOP, STATE_FILE_BOOK_GUID,
&error);
NULL);
DEBUG("File GUID is %s", file_guid);
if (safe_strcmp(guid, file_guid) == 0) {
DEBUG("Matched !!!");
@ -217,11 +293,26 @@ gnc_find_state_file (const gchar *url,
break;
}
/* Handle the case where gnucash was updated first, and is trying
* to find new key names in an old state files. */
file_guid = g_key_file_get_string(key_file,
STATE_FILE_TOP, STATE_FILE_BOOK_GUID_OLD,
NULL);
DEBUG("%s is %s", STATE_FILE_BOOK_GUID,
file_guid ? file_guid : "<not found>");
if (safe_strcmp(guid, file_guid) == 0) {
DEBUG("Matched !!!");
do_increment = !gnc_update_state_file_keys(filename);
} else {
do_increment = TRUE;
}
DEBUG("Clean up this pass");
g_free(file_guid);
g_key_file_free(key_file);
g_free(filename);
i++;
if (do_increment)
i++;
}
DEBUG("Clean up");

View File

@ -60,8 +60,9 @@ gint64 gnc_getline (gchar **line, FILE *file);
/* Definitions shared by file-utils.c and gnc-main-window.c */
#define STATE_FILE_TOP "Top"
#define STATE_FILE_BOOK_GUID "Book Guid"
#define STATE_FILE_TOP "Top"
#define STATE_FILE_BOOK_GUID "BookGuid"
#define STATE_FILE_BOOK_GUID_OLD "Book Guid"
/** Find the state file that corresponds to this URL and guid. The
* URL is used to compute the base name of the file (which will be in

View File

@ -86,7 +86,7 @@ gnc_exp_parser_real_init ( gboolean addPredefined )
if ( addPredefined ) {
filename = gnc_exp_parser_filname();
key_file = gnc_key_file_load_from_file(filename, TRUE, FALSE);
key_file = gnc_key_file_load_from_file(filename, TRUE, FALSE, NULL);
if (key_file) {
keys = g_key_file_get_keys(key_file, GROUP_NAME, NULL, NULL);
for (key = keys; key && *key; key++) {

View File

@ -1676,10 +1676,10 @@ gnc_invoice_new_page (GNCBook *bookp, InvoiceDialogType type,
return iw;
}
#define KEY_INVOICE_TYPE "Invoice Type"
#define KEY_INVOICE_GUID "Invoice GUID"
#define KEY_OWNER_TYPE "Owner Type"
#define KEY_OWNER_GUID "Owner GUID"
#define KEY_INVOICE_TYPE "InvoiceType"
#define KEY_INVOICE_GUID "InvoiceGUID"
#define KEY_OWNER_TYPE "OwnerType"
#define KEY_OWNER_GUID "OwnerGUID"
GncPluginPage *
gnc_invoice_recreate_page (GKeyFile *key_file,

View File

@ -265,7 +265,8 @@ g_key_file_set_double_list (GKeyFile *key_file,
GKeyFile *
gnc_key_file_load_from_file (const gchar *filename,
gboolean ignore_error,
gboolean return_empty_struct)
gboolean return_empty_struct,
GError **caller_error)
{
GKeyFile *key_file;
GError *error = NULL;
@ -290,7 +291,7 @@ gnc_key_file_load_from_file (const gchar *filename,
if (!ignore_error)
g_warning("Unable to read file %s: %s\n", filename, error->message);
g_error_free(error);
g_propagate_error(caller_error, error);
return key_file;
}

View File

@ -71,7 +71,8 @@ g_key_file_set_double_list (GKeyFile *key_file, const gchar *group_name,
*/
GKeyFile *gnc_key_file_load_from_file (const gchar *file,
gboolean ignore_error,
gboolean return_empty_struct);
gboolean return_empty_struct,
GError **caller_error);
/** Write a key/value file from memory to disk. If there is no data

View File

@ -402,20 +402,20 @@ static GtkTooltips *tips = NULL;
/************************************************************
* *
************************************************************/
#define WINDOW_COUNT "Window Count"
#define WINDOW_STRING "Window %d"
#define WINDOW_GEOMETRY "Window Geometry"
#define WINDOW_POSITION "Window Position"
#define WINDOW_MAXIMIZED "Window Maximized"
#define TOOLBAR_VISIBLE "Toolbar Visible"
#define STATUSBAR_VISIBLE "Statusbar Visible"
#define SUMMARYBAR_VISIBLE "Summarybar Visible"
#define WINDOW_FIRSTPAGE "First Page"
#define WINDOW_PAGECOUNT "Page Count"
#define WINDOW_PAGEORDER "Page Order"
#define PAGE_TYPE "Page Type"
#define PAGE_NAME "Page Name"
#define PAGE_STRING "Page %d"
#define WINDOW_COUNT "WindowCount"
#define WINDOW_STRING "Window %d"
#define WINDOW_GEOMETRY "WindowGeometry"
#define WINDOW_POSITION "WindowPosition"
#define WINDOW_MAXIMIZED "WindowMaximized"
#define TOOLBAR_VISIBLE "ToolbarVisible"
#define STATUSBAR_VISIBLE "StatusbarVisible"
#define SUMMARYBAR_VISIBLE "SummarybarVisible"
#define WINDOW_FIRSTPAGE "FirstPage"
#define WINDOW_PAGECOUNT "PageCount"
#define WINDOW_PAGEORDER "PageOrder"
#define PAGE_TYPE "PageType"
#define PAGE_NAME "PageName"
#define PAGE_STRING "Page %d"
typedef struct {
GKeyFile *key_file;

View File

@ -435,7 +435,7 @@ gnc_plugin_history_list_from_gnucash1 (void)
/* Copy the old values from the gnucash 1.x/gnome1 settings file to
* the gnucash 2.x/gconf settings area. */
mdi_file = g_build_filename(home, ".gnome", "GnuCash", (gchar *)NULL);
keyfile = gnc_key_file_load_from_file (mdi_file, FALSE, FALSE);
keyfile = gnc_key_file_load_from_file (mdi_file, FALSE, FALSE, NULL);
if (keyfile) {
keys = g_key_file_get_keys(keyfile, GNOME1_HISTORY, NULL, NULL);
if (keys) {

View File

@ -1853,12 +1853,12 @@ account_filter_dialog_create(AccountFilterDialog *fd, GncPluginPage *page)
LEAVE(" ");
}
#define ACCT_COUNT "Number of Open Accounts"
#define ACCT_OPEN "Open Account %d"
#define ACCT_SELECTED "Selected Account"
#define SHOW_HIDDEN "Show Hidden"
#define SHOW_ZERO "Show Zero Total"
#define ACCT_TYPES "Account Types"
#define ACCT_COUNT "NumberOfOpenAccounts"
#define ACCT_OPEN "OpenAccount%d"
#define ACCT_SELECTED "SelectedAccount"
#define SHOW_HIDDEN "ShowHidden"
#define SHOW_ZERO "ShowZeroTotal"
#define ACCT_TYPES "AccountTypes"
typedef struct foo {
GKeyFile *key_file;

View File

@ -1004,7 +1004,7 @@ read_one_check_format(PrintCheckDialog * pcd, const gchar *groupname,
check_format_t *format;
pathname = g_build_filename(dirname, file, (char *)NULL);
key_file = gnc_key_file_load_from_file(pathname, FALSE, FALSE);
key_file = gnc_key_file_load_from_file(pathname, FALSE, FALSE, NULL);
g_free(pathname);
if (!key_file) {
g_warning("Check file %s, cannot load file", file);

View File

@ -831,10 +831,10 @@ static const gchar *style_names[] = {
NULL
};
#define KEY_REGISTER_TYPE "Register Type"
#define KEY_ACCOUNT_NAME "Account Name"
#define KEY_REGISTER_STYLE "Register Style"
#define KEY_DOUBLE_LINE "Double Line Mode"
#define KEY_REGISTER_TYPE "RegisterType"
#define KEY_ACCOUNT_NAME "AccountName"
#define KEY_REGISTER_STYLE "RegisterStyle"
#define KEY_DOUBLE_LINE "DoubleLineMode"
#define LABEL_ACCOUNT "Account"
#define LABEL_SUBACCOUNT "SubAccount"

View File

@ -649,8 +649,8 @@ gnc_plugin_page_report_destroy_widget(GncPluginPage *plugin_page)
/** The key name used it the state file for storing the report
* options. */
#define SCHEME_OPTIONS "Scheme Options"
#define SCHEME_OPTIONS_N "Scheme Options %d"
#define SCHEME_OPTIONS "SchemeOptions"
#define SCHEME_OPTIONS_N "SchemeOptions%d"
/** Save enough information about this report page that it can be