diff options
-rw-r--r-- | service/dconf-rebuilder.c | 240 |
1 files changed, 98 insertions, 142 deletions
diff --git a/service/dconf-rebuilder.c b/service/dconf-rebuilder.c index 9cac1a7..3f170d6 100644 --- a/service/dconf-rebuilder.c +++ b/service/dconf-rebuilder.c @@ -25,42 +25,34 @@ #include "../gvdb/gvdb-reader.h" #include "../gvdb/gvdb-builder.h" - -typedef struct -{ - const gchar *prefix; - gint prefix_len; - - GHashTable *table; - const gchar *const*keys; - GVariant *const*values; - gint n_items; - gint index; - - gchar name[4096]; - gint name_len; -} DConfRebuilderState; +#include "../common/dconf-paths.h" static GvdbItem * -dconf_rebuilder_get_parent (GHashTable *table, - gchar *key, - gint length) +dconf_rebuilder_get_parent (GHashTable *table, + const gchar *key) { GvdbItem *grandparent, *parent; + gchar *parent_name; + gint len; - if (length == 1) + if (g_str_equal (key, "/")) return NULL; - while (key[--length - 1] != '/'); - key[length] = '\0'; + len = strlen (key); + if (key[len - 1] == '/') + len--; + + while (key[len - 1] != '/') + len--; - parent = g_hash_table_lookup (table, key); + parent_name = g_strndup (key, len); + parent = g_hash_table_lookup (table, parent_name); if (parent == NULL) { - parent = gvdb_hash_table_insert (table, key); + parent = gvdb_hash_table_insert (table, parent_name); - grandparent = dconf_rebuilder_get_parent (table, key, length); + grandparent = dconf_rebuilder_get_parent (table, parent_name); if (grandparent != NULL) gvdb_item_set_parent (parent, grandparent); @@ -69,143 +61,107 @@ dconf_rebuilder_get_parent (GHashTable *table, return parent; } -static void -dconf_rebuilder_insert (GHashTable *table, - const gchar *key, - GVariant *value) +gboolean +dconf_rebuilder_rebuild (const gchar *filename, + const gchar *prefix, + const gchar * const *keys, + GVariant * const *values, + int n_items, + GError **error) { - GvdbItem *item; - gchar *mykey; - gint length; - - length = strlen (key); - mykey = g_alloca (length); - memcpy (mykey, key, length); - - g_assert (g_hash_table_lookup (table, key) == NULL); - item = gvdb_hash_table_insert (table, key); - - gvdb_item_set_parent (item, - dconf_rebuilder_get_parent (table, mykey, length)); + GHashTable *table; + gboolean success; + GHashTable *new; + GvdbTable *old; + gint i; - gvdb_item_set_value (item, value); -} + table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); -static void -dconf_rebuilder_put_item (DConfRebuilderState *state) -{ - if (state->values[state->index] != NULL) + /* read in the old values */ + if ((old = gvdb_table_new (filename, FALSE, NULL))) { - gchar *fullname; + gchar **names; + gint n_names; + gint i; - fullname = g_strconcat (state->prefix, state->keys[state->index], NULL); - dconf_rebuilder_insert (state->table, fullname, state->values[state->index]); - g_free (fullname); - } + names = gvdb_table_get_names (old, &n_names); + for (i = 0; i < n_names; i++) + { + if (dconf_is_key (names[i], NULL)) + { + GVariant *value; - state->index++; -} + value = gvdb_table_get_value (old, names[i]); -static gboolean -dconf_rebuilder_walk_name (DConfRebuilderState *state, - const gchar *name, - gsize name_len) -{ - gint cmp; - - g_assert (state->name_len + name_len < sizeof state->name - 1); - memcpy (state->name + state->name_len, name, name_len); - state->name[state->name_len + name_len] = '\0'; - - if (state->index == state->n_items) - return TRUE; + if (value != NULL) + { + g_hash_table_insert (table, names[i], value); + names[i] = NULL; + } + } - if (state->name_len + name_len < state->prefix_len || - memcmp (state->name, state->prefix, state->prefix_len) != 0) - return TRUE; - - while ((cmp = strcmp (state->name + state->prefix_len, - state->keys[state->index])) > 0) - { - dconf_rebuilder_put_item (state); + g_free (names[i]); + } - if (state->index == state->n_items) - return TRUE; + gvdb_table_unref (old); + g_free (names); } - return cmp != 0; -} - -static void -dconf_rebuilder_walk_value (const gchar *name, - gsize name_len, - GVariant *value, - gpointer user_data) -{ - DConfRebuilderState *state = user_data; - - if (dconf_rebuilder_walk_name (state, name, name_len)) - dconf_rebuilder_insert (state->table, state->name, value); - - else - dconf_rebuilder_put_item (state); -} - -static gboolean -dconf_rebuilder_walk_open (const gchar *name, - gsize name_len, - gpointer user_data) -{ - DConfRebuilderState *state = user_data; - - if (dconf_rebuilder_walk_name (state, name, name_len)) + /* apply the requested changes */ + for (i = 0; i < n_items; i++) { - state->name_len += name_len; - return TRUE; + gchar *path = g_strconcat (prefix, keys[i], NULL); + + /* Check if we are performing a path reset */ + if (g_str_has_suffix (path, "/")) + { + GHashTableIter iter; + gpointer key; + + g_assert (values[i] == NULL); + + /* A path reset is really a request to delete all keys that + * has a name starting with the reset path. + */ + g_hash_table_iter_init (&iter, table); + while (g_hash_table_iter_next (&iter, &key, NULL)) + if (g_str_has_prefix (key, path)) + g_hash_table_iter_remove (&iter); + } + + if (values[i] != NULL) + g_hash_table_insert (table, g_strdup (path), g_variant_ref (values[i])); + else + g_hash_table_remove (table, path); + + g_free (path); } - return FALSE; -} + /* convert back to GVDB format */ + { + GHashTableIter iter; + gpointer key, value; -static void -dconf_rebuilder_walk_close (gsize name_len, - gpointer user_data) -{ - DConfRebuilderState *state = user_data; + new = gvdb_hash_table_new (NULL, NULL); - state->name_len -= name_len; -} - -gboolean -dconf_rebuilder_rebuild (const gchar *filename, - const gchar *prefix, - const gchar *const*keys, - GVariant *const*values, - int n_items, - GError **error) -{ - DConfRebuilderState state = { prefix, strlen (prefix), - 0, keys, values, n_items }; - gboolean success; - GvdbTable *old; + g_hash_table_iter_init (&iter, table); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + GvdbItem *item; - state.table = gvdb_hash_table_new (NULL, NULL); - - if ((old = gvdb_table_new (filename, FALSE, NULL))) - { - gvdb_table_walk (old, "/", - dconf_rebuilder_walk_open, - dconf_rebuilder_walk_value, - dconf_rebuilder_walk_close, - &state); - gvdb_table_unref (old); - } + g_assert (g_hash_table_lookup (new, key) == NULL); + item = gvdb_hash_table_insert (new, key); + gvdb_item_set_parent (item, dconf_rebuilder_get_parent (new, key)); + gvdb_item_set_value (item, value); + } + } - while (state.index != state.n_items) - dconf_rebuilder_put_item (&state); + /* write the new file */ + success = gvdb_table_write_contents (new, filename, FALSE, error); - success = gvdb_table_write_contents (state.table, filename, FALSE, error); - g_hash_table_unref (state.table); + /* clean up */ + g_hash_table_unref (table); + g_hash_table_unref (new); return success; } |