"single file" backend
- From: Mark McLoughlin <mark skynet ie>
- To: hp redhat com
- Cc: michael ximian com, gconf-list gnome org
- Subject: "single file" backend
- Date: 06 Jul 2003 18:01:19 +0100
Hey,
So, at GUADEC Michael brought up the issue again of GConf having so
many little XML files scattered across the disk and there seemed to be
some agreement that it would be worth at least giving a backend which
read/writes to a single file a try.
I implemented this in the markup backend, but with a little twist.
Instead of a single file, you can just reduce the number of files used
by the backend - e.g. each app's configuration might be saved to a
single file ...
The way it works is that if a dir contains %gconf-tree.xml, then it is
assumed to contain the entire tree under that directory and, therefore,
%gconf.xml and any sub-directories on the filesystem is ingored. I've
also included a little utility to merge a part of the tree into a
single file ...
After all that, I benchmarked some of the possibilities with the simple
test case of dumping the entire GConf tree. Not necessarily the case we
are trying to speed up, but the results were quite interesting.
So, the four cases timed below are:
1) Using the XML backend
2) Using the markup backend, with a file for each dir
3) Using the markup backend with a single file for the
entire tree
4) Using the markup backend with a single file for each
subdir of /apps and /schemas/apps
Its pretty clear from the timings that (4) is marginally better than
(3) which is better than (2), but by far the biggest difference is
between the markup backend and the XML backend :-)
Anyway, it would be good to some better benchmarking of things like
application startup[1] with each case, especially on some slower
machines. You're pretty good a doing these kind of comparisons Michael
... :-)
Attached is the patch to the markup backend (which also fixes quite a
few things in the backend), the script I used for the benchmarks and the
results.
Good Luck,
Mark.
[1] - Amazingly, with case (4) - that's all I've tried so far - the
panel starts up as fast as the terminal ... and the panel easily took 3
to 4 times as long before that. That suggests to me that the panel is
doing something silly with GConf, though ...
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/gconf/backends/Makefile.am,v
retrieving revision 1.28
diff -u -p -r1.28 Makefile.am
--- Makefile.am 26 Apr 2003 07:21:09 -0000 1.28
+++ Makefile.am 6 Jul 2003 15:56:50 -0000
@@ -4,8 +4,7 @@ INCLUDES= -I$(top_srcdir) -I$(top_buildd
backenddir = $(pkglibdir)/$(MAJOR_VERSION)
-backend_LTLIBRARIES = libgconfbackend-xml.la
-noinst_LTLIBRARIES = libgconfbackend-markup.la
+backend_LTLIBRARIES = libgconfbackend-xml.la libgconfbackend-markup.la
libgconfbackend_xml_la_SOURCES = \
xml-cache.h \
@@ -31,3 +30,7 @@ noinst_PROGRAMS = xml-test
xml_test_SOURCES= $(libgconfbackend_xml_la_SOURCES) xml-test.c
xml_test_LDADD = $(DEPENDENT_WITH_XML_LIBS) $(top_builddir)/gconf/libgconf-$(MAJOR_VERSION).la
+
+bin_PROGRAMS = gconf-merge-tree
+gconf_merge_tree_SOURCES = gconf-merge-tree.c
+gconf_merge_tree_LDADD = $(DEPENDENT_LIBS) $(top_builddir)/gconf/libgconf-$(MAJOR_VERSION).la
Index: gconf-merge-tree.c
===================================================================
RCS file: gconf-merge-tree.c
diff -N gconf-merge-tree.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gconf-merge-tree.c 6 Jul 2003 15:56:50 -0000
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2001 Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Mark McLoughlin <mark skynet ie>
+ */
+
+#include "markup-tree.c"
+
+guint
+_gconf_mode_t_to_mode (mode_t orig)
+{
+ /* I don't think this is portable. */
+ guint mode = 0;
+ guint fullmask = S_IRWXG | S_IRWXU | S_IRWXO;
+
+ mode = orig & fullmask;
+
+ g_return_val_if_fail (mode <= 0777, 0700);
+
+ return mode;
+}
+
+static void
+recursively_load_entire_tree (MarkupDir *dir)
+{
+ GSList *tmp;
+
+ load_entries (dir);
+ load_subdirs (dir);
+
+ tmp = dir->subdirs;
+ while (tmp != NULL)
+ {
+ MarkupDir *subdir = tmp->data;
+
+ recursively_load_entire_tree (subdir);
+
+ tmp = tmp->next;
+ }
+}
+
+static gboolean
+merge_tree (const char *root_dir)
+{
+ struct stat statbuf;
+ guint dir_mode;
+ guint file_mode;
+ MarkupTree *tree;
+ GError *error;
+
+ if (stat (root_dir, &statbuf) == 0)
+ {
+ dir_mode = _gconf_mode_t_to_mode (statbuf.st_mode);
+ /* dir_mode without search bits */
+ file_mode = dir_mode & (~0111);
+ }
+ else
+ {
+ fprintf (stderr, _("Cannot find directory %s\n"), root_dir);
+ return FALSE;
+
+ }
+
+ tree = markup_tree_get (root_dir, dir_mode, file_mode);
+
+ recursively_load_entire_tree (tree->root);
+
+ error = NULL;
+ save_tree (tree->root, TRUE, file_mode, &error);
+ if (error)
+ {
+ char *markup_file;
+
+ markup_file = markup_dir_build_path (tree->root, TRUE, TRUE);
+ fprintf (stderr, _("Error saving GConf tree to '%s': %s\n"),
+ markup_file,
+ error->message);
+ g_error_free (error);
+ g_free (markup_file);
+ markup_tree_unref (tree);
+ return FALSE;
+ }
+
+ tree->root->entries_need_save = FALSE;
+ tree->root->some_subdir_needs_sync = FALSE;
+
+ markup_tree_unref (tree);
+
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+ _gconf_init_i18n ();
+ textdomain (GETTEXT_PACKAGE);
+
+ if (argc != 2)
+ {
+ fprintf (stderr, _("Usage: %s <dir>\n"), argv [0]);
+ return 1;
+ }
+
+ if (!strcmp (argv [1], "--help"))
+ {
+ printf (_("Usage: %s <dir>\n"
+ " Merges a markup backend filesystem hierarchy like:\n"
+ " dir/%%gconf.xml\n"
+ " subdir1/%%gconf.xml\n"
+ " subdir2/%%gconf.xml\n"
+ " to:\n"
+ " dir/%%gconf-tree.xml\n"), argv [0]);
+ return 0;
+ }
+
+ return !merge_tree (argv [1]);
+}
+
Index: markup-tree.c
===================================================================
RCS file: /cvs/gnome/gconf/backends/markup-tree.c,v
retrieving revision 1.6
diff -u -p -r1.6 markup-tree.c
--- markup-tree.c 31 May 2003 15:31:36 -0000 1.6
+++ markup-tree.c 6 Jul 2003 15:56:51 -0000
@@ -57,14 +57,16 @@ static LocalSchemaInfo* local_schema_inf
static void local_schema_info_free (LocalSchemaInfo *info);
-static MarkupDir* markup_dir_new (MarkupTree *tree,
- MarkupDir *parent,
- const char *name);
-static void markup_dir_free (MarkupDir *dir);
-static gboolean markup_dir_needs_sync (MarkupDir *dir);
-static gboolean markup_dir_sync (MarkupDir *dir);
-static char* markup_dir_build_path (MarkupDir *dir,
- gboolean with_data_file);
+static MarkupDir* markup_dir_new (MarkupTree *tree,
+ MarkupDir *parent,
+ const char *name);
+static void markup_dir_free (MarkupDir *dir);
+static gboolean markup_dir_needs_sync (MarkupDir *dir);
+static gboolean markup_dir_sync (MarkupDir *dir);
+static char* markup_dir_build_path (MarkupDir *dir,
+ gboolean with_data_file,
+ gboolean subtree_data_file);
+static void markup_dir_flag_entries_need_save (MarkupDir *dir);
static MarkupEntry* markup_entry_new (MarkupDir *dir,
const char *name);
@@ -74,12 +76,13 @@ static void markup_entry_set_mod
static void markup_entry_set_mod_time (MarkupEntry *entry,
GTime mtime);
-static GSList* parse_entries (const char *filename,
- GError **err);
-static gboolean save_entries (const char *filename,
- guint file_mode,
- GSList *entries,
- GError **err);
+static void parse_tree (MarkupDir *root,
+ gboolean parse_subtree,
+ GError **err);
+static void save_tree (MarkupDir *root,
+ gboolean save_as_subtree,
+ guint file_mode,
+ GError **err);
struct _MarkupTree
@@ -158,7 +161,7 @@ markup_tree_unref (MarkupTree *tree)
void
markup_tree_rebuild (MarkupTree *tree)
{
- g_return_if_fail (markup_dir_needs_sync (tree->root));
+ g_return_if_fail (!markup_dir_needs_sync (tree->root));
markup_dir_free (tree->root);
tree->root = markup_dir_new (tree, NULL, "/");
@@ -188,6 +191,12 @@ struct _MarkupDir
/* We are pretty sure the filesystem dir exists */
guint filesystem_dir_probably_exists : 1;
+
+ /* Not represented by an actual filesystem dir */
+ guint not_in_filesystem : 1;
+
+ /* Save to %gconf-tree.xml when syncing */
+ guint save_as_subtree : 1;
};
static MarkupDir*
@@ -209,8 +218,28 @@ markup_dir_new (MarkupTree *tree,
static void
markup_dir_free (MarkupDir *dir)
{
- /* FIXME free the entries */
-
+ GSList *tmp;
+
+ tmp = dir->entries;
+ while (tmp)
+ {
+ MarkupEntry *entry = tmp->data;
+
+ markup_entry_free (entry);
+
+ tmp = tmp->next;
+ }
+
+ tmp = dir->subdirs;
+ while (tmp)
+ {
+ MarkupDir *subdir = tmp->data;
+
+ markup_dir_free (subdir);
+
+ tmp = tmp->next;
+ }
+
g_free (dir->name);
g_free (dir);
@@ -325,34 +354,27 @@ markup_tree_sync (MarkupTree *tree,
}
static gboolean
-load_entries (MarkupDir *dir)
+load_subtree (MarkupDir *dir)
{
- /* Load the entries in this directory */
+ /* Load subtree from %gconf-tree.xml if exists */
+
+ GError *tmp_err = NULL;
char *markup_file;
- GSList *entries;
- GError *tmp_err;
- GSList *tmp;
-
- if (dir->entries_loaded)
- return TRUE;
- /* We mark it loaded even if the next stuff
- * fails, because we don't want to keep trying and
- * failing, plus we have invariants
- * that assume entries_loaded is TRUE once we've
- * called load_entries()
- */
- dir->entries_loaded = TRUE;
-
- markup_file = markup_dir_build_path (dir, TRUE);
+ markup_file = markup_dir_build_path (dir, TRUE, TRUE);
+ if (!gconf_file_exists (markup_file))
+ {
+ g_free (markup_file);
+ return FALSE;
+ }
- tmp_err = NULL;
- entries = parse_entries (markup_file, &tmp_err);
-
+ dir->subdirs_loaded = TRUE;
+ dir->entries_loaded = TRUE;
+ dir->save_as_subtree = TRUE;
+
+ parse_tree (dir, TRUE, &tmp_err);
if (tmp_err)
{
- g_assert (entries == NULL);
-
/* note that tmp_err may be a G_MARKUP_ERROR while only
* GCONF_ERROR could be returned, so if we decide to return this
* error someday we need to fix it up first
@@ -362,29 +384,58 @@ load_entries (MarkupDir *dir)
* when creating a new directory
*/
gconf_log (GCL_DEBUG,
- "Failed to load file \"%s\": %s",
- markup_file, tmp_err->message);
+ "Failed to load file \"%s\": %s",
+ markup_file, tmp_err->message);
g_error_free (tmp_err);
- g_free (markup_file);
- return FALSE;
}
g_free (markup_file);
- g_assert (dir->entries == NULL);
- dir->entries = entries;
+ return TRUE;
+}
- /* Fill in entry->dir */
- tmp = dir->entries;
- while (tmp != NULL)
+static gboolean
+load_entries (MarkupDir *dir)
+{
+ /* Load the entries in this directory */
+
+ if (dir->entries_loaded)
+ return TRUE;
+
+ /* We mark it loaded even if the next stuff
+ * fails, because we don't want to keep trying and
+ * failing, plus we have invariants
+ * that assume entries_loaded is TRUE once we've
+ * called load_entries()
+ */
+ dir->entries_loaded = TRUE;
+
+ if (!load_subtree (dir))
{
- MarkupEntry *entry = tmp->data;
+ GError *tmp_err = NULL;
- entry->dir = dir;
-
- tmp = tmp->next;
+ parse_tree (dir, FALSE, &tmp_err);
+ if (tmp_err)
+ {
+ char *markup_file;
+
+ /* note that tmp_err may be a G_MARKUP_ERROR while only
+ * GCONF_ERROR could be returned, so if we decide to return this
+ * error someday we need to fix it up first
+ */
+
+ /* this message is debug-only because it usually happens
+ * when creating a new directory
+ */
+ markup_file = markup_dir_build_path (dir, TRUE, FALSE);
+ gconf_log (GCL_DEBUG,
+ "Failed to load file \"%s\": %s",
+ markup_file, tmp_err->message);
+ g_error_free (tmp_err);
+ g_free (markup_file);
+ }
}
-
+
return TRUE;
}
@@ -414,7 +465,10 @@ load_subdirs (MarkupDir *dir)
g_assert (dir->subdirs == NULL);
- markup_dir = markup_dir_build_path (dir, FALSE);
+ if (load_subtree (dir))
+ return TRUE;
+
+ markup_dir = markup_dir_build_path (dir, FALSE, FALSE);
dp = opendir (markup_dir);
@@ -536,10 +590,9 @@ markup_dir_ensure_entry (MarkupDir *di
/* Create a new entry */
entry = markup_entry_new (dir, relative_key);
- dir->entries = g_slist_prepend (dir->entries, entry);
/* Need to save this */
- dir->entries_need_save = TRUE;
+ markup_dir_flag_entries_need_save (dir);
markup_dir_queue_sync (dir);
return entry;
@@ -589,7 +642,7 @@ markup_dir_ensure_subdir (MarkupDir *d
g_return_val_if_fail (dir->subdirs_loaded, NULL);
subdir = markup_dir_new (dir->tree, dir, relative_key);
- subdir->entries_need_save = TRUE; /* so we save empty %gconf.xml */
+ markup_dir_flag_entries_need_save (subdir); /* so we save empty %gconf.xml */
/* we don't need to load stuff, since we know the dir didn't exist */
subdir->entries_loaded = TRUE;
@@ -633,6 +686,22 @@ markup_dir_needs_sync (MarkupDir *dir)
}
static void
+markup_dir_flag_entries_need_save (MarkupDir *dir)
+{
+
+ if (!dir->not_in_filesystem)
+ dir->entries_need_save = TRUE;
+
+ else
+ {
+ /* root must be a filesystem dir */
+ g_assert (dir->parent);
+
+ markup_dir_flag_entries_need_save (dir->parent);
+ }
+}
+
+static void
markup_entry_clean_old_local_schemas (MarkupEntry *entry)
{
@@ -720,28 +789,32 @@ delete_useless_subdirs (MarkupDir *dir)
subdir->entries == NULL &&
subdir->subdirs == NULL)
{
- char *fs_dirname;
- char *fs_filename;
-
- fs_dirname = markup_dir_build_path (subdir, FALSE);
- fs_filename = markup_dir_build_path (subdir, TRUE);
+
+ if (!dir->not_in_filesystem)
+ {
+ char *fs_dirname;
+ char *fs_filename;
- if (unlink (fs_filename) < 0)
- {
- gconf_log (GCL_WARNING,
- _("Could not remove \"%s\": %s\n"),
- fs_filename, strerror (errno));
- }
+ fs_dirname = markup_dir_build_path (subdir, FALSE, FALSE);
+ fs_filename = markup_dir_build_path (subdir, TRUE, dir->save_as_subtree);
- if (rmdir (fs_dirname) < 0)
- {
- gconf_log (GCL_WARNING,
- _("Could not remove \"%s\": %s\n"),
- fs_dirname, strerror (errno));
- }
+ if (unlink (fs_filename) < 0)
+ {
+ gconf_log (GCL_WARNING,
+ _("Could not remove \"%s\": %s\n"),
+ fs_filename, strerror (errno));
+ }
+
+ if (rmdir (fs_dirname) < 0)
+ {
+ gconf_log (GCL_WARNING,
+ _("Could not remove \"%s\": %s\n"),
+ fs_dirname, strerror (errno));
+ }
- g_free (fs_dirname);
- g_free (fs_filename);
+ g_free (fs_dirname);
+ g_free (fs_filename);
+ }
markup_dir_free (subdir);
@@ -759,7 +832,30 @@ delete_useless_subdirs (MarkupDir *dir)
dir->subdirs = g_slist_reverse (kept_subdirs);
return some_deleted;
-}
+}
+
+static gboolean
+delete_useless_subdirs_recurse (MarkupDir *dir)
+{
+ GSList *tmp;
+ gboolean retval = FALSE;
+
+ tmp = dir->subdirs;
+ while (tmp)
+ {
+ MarkupDir *subdir = tmp->data;
+
+ if (delete_useless_subdirs (subdir))
+ retval = TRUE;
+
+ tmp = tmp->next;
+ }
+
+ if (delete_useless_subdirs (dir))
+ retval = TRUE;
+
+ return retval;
+}
static gboolean
delete_useless_entries (MarkupDir *dir)
@@ -805,6 +901,7 @@ markup_dir_sync (MarkupDir *dir)
{
char *fs_dirname;
char *fs_filename;
+ char *fs_subtree;
GSList *tmp;
gboolean some_useless_entries;
gboolean some_useless_subdirs;
@@ -812,25 +909,14 @@ markup_dir_sync (MarkupDir *dir)
some_useless_entries = FALSE;
some_useless_subdirs = FALSE;
-#if 0
- {
- MarkupDir *parent;
-
- parent = dir->parent;
- while (parent)
- {
- fputs (" ", stdout);
- parent = parent->parent;
- }
-
- g_print ("%s\n", dir->name);
- }
-#endif
-
/* We assume our parent directories have all been synced, before
* we are synced. So we don't need to mkdir() parent directories.
*/
+ /* We must have been synced already */
+ if (dir->not_in_filesystem)
+ return TRUE;
+
/* Sanitize the entries */
tmp = dir->entries;
while (tmp != NULL)
@@ -842,8 +928,9 @@ markup_dir_sync (MarkupDir *dir)
tmp = tmp->next;
}
- fs_dirname = markup_dir_build_path (dir, FALSE);
- fs_filename = markup_dir_build_path (dir, TRUE);
+ fs_dirname = markup_dir_build_path (dir, FALSE, FALSE);
+ fs_filename = markup_dir_build_path (dir, TRUE, FALSE);
+ fs_subtree = markup_dir_build_path (dir, TRUE, TRUE);
/* For a dir to be loaded as a subdir, it must have a
* %gconf.xml file, even if it has no entries in that
@@ -851,7 +938,8 @@ markup_dir_sync (MarkupDir *dir)
* even though the entry list is initially empty.
*/
- if (dir->entries_need_save)
+ if (dir->entries_need_save ||
+ (dir->some_subdir_needs_sync && dir->save_as_subtree))
{
GError *err;
@@ -869,22 +957,24 @@ markup_dir_sync (MarkupDir *dir)
/* Now write the file */
err = NULL;
- save_entries (fs_filename, dir->tree->file_mode, dir->entries, &err);
+ save_tree (dir, dir->save_as_subtree, dir->tree->file_mode, &err);
if (err != NULL)
{
gconf_log (GCL_WARNING,
_("Failed to write \"%s\": %s\n"),
- fs_filename, err->message);
+ !dir->save_as_subtree ? fs_filename : fs_subtree, err->message);
g_error_free (err);
}
else
{
dir->entries_need_save = FALSE;
+ if (dir->save_as_subtree)
+ dir->some_subdir_needs_sync = FALSE;
}
}
- if (dir->some_subdir_needs_sync)
+ if (dir->some_subdir_needs_sync && !dir->save_as_subtree)
{
GSList *tmp;
gboolean one_failed;
@@ -929,12 +1019,24 @@ markup_dir_sync (MarkupDir *dir)
* we're deleting our subdirs, the root dir (tree->root)
* never gets deleted - this is intentional.
*/
- if (delete_useless_subdirs (dir))
- some_useless_subdirs = TRUE;
+
+ if (!dir->save_as_subtree)
+ {
+ if (delete_useless_subdirs (dir))
+ some_useless_subdirs = TRUE;
+ }
+ else
+ {
+ /* We haven't recursively synced subdirs so we need
+ * to now recursively delete useless subdirs.
+ */
+ if (delete_useless_subdirs_recurse (dir))
+ some_useless_subdirs = TRUE;
+ }
g_free (fs_dirname);
g_free (fs_filename);
-
+ g_free (fs_subtree);
/* If we deleted an entry or subdir from this directory, and hadn't
* fully loaded this directory, we now don't know whether the entry
@@ -958,7 +1060,8 @@ markup_dir_sync (MarkupDir *dir)
static char*
markup_dir_build_path (MarkupDir *dir,
- gboolean with_data_file)
+ gboolean with_data_file,
+ gboolean subtree_data_file)
{
GString *name;
GSList *components;
@@ -988,7 +1091,7 @@ markup_dir_build_path (MarkupDir *dir,
g_slist_free (components);
if (with_data_file)
- g_string_append (name, "/%gconf.xml");
+ g_string_append (name, !subtree_data_file ? "/%gconf.xml" : "/%gconf-tree.xml");
return g_string_free (name, FALSE);
}
@@ -1005,10 +1108,10 @@ markup_entry_new (MarkupDir *dir,
entry = g_new0 (MarkupEntry, 1);
- /* "dir" may be NULL during %gconf.xml parsing */
+ entry->name = g_strdup (name);
entry->dir = dir;
- entry->name = g_strdup (name);
+ dir->entries = g_slist_prepend (dir->entries, entry);
return entry;
}
@@ -1169,7 +1272,7 @@ markup_entry_set_value (MarkupEntry
entry->mod_time = time (NULL);
/* Need to save to disk */
- entry->dir->entries_need_save = TRUE;
+ markup_dir_flag_entries_need_save (entry->dir);
markup_dir_queue_sync (entry->dir);
}
@@ -1239,7 +1342,7 @@ markup_entry_unset_value (MarkupEntry *e
entry->mod_time = time (NULL);
/* Need to save to disk */
- entry->dir->entries_need_save = TRUE;
+ markup_dir_flag_entries_need_save (entry->dir);
markup_dir_queue_sync (entry->dir);
}
@@ -1263,7 +1366,7 @@ markup_entry_set_schema_name (MarkupEntr
entry->mod_time = time (NULL);
/* Need to save to disk */
- entry->dir->entries_need_save = TRUE;
+ markup_dir_flag_entries_need_save (entry->dir);
markup_dir_queue_sync (entry->dir);
}
@@ -1443,6 +1546,7 @@ typedef enum
{
STATE_START,
STATE_GCONF,
+ STATE_DIR,
STATE_ENTRY,
STATE_STRINGVALUE,
STATE_LONGDESC,
@@ -1462,16 +1566,19 @@ typedef enum
typedef struct
{
- GSList *states;
+ GSList *states;
+
+ MarkupDir *root;
+ GSList *dir_stack;
+
MarkupEntry *current_entry;
- GSList *value_stack;
- GSList *value_freelist;
+ GSList *value_stack;
+ GSList *value_freelist;
/* Collected while parsing a schema entry */
- GSList *local_schemas;
+ GSList *local_schemas;
- GSList *complete_entries;
-
+ guint allow_subdirs : 1;
} ParseInfo;
static void set_error (GError **err,
@@ -1480,7 +1587,9 @@ static void set_error (GError
const char *format,
...) G_GNUC_PRINTF (4, 5);
-static void parse_info_init (ParseInfo *info);
+static void parse_info_init (ParseInfo *info,
+ MarkupDir *root,
+ gboolean allow_subdirs);
static void parse_info_free (ParseInfo *info);
static void push_state (ParseInfo *info,
@@ -1492,7 +1601,11 @@ static void value_stack_push (Par
GConfValue *value,
gboolean add_to_freelist);
static GConfValue* value_stack_peek (ParseInfo *info);
-static void value_stack_pop (ParseInfo *info);
+static GConfValue* value_stack_pop (ParseInfo *info);
+static void dir_stack_push (ParseInfo *info,
+ MarkupDir *dir);
+static MarkupDir* dir_stack_peek (ParseInfo *info);
+static MarkupDir* dir_stack_pop (ParseInfo *info);
static void start_element_handler (GMarkupParseContext *context,
@@ -1544,32 +1657,38 @@ set_error (GError **err,
}
static void
-parse_info_init (ParseInfo *info)
+parse_info_init (ParseInfo *info,
+ MarkupDir *root,
+ gboolean allow_subdirs)
{
info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
+
+ info->root = root;
+ info->dir_stack = NULL;
+
info->current_entry = NULL;
info->value_stack = NULL;
info->value_freelist = NULL;
+
info->local_schemas = NULL;
- info->complete_entries = NULL;
+
+ info->allow_subdirs = allow_subdirs != FALSE;
+
+ dir_stack_push (info, root);
}
static void
parse_info_free (ParseInfo *info)
{
- if (info->current_entry)
- markup_entry_free (info->current_entry);
+ g_slist_free (info->dir_stack);
+
+ /* Don't free current_entry - always owned by tree */
g_slist_foreach (info->local_schemas,
(GFunc) local_schema_info_free,
NULL);
g_slist_free (info->local_schemas);
- g_slist_foreach (info->complete_entries,
- (GFunc) markup_entry_free,
- NULL);
- g_slist_free (info->complete_entries);
-
/* only free values on the freelist, not those on the stack,
* but all values in the freelist are also in the stack.
*/
@@ -1623,13 +1742,45 @@ value_stack_peek (ParseInfo *info)
return info->value_stack ? info->value_stack->data : NULL;
}
-static void
+static GConfValue*
value_stack_pop (ParseInfo *info)
{
- info->value_freelist = g_slist_remove (info->value_freelist,
- info->value_stack->data);
- info->value_stack = g_slist_remove (info->value_stack,
- info->value_stack->data);
+ GConfValue *retval;
+
+ retval = info->value_stack ? info->value_stack->data : NULL;
+
+ info->value_freelist = g_slist_remove (info->value_freelist, retval);
+ info->value_stack = g_slist_remove (info->value_stack, retval);
+
+ return retval;
+}
+
+static void
+dir_stack_push (ParseInfo *info,
+ MarkupDir *dir)
+{
+ info->dir_stack = g_slist_prepend (info->dir_stack, dir);
+}
+
+static MarkupDir*
+dir_stack_peek (ParseInfo *info)
+{
+ g_return_val_if_fail (info->dir_stack != NULL, NULL);
+
+ return info->dir_stack->data;
+}
+
+static MarkupDir*
+dir_stack_pop (ParseInfo *info)
+{
+ MarkupDir *retval;
+
+ g_return_val_if_fail (info->dir_stack != NULL, NULL);
+
+ retval = info->dir_stack->data;
+ info->dir_stack = g_slist_remove (info->dir_stack, retval);
+
+ return retval;
}
#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
@@ -1691,9 +1842,6 @@ locate_attributes (GMarkupParseContext *
va_end (args);
- if (!retval)
- return retval;
-
i = 0;
while (attribute_names[i])
{
@@ -2204,7 +2352,7 @@ parse_entry_element (GMarkupParseContext
GConfValue *value;
GError *tmp_err;
- g_return_if_fail (peek_state (info) == STATE_GCONF);
+ g_return_if_fail (peek_state (info) == STATE_GCONF || peek_state (info) == STATE_DIR);
g_return_if_fail (ELEMENT_IS ("entry"));
g_return_if_fail (info->current_entry == NULL);
@@ -2266,7 +2414,7 @@ parse_entry_element (GMarkupParseContext
g_error_free (tmp_err);
}
- info->current_entry = markup_entry_new (NULL, name);
+ info->current_entry = markup_entry_new (dir_stack_peek (info), name);
if (value != NULL)
{
info->current_entry->value = value;
@@ -2274,8 +2422,8 @@ parse_entry_element (GMarkupParseContext
}
if (muser)
- markup_entry_set_mod_user (info->current_entry,
- muser);
+ markup_entry_set_mod_user (info->current_entry, muser);
+
if (mtime)
{
GTime vmtime;
@@ -2283,7 +2431,7 @@ parse_entry_element (GMarkupParseContext
vmtime = gconf_string_to_gulong (mtime);
markup_entry_set_mod_time (info->current_entry,
- vmtime);
+ vmtime);
}
/* don't use markup_entry_set_schema_name because it would
@@ -2294,6 +2442,51 @@ parse_entry_element (GMarkupParseContext
}
static void
+parse_dir_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParseInfo *info,
+ GError **error)
+{
+ MarkupDir *dir;
+ MarkupDir *subdir;
+ const char *name;
+
+ g_return_if_fail (peek_state (info) == STATE_GCONF || peek_state (info) == STATE_DIR);
+ g_return_if_fail (ELEMENT_IS ("dir"));
+
+ push_state (info, STATE_DIR);
+
+ name = NULL;
+
+ if (!locate_attributes (context, element_name, attribute_names, attribute_values,
+ error,
+ "name", &name,
+ NULL))
+ return;
+
+ if (name == NULL)
+ {
+ set_error (error, context, GCONF_ERROR_PARSE_ERROR,
+ _("No \"%s\" attribute on element <%s>"),
+ "name", element_name);
+ return;
+ }
+
+ dir = dir_stack_peek (info);
+
+ subdir = markup_dir_new (info->root->tree, dir, name);
+ dir->subdirs = g_slist_prepend (dir->subdirs, subdir);
+
+ subdir->not_in_filesystem = TRUE;
+ subdir->entries_loaded = TRUE;
+ subdir->subdirs_loaded = TRUE;
+
+ dir_stack_push (info, subdir);
+}
+
+static void
parse_local_schema_child_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
@@ -2675,6 +2868,7 @@ start_element_handler (GMarkupParseConte
element_name);
break;
+ case STATE_DIR:
case STATE_GCONF:
if (ELEMENT_IS ("entry"))
{
@@ -2682,10 +2876,16 @@ start_element_handler (GMarkupParseConte
attribute_names, attribute_values,
info, error);
}
+ else if (ELEMENT_IS ("dir") && info->allow_subdirs)
+ {
+ parse_dir_element (context, element_name,
+ attribute_names, attribute_values,
+ info, error);
+ }
else
set_error (error, context, GCONF_ERROR_PARSE_ERROR,
_("Element <%s> is not allowed inside a <%s> element"),
- element_name, "gconf");
+ element_name, current_state == STATE_GCONF ? "gconf" : "dir");
break;
case STATE_ENTRY:
@@ -2734,14 +2934,12 @@ end_element_handler (GMarkupParseContext
g_assert (info->current_entry);
g_assert (info->current_entry->local_schemas == NULL);
- info->current_entry->local_schemas = info->local_schemas;
+ info->current_entry->local_schemas = g_slist_reverse (info->local_schemas);
info->local_schemas = NULL;
if (info->current_entry->value != NULL)
value_stack_pop (info);
- info->complete_entries = g_slist_prepend (info->complete_entries,
- info->current_entry);
info->current_entry = NULL;
pop_state (info);
@@ -2772,7 +2970,19 @@ end_element_handler (GMarkupParseContext
pop_state (info);
break;
+ case STATE_DIR:
case STATE_GCONF:
+ {
+ MarkupDir *dir;
+
+ dir = dir_stack_pop (info);
+ dir->entries = g_slist_reverse (dir->entries);
+ dir->subdirs = g_slist_reverse (dir->subdirs);
+
+ pop_state (info);
+ }
+ break;
+
case STATE_LOCAL_SCHEMA:
case STATE_LONGDESC:
case STATE_STRINGVALUE:
@@ -2848,6 +3058,9 @@ text_handler (GMarkupParseContext *conte
case STATE_GCONF:
NO_TEXT ("gconf");
break;
+ case STATE_DIR:
+ NO_TEXT ("dir");
+ break;
case STATE_ENTRY:
NO_TEXT ("entry");
break;
@@ -2869,13 +3082,15 @@ text_handler (GMarkupParseContext *conte
}
}
-static GSList*
-parse_entries (const char *filename,
- GError **err)
+static void
+parse_tree (MarkupDir *root,
+ gboolean parse_subtree,
+ GError **err)
{
GMarkupParseContext *context;
GError *error;
ParseInfo info;
+ char *filename;
char *text;
int length;
GSList *retval;
@@ -2884,30 +3099,34 @@ parse_entries (const char *filename,
length = 0;
retval = NULL;
- error = NULL;
- if (!g_file_get_contents (filename,
- &text,
- &length,
- &error))
- {
- g_propagate_error (err, error);
- return NULL;
- }
+ filename = markup_dir_build_path (root, TRUE, parse_subtree);
+
+ parse_info_init (&info, root, parse_subtree);
- g_assert (text);
+ text = NULL;
+ length = 0;
+ error = NULL;
+ if (!g_file_get_contents (filename, &text, &length, &error))
+ goto out;
/* Empty documents are OK */
if (length == 0)
- {
- g_free (text);
- return NULL;
- }
-
- parse_info_init (&info);
+ goto out;
+
+ g_assert (text);
context = g_markup_parse_context_new (&gconf_parser,
0, &info, NULL);
+ /* FIXME: if we encounter some error while parsing we
+ * soldier on with and return whatever we did
+ * manage to parse. Is this behavious okay, or
+ * do we want to go back to the previous behaviour
+ * of ignoring what we managed to parse ?
+ * Hmm, did the previous behaviour cause the
+ * problem file to be deleted. Maybe that *is*
+ * what we want.
+ */
error = NULL;
if (!g_markup_parse_context_parse (context,
text,
@@ -2921,53 +3140,38 @@ parse_entries (const char *filename,
g_markup_parse_context_free (context);
- goto out;
-
out:
+ g_free (filename);
g_free (text);
-
- if (error)
- {
- g_propagate_error (err, error);
- }
- else if (info.complete_entries)
- {
- retval = info.complete_entries;
- info.complete_entries = NULL;
- }
- else
- {
- /* No entries in here, but that's not an error,
- * empty files are allowed.
- */
- }
-
parse_info_free (&info);
- return retval;
+ if (error)
+ g_propagate_error (err, error);
}
/*
* Save
*/
+#define INDENT_SPACES 8
+
static gboolean write_list_children (GConfValue *value,
FILE *f,
- const char *indent);
+ int indent);
static gboolean write_pair_children (GConfValue *value,
FILE *f,
- const char *indent);
+ int indent);
static gboolean write_schema_children (GConfValue *value,
GSList *local_schemas,
FILE *f,
- const char *indent);
+ int indent);
static gboolean
write_value_element (GConfValue *value,
GSList *local_schemas,
FILE *f,
- const char *indent)
+ int indent)
{
/* We are at the "<foo bar="whatever"" stage here,
* <foo> still missing the closing >
@@ -2983,6 +3187,9 @@ write_value_element (GConfValue *value,
if (fprintf (f, " ltype=\"%s\"",
gconf_value_type_to_string (gconf_value_get_list_type (value))) < 0)
return FALSE;
+
+ if (fputs (">\n", f) < 0)
+ return FALSE;
break;
case GCONF_VALUE_SCHEMA:
@@ -3050,17 +3257,20 @@ write_value_element (GConfValue *value,
return FALSE;
}
}
+
+ if (fputs (">\n", f) < 0)
+ return FALSE;
}
break;
case GCONF_VALUE_INT:
- if (fprintf (f, "value=\"%d\"",
+ if (fprintf (f, " value=\"%d\"",
gconf_value_get_int (value)) < 0)
return FALSE;
break;
case GCONF_VALUE_BOOL:
- if (fprintf (f, "value=\"%s\"",
+ if (fprintf (f, " value=\"%s\"",
gconf_value_get_bool (value) ? "true" : "false") < 0)
return FALSE;
break;
@@ -3070,7 +3280,7 @@ write_value_element (GConfValue *value,
char *s;
s = gconf_double_to_string (gconf_value_get_float (value));
- if (fprintf (f, "value=\"%s\"", s) < 0)
+ if (fprintf (f, " value=\"%s\"", s) < 0)
{
g_free (s);
return FALSE;
@@ -3082,28 +3292,31 @@ write_value_element (GConfValue *value,
case GCONF_VALUE_INVALID:
case GCONF_VALUE_STRING:
case GCONF_VALUE_PAIR:
+ if (fputs (">\n", f) < 0)
+ return FALSE;
break;
}
- if (fputs (">\n", f) < 0)
- return FALSE;
-
switch (value->type)
{
case GCONF_VALUE_STRING:
{
char *s;
+ char *whitespace;
s = g_markup_escape_text (gconf_value_get_string (value),
-1);
+ whitespace = g_strnfill (indent, ' ');
if (fprintf (f, "%s<stringvalue>%s</stringvalue>\n",
- indent, s) < 0)
+ whitespace, s) < 0)
{
+ g_free (whitespace);
g_free (s);
return FALSE;
}
+ g_free (whitespace);
g_free (s);
}
break;
@@ -3136,102 +3349,157 @@ write_value_element (GConfValue *value,
static gboolean
write_list_children (GConfValue *value,
FILE *f,
- const char *indent)
+ int indent)
{
GSList *tmp;
+ gboolean retval = FALSE;
+ char *whitespace;
+
+ whitespace = g_strnfill (indent, ' ');
tmp = gconf_value_get_list (value);
while (tmp != NULL)
{
GConfValue *li = tmp->data;
- if (fputs (indent, f) < 0)
- return FALSE;
+ if (fputs (whitespace, f) < 0)
+ goto out;
if (fputs ("<li", f) < 0)
- return FALSE;
+ goto out;
- if (!write_value_element (li, NULL, f, indent))
- return FALSE;
+ if (!write_value_element (li, NULL, f, indent + INDENT_SPACES))
+ goto out;
- if (fprintf (f, "%s</li>", indent) < 0)
- return FALSE;
+ switch (li->type)
+ {
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_FLOAT:
+ if (fputs ("/>", f) < 0)
+ goto out;
+ break;
+ default:
+ if (fprintf (f, "%s</li>\n", whitespace) < 0)
+ goto out;
+ break;
+ }
tmp = tmp->next;
}
- return TRUE;
+ retval = TRUE;
+
+ out:
+
+ g_free (whitespace);
+
+ return retval;
}
static gboolean
write_pair_children (GConfValue *value,
FILE *f,
- const char *indent)
+ int indent)
{
GConfValue *child;
+ gboolean retval = FALSE;
+ char *whitespace;
+
+ whitespace = g_strnfill (indent, ' ');
child = gconf_value_get_car (value);
if (child != NULL)
{
- if (fputs (indent, f) < 0)
- return FALSE;
-
+ if (fputs (whitespace, f) < 0)
+ goto out;
+
if (fputs ("<car", f) < 0)
- return FALSE;
+ goto out;
- if (!write_value_element (child, NULL, f, indent))
- return FALSE;
-
- if (fprintf (f, "%s</car>", indent) < 0)
- return FALSE;
+ if (!write_value_element (child, NULL, f, indent + INDENT_SPACES))
+ goto out;
+
+ switch (child->type)
+ {
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_FLOAT:
+ if (fputs ("/>", f) < 0)
+ goto out;
+ break;
+ default:
+ if (fprintf (f, "%s</car>\n", whitespace) < 0)
+ goto out;
+ break;
+ }
}
child = gconf_value_get_cdr (value);
if (child != NULL)
{
- if (fputs (indent, f) < 0)
- return FALSE;
+ if (fputs (whitespace, f) < 0)
+ goto out;
if (fputs ("<cdr", f) < 0)
- return FALSE;
+ goto out;
- if (!write_value_element (child, NULL, f, indent))
- return FALSE;
+ if (!write_value_element (child, NULL, f, indent + INDENT_SPACES))
+ goto out;
- if (fprintf (f, "%s</cdr>", indent) < 0)
- return FALSE;
+ switch (child->type)
+ {
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_FLOAT:
+ if (fputs ("/>", f) < 0)
+ goto out;
+ break;
+ default:
+ if (fprintf (f, "%s</cdr>\n", whitespace) < 0)
+ goto out;
+ break;
+ }
}
+
+ retval = TRUE;
+
+ out:
+
+ g_free (whitespace);
- return TRUE;
+ return retval;
}
static gboolean
write_schema_children (GConfValue *value,
GSList *local_schemas,
FILE *f,
- const char *indent)
+ int indent)
{
/* Here we write each local_schema, in turn a local_schema can
* contain <default> and <longdesc> and have locale and short_desc
* attributes
*/
GSList *tmp;
-
- if (fputs (indent, f) < 0)
- return FALSE;
-
- if (fputs ("<local_schema", f) < 0)
- return FALSE;
+ gboolean retval;
+ char *whitespace;
+ whitespace = g_strnfill (indent, ' ');
+
tmp = local_schemas;
while (tmp != NULL)
{
- LocalSchemaInfo *local_schema;
+ LocalSchemaInfo *local_schema = tmp->data;
char *s;
- local_schema = tmp->data;
+ if (fputs (whitespace, f) < 0)
+ goto out;
+
+ if (fputs ("<local_schema", f) < 0)
+ goto out;
g_assert (local_schema->locale);
@@ -3240,7 +3508,7 @@ write_schema_children (GConfValue *valu
if (fprintf (f, " locale=\"%s\"", s) < 0)
{
g_free (s);
- return FALSE;
+ goto out;
}
g_free (s);
@@ -3252,115 +3520,212 @@ write_schema_children (GConfValue *valu
if (fprintf (f, " short_desc=\"%s\"", s) < 0)
{
g_free (s);
- return FALSE;
+ goto out;
}
g_free (s);
}
if (fputs (">\n", f) < 0)
- return FALSE;
+ goto out;
if (local_schema->default_value)
{
- if (fputs (indent, f) < 0)
- return FALSE;
+ if (fputs (whitespace, f) < 0)
+ goto out;
- if (fputs ("<default", f) < 0)
- return FALSE;
+ if (fputs (" <default", f) < 0)
+ goto out;
- if (!write_value_element (local_schema->default_value, NULL, f, indent))
- return FALSE;
-
- if (fprintf (f, "%s</default>", indent) < 0)
- return FALSE;
+ if (!write_value_element (local_schema->default_value, NULL, f, indent + INDENT_SPACES))
+ goto out;
+
+ switch (local_schema->default_value->type)
+ {
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_FLOAT:
+ if (fputs ("/>\n", f) < 0)
+ goto out;
+ break;
+ default:
+ if (fprintf (f, "%s </default>\n", whitespace) < 0)
+ goto out;
+ break;
+ }
}
if (local_schema->long_desc)
{
- if (fputs (indent, f) < 0)
- return FALSE;
-
- if (fputs ("<longdesc>\n", f) < 0)
- return FALSE;
+ if (fprintf (f, "%s <longdesc>", whitespace) < 0)
+ goto out;
s = g_markup_escape_text (local_schema->long_desc, -1);
if (fputs (s, f) < 0)
{
g_free (s);
- return FALSE;
+ goto out;
}
g_free (s);
+
+ if (fputs ("</longdesc>\n", f) < 0)
+ goto out;
}
-
+
+ if (fputs (whitespace, f) < 0)
+ goto out;
+
+ if (fputs ("</local_schema>\n", f) < 0)
+ goto out;
+
tmp = tmp->next;
}
- if (fputs (indent, f) < 0)
- return FALSE;
+ retval = TRUE;
- if (fputs ("</local_schema>\n", f) < 0)
- return FALSE;
+ out:
+
+ g_free (whitespace);
return TRUE;
}
static gboolean
write_entry (MarkupEntry *entry,
- FILE *f)
+ FILE *f,
+ int indent)
{
- if (fputs (" <entry", f) < 0)
- return FALSE;
+ gboolean retval = FALSE;
+ char *whitespace;
+
+ whitespace = g_strnfill (indent, ' ');
+
+ if (fprintf (f, "%s<entry", whitespace) < 0)
+ goto out;
g_assert (entry->name != NULL);
if (fprintf (f, " name=\"%s\" mtime=\"%lu\"",
entry->name,
(unsigned long) entry->mod_time) < 0)
- return FALSE;
+ goto out;
if (entry->schema_name)
{
if (fprintf (f, " schema=\"%s\"", entry->schema_name) < 0)
- return FALSE;
+ goto out;
}
if (entry->mod_user)
{
if (fprintf (f, " muser=\"%s\"", entry->mod_user) < 0)
- return FALSE;
+ goto out;
}
if (entry->value != NULL)
{
- if (!write_value_element (entry->value, entry->local_schemas, f, " "))
- return FALSE;
- }
+ if (!write_value_element (entry->value, entry->local_schemas, f, indent + INDENT_SPACES))
+ goto out;
+
+ switch (entry->value->type)
+ {
+ case GCONF_VALUE_BOOL:
+ case GCONF_VALUE_INT:
+ case GCONF_VALUE_FLOAT:
+ if (fputs ("/>\n", f) < 0)
+ goto out;
+ break;
+ default:
+ if (fprintf (f, "%s</entry>\n", whitespace) < 0)
+ goto out;
+ break;
+ }
+ }
else
{
- if (fputs (">", f) < 0)
- return FALSE;
+ if (fputs ("/>\n", f) < 0)
+ goto out;
}
- if (fputs (" </entry>\n", f) < 0)
- return FALSE;
+ retval = TRUE;
+
+ out:
+
+ g_free (whitespace);
- return TRUE;
+ return retval;
}
static gboolean
-save_entries (const char *filename,
- guint file_mode,
- GSList *entries,
- GError **err)
+write_dir (MarkupDir *dir,
+ FILE *f,
+ int indent)
+{
+ GSList *tmp;
+ gboolean retval = FALSE;
+ char *whitespace;
+
+ /* This dir will be deleted from the
+ * MarkupTree after syncing anyway ...
+ */
+ if (!dir->entries && !dir->subdirs)
+ return TRUE;
+
+ whitespace = g_strnfill (indent, ' ');
+
+ g_assert (dir->name != NULL);
+
+ if (fprintf (f, "%s<dir name=\"%s\">\n", whitespace, dir->name) < 0)
+ goto out;
+
+ tmp = dir->entries;
+ while (tmp != NULL)
+ {
+ MarkupEntry *entry = tmp->data;
+
+ if (!write_entry (entry, f, indent + INDENT_SPACES))
+ goto out;
+
+ tmp = tmp->next;
+ }
+
+ tmp = dir->subdirs;
+ while (tmp != NULL)
+ {
+ MarkupDir *subdir = tmp->data;
+
+ if (!write_dir (subdir, f, indent + INDENT_SPACES))
+ goto out;
+
+ tmp = tmp->next;
+ }
+
+ if (fprintf (f, "%s</dir>\n", whitespace) < 0)
+ return FALSE;
+
+ retval = TRUE;
+
+ out:
+
+ g_free (whitespace);
+
+ return retval;
+}
+
+static void
+save_tree (MarkupDir *dir,
+ gboolean save_as_subtree,
+ guint file_mode,
+ GError **err)
{
/* We save to a secondary file then copy over, to handle
* out-of-disk-space robustly
*/
FILE *f;
int new_fd;
+ char *filename;
char *new_filename;
char *err_str;
gboolean write_failed;
@@ -3369,6 +3734,8 @@ save_entries (const char *filename,
err_str = NULL;
new_fd = -1;
f = NULL;
+
+ filename = markup_dir_build_path (dir, TRUE, save_as_subtree);
new_filename = g_strconcat (filename, ".new", NULL);
new_fd = open (new_filename, O_WRONLY | O_CREAT, file_mode);
@@ -3382,7 +3749,7 @@ save_entries (const char *filename,
/* Leave the file empty to avoid parsing it later
* if there are no entries in it.
*/
- if (entries == NULL)
+ if (dir->entries == NULL && (!save_as_subtree || dir->subdirs == NULL))
goto done_writing;
f = fdopen (new_fd, "w");
@@ -3412,12 +3779,12 @@ save_entries (const char *filename,
{
GSList *tmp;
- tmp = entries;
+ tmp = dir->entries;
while (tmp != NULL)
{
MarkupEntry *entry = tmp->data;
- if (!write_entry (entry, f))
+ if (!write_entry (entry, f, INDENT_SPACES))
{
write_failed = TRUE;
goto done_writing;
@@ -3425,6 +3792,20 @@ save_entries (const char *filename,
tmp = tmp->next;
}
+
+ tmp = dir->subdirs;
+ while (tmp != NULL)
+ {
+ MarkupDir *dir = tmp->data;
+
+ if (!write_dir (dir, f, INDENT_SPACES))
+ {
+ write_failed = TRUE;
+ goto done_writing;
+ }
+
+ tmp = tmp->next;
+ }
}
if (fputs ("</gconf>\n", f) < 0)
@@ -3462,6 +3843,7 @@ save_entries (const char *filename,
out:
g_free (new_filename);
+ g_free (filename);
if (err_str)
{
@@ -3478,8 +3860,6 @@ save_entries (const char *filename,
if (f != NULL)
fclose (f);
-
- return err_str == NULL;
}
/*
#!/bin/sh
killall gconfd-2
cp /gnome/head/INSTALL/etc/gconf/2/path-xml /gnome/head/INSTALL/etc/gconf/2/path
echo "First XML dump:"
time gconftool-2 --dump / > /dev/null
echo "Second XML dump:"
time gconftool-2 --dump / > /dev/null
echo "Third XML dump:"
time gconftool-2 --dump / > /dev/null
killall gconfd-2
cp /gnome/head/INSTALL/etc/gconf/2/path-markup /gnome/head/INSTALL/etc/gconf/2/path
find ~/.gconf-markup -name %gconf-tree.xml -exec rm -f {} \;
find /gnome/head/INSTALL/etc/gconf/gconf.markup.defaults -name %gconf-tree.xml -exec rm -f {} \;
sleep 5
echo; echo;
echo "First Markup dump:"
time gconftool-2 --dump / > /dev/null
echo "Second Markup dump:"
time gconftool-2 --dump / > /dev/null
echo "Third Markup dump:"
time gconftool-2 --dump / > /dev/null
killall gconfd-2
cp /gnome/head/INSTALL/etc/gconf/2/path-markup /gnome/head/INSTALL/etc/gconf/2/path
gconf-merge-tree ~/.gconf-markup
gconf-merge-tree /gnome/head/INSTALL/etc/gconf/gconf.markup.defaults
sleep 5
echo; echo;
echo "First Markup dump (single file):"
time gconftool-2 --dump / > /dev/null
echo "Second Markup dump (single file):"
time gconftool-2 --dump / > /dev/null
echo "Third Markup dump (single file):"
time gconftool-2 --dump / > /dev/null
killall gconfd-2
cp /gnome/head/INSTALL/etc/gconf/2/path-markup /gnome/head/INSTALL/etc/gconf/2/path
find ~/.gconf-markup -name %gconf-tree.xml -exec rm -f {} \;
find /gnome/head/INSTALL/etc/gconf/gconf.markup.defaults -name %gconf-tree.xml -exec rm -f {} \;
for iii in ~/.gconf-markup/apps/* ~/.gconf-markup/schemas/apps/* /gnome/head/INSTALL/etc/gconf/gconf.markup.defaults/apps/* /gnome/head/INSTALL/etc/gconf/gconf.markup.defaults/schemas/apps/*; do
if [ -d $iii ]; then
gconf-merge-tree $iii;
fi
done
sleep 5
echo; echo;
echo "First Markup dump (files in /apps and /schemas/apps):"
time gconftool-2 --dump / > /dev/null
echo "Second Markup dump (files in /apps and /schemas/apps):"
time gconftool-2 --dump / > /dev/null
echo "Third Markup dump (files in /apps and /schemas/apps):"
time gconftool-2 --dump / > /dev/null
First XML dump:
real 0m4.827s
user 0m0.130s
sys 0m0.020s
Second XML dump:
real 0m1.553s
user 0m0.080s
sys 0m0.010s
Third XML dump:
real 0m1.419s
user 0m0.100s
sys 0m0.010s
First Markup dump:
real 0m2.374s
user 0m0.100s
sys 0m0.000s
Second Markup dump:
real 0m0.246s
user 0m0.090s
sys 0m0.000s
Third Markup dump:
real 0m0.251s
user 0m0.060s
sys 0m0.010s
First Markup dump (single file):
real 0m1.952s
user 0m0.110s
sys 0m0.020s
Second Markup dump (single file):
real 0m0.252s
user 0m0.120s
sys 0m0.010s
Third Markup dump (single file):
real 0m0.245s
user 0m0.080s
sys 0m0.000s
First Markup dump (files in /apps and /schemas/apps):
real 0m1.872s
user 0m0.110s
sys 0m0.010s
Second Markup dump (files in /apps and /schemas/apps):
real 0m0.247s
user 0m0.110s
sys 0m0.000s
Third Markup dump (files in /apps and /schemas/apps):
real 0m0.249s
user 0m0.070s
sys 0m0.020s
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]