diff options
author | Michael Meeks <michael.meeks@novell.com> | 2010-01-08 12:49:29 -0600 |
---|---|---|
committer | Federico Mena Quintero <federico@novell.com> | 2010-01-08 12:49:29 -0600 |
commit | a867a03613b7c68523d13b5a7c96636b011692d2 (patch) | |
tree | 0fa7e0348d8ede2414eccff7d14b2e8a2319dd8f | |
parent | 5c2b93d85c0ec93e4090f7f3b69b0d5d1a219b9b (diff) | |
download | gconf-moblin-speed-inherit-bgo571449.tar.gz |
bgo#571449 - Inherit attributes from a parent node for mtimemoblin-speed-inherit-bgo571449
-rw-r--r-- | backends/Makefile.am | 3 | ||||
-rw-r--r-- | backends/markup-backend.c | 10 | ||||
-rw-r--r-- | backends/markup-tree.c | 348 | ||||
-rw-r--r-- | backends/markup-tree.h | 5 | ||||
-rw-r--r-- | backends/xml-test.c | 13 |
5 files changed, 345 insertions, 34 deletions
diff --git a/backends/Makefile.am b/backends/Makefile.am index 3c1bc3b6..61ab03e0 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -1,5 +1,6 @@ INCLUDES= -I$(top_srcdir) -I$(top_builddir) -I$(top_builddir)/gconf \ $(DEPENDENT_WITH_XML_CFLAGS) \ + -DGCONF_ETCDIR=\""$(sysgconfdir)"\" \ -DGCONF_ENABLE_INTERNALS=1 -DG_LOG_DOMAIN=\"GConf-Backends\" backenddir = $(pkglibdir)/$(MAJOR_VERSION) @@ -36,7 +37,7 @@ xml_test_SOURCES= xml-test.c xml_test_LDADD = \ $(DEPENDENT_WITH_XML_LIBS) \ $(top_builddir)/gconf/libgconf-$(MAJOR_VERSION).la \ - libgconfbackend-oldxml.la + libgconfbackend-xml.la bin_PROGRAMS = gconf-merge-tree gconf_merge_tree_SOURCES = gconf-merge-tree.c diff --git a/backends/markup-backend.c b/backends/markup-backend.c index 3e653dfb..ce0bb12c 100644 --- a/backends/markup-backend.c +++ b/backends/markup-backend.c @@ -489,7 +489,7 @@ query_value (GConfSource *source, { retval = markup_entry_get_value (entry, locales); if (schema_name) - *schema_name = g_strdup (markup_entry_get_schema_name (entry)); + *schema_name = markup_entry_get_schema_name (entry); } else { @@ -521,7 +521,7 @@ query_metainfo (GConfSource *source, if (entry != NULL) { GConfMetaInfo* gcmi; - const char *schema_name; + char *schema_name; GTime mtime; const char *mod_user; @@ -533,6 +533,8 @@ query_metainfo (GConfSource *source, if (schema_name) gconf_meta_info_set_schema (gcmi, schema_name); + + g_free (schema_name); gconf_meta_info_set_mod_time (gcmi, mtime); @@ -579,7 +581,7 @@ gconf_entry_from_markup_entry (MarkupEntry *entry, const char **locales) { GConfValue *value; - const char *schema_name; + char *schema_name; GConfEntry *gconf_entry; value = markup_entry_get_value (entry, locales); @@ -592,6 +594,8 @@ gconf_entry_from_markup_entry (MarkupEntry *entry, value); gconf_entry_set_schema_name (gconf_entry, schema_name); + g_free (schema_name); + return gconf_entry; } diff --git a/backends/markup-tree.c b/backends/markup-tree.c index c08d6c79..4bb94da9 100644 --- a/backends/markup-tree.c +++ b/backends/markup-tree.c @@ -49,7 +49,7 @@ struct _MarkupEntry GConfValue *value; /* list of LocalSchemaInfo */ GSList *local_schemas; - char *schema_name; + char *schema_name; /* potentially compressed name */ char *mod_user; GTime mod_time; }; @@ -74,6 +74,8 @@ static void markup_dir_setup_as_subtree_root (MarkupDir *dir); static MarkupEntry* markup_entry_new (MarkupDir *dir, const char *name); static void markup_entry_free (MarkupEntry *entry); +static void markup_entry_set_mod_time (MarkupEntry *entry, + GTime mtime); static void parse_tree (MarkupDir *root, gboolean parse_subtree, @@ -96,10 +98,23 @@ struct _MarkupTree guint refcount; guint merged : 1; + guint inherited : 1; }; static GHashTable *trees_by_root_dir = NULL; +/* + * To improve interoperability with previous versions of + * the XML schema when eg. sharing NFS home directories, we + * only use the new compacted / inherited form for + * /etc/gconf/... (for now). + */ +static gboolean +enable_inheritance (MarkupTree *tree) +{ + return !strncmp (tree->dirname, GCONF_ETCDIR, strlen (GCONF_ETCDIR)); +} + MarkupTree* markup_tree_get (const char *root_dir, guint dir_mode, @@ -127,6 +142,7 @@ markup_tree_get (const char *root_dir, tree->dir_mode = dir_mode; tree->file_mode = file_mode; tree->merged = merged != FALSE; + tree->inherited = enable_inheritance (tree); tree->root = markup_dir_new (tree, NULL, "/"); @@ -179,6 +195,7 @@ struct _MarkupDir MarkupDir *parent; MarkupDir *subtree_root; char *name; + GTime mod_time; GSList *entries; GSList *subdirs; @@ -230,6 +247,7 @@ markup_dir_new (MarkupTree *tree, dir->name = g_strdup (name); dir->tree = tree; dir->parent = parent; + dir->mod_time = parent ? parent->mod_time : 0; if (parent) { @@ -245,6 +263,17 @@ markup_dir_new (MarkupTree *tree, } static void +markup_dir_set_mod_time (MarkupDir *dir, GTime mtime) +{ + /* set only if newer for import */ + while (dir != NULL && dir->mod_time < mtime) + { + dir->mod_time = mtime; + dir = dir->parent; + } +} + +static void markup_dir_free (MarkupDir *dir) { GSList *tmp; @@ -512,6 +541,7 @@ load_subtree (MarkupDir *dir) markup_dir_list_available_local_descs (dir); parse_tree (dir, TRUE, NULL, &tmp_err); + if (tmp_err) { /* note that tmp_err may be a G_MARKUP_ERROR while only @@ -1335,9 +1365,10 @@ markup_entry_new (MarkupDir *dir, entry = g_new0 (MarkupEntry, 1); + entry->dir = dir; entry->name = g_strdup (name); + entry->mod_time = dir->mod_time; - entry->dir = dir; dir->entries = g_slist_prepend (dir->entries, entry); return entry; @@ -1596,7 +1627,7 @@ markup_entry_set_value (MarkupEntry *entry, } /* Update mod time */ - entry->mod_time = time (NULL); + markup_entry_set_mod_time (entry, time (NULL)); /* Need to save to disk */ markup_dir_set_entries_need_save (entry->dir); @@ -1670,13 +1701,141 @@ markup_entry_unset_value (MarkupEntry *entry, } /* Update mod time */ - entry->mod_time = time (NULL); + markup_entry_set_mod_time (entry, time (NULL)); /* Need to save to disk */ markup_dir_set_entries_need_save (entry->dir); markup_dir_queue_sync (entry->dir); } +#define VERBOSE_SCHEMA_PREFIX "/schemas" +#define COMPRESS_CHAR '~' + +/* + * Sets schema string, and if in inherited mode compresses + * it thus, to either "~" (which means the schema is + * /schemas + the node path), or to ~/foo (which means + * /schemas + the parent path + foo). + */ +void +markup_entry_set_verbose_schema_name (MarkupEntry *entry, + const char *verbose) +{ + int len; + char *cname; + const char *elem; + GSList *names, *l; + MarkupDir *dir; + + g_return_if_fail (entry->dir != NULL); + + /* if we have a ~ - we're perfectly stemmed already... */ + if (!entry->dir->tree->inherited || !verbose || verbose[0] == '~' || + strncmp (verbose, VERBOSE_SCHEMA_PREFIX, + sizeof (VERBOSE_SCHEMA_PREFIX) - 1)) + { + entry->schema_name = g_strdup (verbose); + return; + } + + /* Not the common case ... compress the schema path */ + + /* build list of path elements names */ + names = g_slist_prepend (NULL, entry->name); + for (dir = entry->dir; dir != NULL; dir = dir->parent) + names = g_slist_prepend (names, dir->name); + /* remove root with name '/' */ + names = g_slist_delete_link (names, names); + + elem = verbose + sizeof (VERBOSE_SCHEMA_PREFIX); + for (l = names; *elem && l != NULL; l = l->next) { + const char *frag = l->data; + len = strlen (frag); + if (strncmp (frag, elem, len) != 0) + break; + + if (G_LIKELY (elem[len] == '/')) { + elem += len + 1; + } else { /* special case "~" means exact path */ + if (elem[len] == '\0') + elem += len; + else + break; + } + } + + if (!l) { + g_assert (*elem == '\0'); + cname = g_strdup ("~"); + } else if (l->next == NULL) { /* just replace the last elem */ + len = strlen (elem); + cname = g_malloc (len + 3); + cname[0] = '~'; + cname[1] = '\0'; + strcpy (cname + 1, elem - 1); + } else { + cname = g_strdup (verbose); + } + g_slist_free (names); + + entry->schema_name = cname; +} + +char* +markup_entry_get_schema_name (MarkupEntry *entry) +{ + int len; + char *schema; + GSList *names, *l; + MarkupDir *dir; + + g_return_val_if_fail (entry->dir != NULL, NULL); + g_return_val_if_fail (entry->dir->entries_loaded, NULL); + + /* if we have no ~ - we're not stemmed... */ + if (!entry->dir->tree->inherited || + entry->schema_name == NULL || + entry->schema_name[0] == '\0' || + entry->schema_name[0] != '~') { + return g_strdup (entry->schema_name); + } + + /* expand the leading '~' */ + + len = sizeof (VERBOSE_SCHEMA_PREFIX) - 1; + if (entry->schema_name[1] == '/') + { + names = g_slist_prepend (NULL, entry->schema_name + 2); + } + else + { /* just ~ special case */ + g_assert (entry->schema_name[1] == '\0'); + names = g_slist_prepend (NULL, entry->name); + } + len += strlen (names->data) + 1; + + /* build list of path elements names */ + for (dir = entry->dir; dir != NULL; dir = dir->parent) + { + len += 1 + strlen (dir->name); + names = g_slist_prepend (names, dir->name); + } + /* remove root with name '/' */ + names = g_slist_delete_link (names, names); + len -= 2; + + schema = g_new (char, len + 1); + strcpy (schema, VERBOSE_SCHEMA_PREFIX); + for (l = names; l != NULL; l = l->next) + { + strcat (schema, "/"); + strcat (schema, l->data); + } + g_assert (strlen (schema) == len); + + return schema; +} + void markup_entry_set_schema_name (MarkupEntry *entry, const char *schema_name) @@ -1691,10 +1850,10 @@ markup_entry_set_schema_name (MarkupEntry *entry, /* schema_name may be NULL to unset it */ g_free (entry->schema_name); - entry->schema_name = g_strdup (schema_name); + markup_entry_set_verbose_schema_name (entry, schema_name); /* Update mod time */ - entry->mod_time = time (NULL); + markup_entry_set_mod_time (entry, time (NULL)); /* Need to save to disk */ markup_dir_set_entries_need_save (entry->dir); @@ -1819,15 +1978,6 @@ markup_entry_get_name (MarkupEntry *entry) } const char* -markup_entry_get_schema_name (MarkupEntry *entry) -{ - g_return_val_if_fail (entry->dir != NULL, NULL); - g_return_val_if_fail (entry->dir->entries_loaded, NULL); - - return entry->schema_name; -} - -const char* markup_entry_get_mod_user (MarkupEntry *entry) { g_return_val_if_fail (entry->dir != NULL, NULL); @@ -1860,7 +2010,10 @@ static void markup_entry_set_mod_time (MarkupEntry *entry, GTime mtime) { + g_return_if_fail (entry->dir != NULL); + entry->mod_time = mtime; + markup_dir_set_mod_time (entry->dir, mtime); } /* @@ -2756,7 +2909,7 @@ parse_entry_element (GMarkupParseContext *context, * mess up the modtime */ if (schema) - entry->schema_name = g_strdup (schema); + markup_entry_set_verbose_schema_name (entry, schema); } else { @@ -2815,18 +2968,21 @@ parse_dir_element (GMarkupParseContext *context, { MarkupDir *parent; MarkupDir *dir; + const char *mtime; 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; + mtime = NULL; if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, "name", &name, + "mtime", &mtime, NULL)) return; @@ -2870,6 +3026,9 @@ parse_dir_element (GMarkupParseContext *context, { dir = markup_dir_new (info->root->tree, parent, name); + if (mtime != NULL) + markup_dir_set_mod_time (dir, gconf_string_to_gulong (mtime)); + /* This is a dummy directory which will be deleted when * we've finised parsing the contents of this element. */ @@ -4159,8 +4318,9 @@ write_entry (MarkupEntry *entry, if (local_schema_info == NULL) { - if (fprintf (f, " mtime=\"%lu\"", (unsigned long) entry->mod_time) < 0) - goto out; + if (!entry->dir->tree->inherited || entry->dir->mod_time != entry->mod_time) + if (fprintf (f, " mtime=\"%lu\"", (unsigned long) entry->mod_time) < 0) + goto out; if (entry->schema_name) { @@ -4231,9 +4391,17 @@ write_dir (MarkupDir *dir, g_assert (dir->name != NULL); - if (fprintf (f, "%s<dir name=\"%s\">\n", + if (fprintf (f, "%s<dir name=\"%s\"", make_whitespace (indent), dir->name) < 0) goto out; + if (dir->tree->inherited && + (dir->parent == dir->tree->root || + dir->parent->mod_time != dir->mod_time)) + if (fprintf (f, " mtime=\"%lu\"", + (unsigned long)dir->mod_time) < 0) + goto out; + if (fprintf (f, ">\n") < 0) + goto out; tmp = dir->entries; while (tmp != NULL) @@ -4634,3 +4802,143 @@ local_schema_info_free (LocalSchemaInfo *info) gconf_value_free (info->default_value); g_free (info); } + +#ifndef GCONF_DISABLE_TESTS +static gboolean +test_tree (MarkupTree *tree) +{ + int i; + MarkupDir *dir; + MarkupEntry *entry; + GError *error = NULL; + + dir = markup_tree_lookup_dir (tree, "/system/smb", &error); + if (dir == NULL) + g_error ("Failed to lookup directory"); + + entry = markup_dir_lookup_entry (dir, "workgroup", &error); + if (entry == NULL) + g_error ("Failed to lookup entry"); + + /* test schema name mangling */ + struct { + char *entry; + char *schema_name; + char *uncompressed; + } tests[] = { + { "workgroup", "~", "/schemas/system/smb/workgroup" }, + { "workgroup_lastelem", "~/workgroup", "/schemas/system/smb/workgroup" }, + { "workgroup_random", "/schemas/random", "/schemas/random" }, + { "workgroup_noschema", NULL, NULL }, + { "workgroup_oddpath", "/foo/system/smb/workgroup", "/foo/system/smb/workgroup" } + }; + for (i = 0; i < G_N_ELEMENTS (tests); i++) + { + MarkupEntry *entry; + entry = markup_dir_lookup_entry (dir, tests[i].entry, &error); + if (entry == NULL) + g_error ("Failed to lookup '%s'", tests[i].entry); + if (!tests[i].schema_name) { + if (entry->schema_name) + g_error ("expected NULL schema name on '%s'", tests[i].entry); + } else { + char *name; + + if (!entry->schema_name) + g_error ("unexpected NULL schema name on '%s'", tests[i].entry); + if (strcmp (tests[i].schema_name, entry->schema_name)) { + g_error ("mis-matching compressed schema name '%s' vs. '%s' on key '%s'", + entry->schema_name, tests[i].schema_name, tests[i].entry); + } + name = markup_entry_get_schema_name (entry); + if (strcmp (tests[i].uncompressed, name)) { + g_error ("mis-matching uncompressed schema name '%s' vs. '%s' on key '%s'", + tests[i].uncompressed, name, tests[i].entry); + } + g_free (name); + } + } + return TRUE; +} +#endif + +gboolean +markup_test_tree (void) +{ +#ifndef GCONF_DISABLE_TESTS + const char *xml = +"<?xml version=\"1.0\"?>\n" +"<gconf>\n" +" <dir name=\"system\">\n" +" <dir name=\"smb\">\n" +" <entry name=\"workgroup\" mtime=\"1234375446\" schema=\"/schemas/system/smb/workgroup\"/>\n" +" <entry name=\"workgroup_lastelem\" mtime=\"1234375446\" schema=\"/schemas/system/smb/workgroup\"/>\n" +" <entry name=\"workgroup_random\" mtime=\"1234375446\" schema=\"/schemas/random\"/>\n" +" <entry name=\"workgroup_noschema\" mtime=\"1234375446\"/>\n" +" <entry name=\"workgroup_oddpath\" mtime=\"1234375446\" schema=\"/foo/system/smb/workgroup\"/>\n" +" </dir>\n" +" </dir>\n" +" <dir name=\"schemas\">\n" +" <dir name=\"system\">\n" +" <dir name=\"smb\">\n" +" <entry name=\"workgroup\" mtime=\"1234375446\" type=\"schema\" stype=\"string\" owner=\"gnome-vfs\">\n" +" <local_schema locale=\"C\" short_desc=\"SMB workgroup\">\n" +" <default type=\"string\">\n" +" <stringvalue>WORKGROUP</stringvalue>\n" +" </default>\n" +" <longdesc>A Long Desc</longdesc>\n" +" </local_schema>\n" +" </entry>\n" +" </dir>\n" +" <entry name=\"random\" mtime=\"1234375446\" type=\"schema\" stype=\"string\" owner=\"gnome-vfs\">\n" +" <local_schema locale=\"C\" short_desc=\"other schema\">\n" +" <default type=\"string\">\n" +" <stringvalue>Random</stringvalue>\n" +" </default>\n" +" <longdesc>Random Desc</longdesc>\n" +" </local_schema>\n" +" </entry>\n" +" </dir>\n" +" </dir>\n" +"</gconf>"; + + GMarkupParseContext *context = NULL; + GError *error = NULL; + ParseInfo info; + MarkupTree *tree; + + tree = markup_tree_get ("/home/baa/.gconf/foo.xml", 0, 0, TRUE); + g_assert (tree->inherited == FALSE); + markup_tree_unref (tree); + + tree = markup_tree_get (GCONF_ETCDIR "/foo.xml", 0, 0, TRUE); + g_assert (tree->inherited == TRUE); + + parse_info_init (&info, tree->root, TRUE, NULL); + + /* analog of load_subtree */ + tree->root->subdirs_loaded = TRUE; + tree->root->entries_loaded = TRUE; + tree->root->save_as_subtree = TRUE; + markup_dir_setup_as_subtree_root (tree->root); + markup_dir_list_available_local_descs (tree->root); + + context = g_markup_parse_context_new (&gconf_parser, + 0, &info, NULL); + if (!g_markup_parse_context_parse (context, xml, strlen (xml), &error)) + g_error ("Failed to parse gconf xml '%s'", error->message); + + if (!g_markup_parse_context_end_parse (context, &error)) + g_error ("Failed to end the parsing"); + + g_markup_parse_context_free (context); + + parse_info_free (&info); + + test_tree (tree); + + markup_tree_unref (tree); + g_message ("All tests passed successfully\n"); +#endif + return TRUE; +} diff --git a/backends/markup-tree.h b/backends/markup-tree.h index cda6bd8c..4b31f97b 100644 --- a/backends/markup-tree.h +++ b/backends/markup-tree.h @@ -75,9 +75,12 @@ void markup_entry_unset_value (MarkupEntry *entry, const char *locale); void markup_entry_set_schema_name (MarkupEntry *entry, const char *schema_name); +char* markup_entry_get_schema_name (MarkupEntry *entry); const char* markup_entry_get_name (MarkupEntry *entry); -const char* markup_entry_get_schema_name (MarkupEntry *entry); const char* markup_entry_get_mod_user (MarkupEntry *entry); GTime markup_entry_get_mod_time (MarkupEntry *entry); +/* testing hook */ +gboolean markup_tree_test (void); + #endif diff --git a/backends/xml-test.c b/backends/xml-test.c index 687d558b..4829d961 100644 --- a/backends/xml-test.c +++ b/backends/xml-test.c @@ -18,15 +18,11 @@ */ #include <config.h> -#include "xml-entry.h" -#include "xml-dir.h" -#include "xml-cache.h" +#include "markup-tree.h" #include "gconf/gconf-internals.h" #include "gconf/gconf-backend.h" #include <stdlib.h> #include <string.h> -#include <libxml/entities.h> -#include <libxml/globals.h> GConfBackendVTable* gconf_backend_get_vtable (void); @@ -36,10 +32,9 @@ main (int argc, char **argv) GConfBackendVTable *vtable; vtable = gconf_backend_get_vtable (); - - xml_test_entry (); - xml_test_dir (); - xml_test_cache (); + + if (!markup_test_tree ()) + return 1; return 0; } |