summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2011-12-31 15:04:28 -0500
committerRyan Lortie <desrt@desrt.ca>2012-07-29 10:40:16 +0200
commit6897234cd9e582da84e06b8b42138c807a160412 (patch)
treeb7ba02750d563e472e1ba3b229466bb32b9adaf8
parent55005057d2ccd413a14ed6048d50a364874659f9 (diff)
downloadglib-6897234cd9e582da84e06b8b42138c807a160412.tar.gz
GSettingsBackend: add list support vfuncs
Implement it for GMemorySettingsBackend.
-rw-r--r--gio/gmemorysettingsbackend.c331
-rw-r--r--gio/gsettingsbackend.c148
-rw-r--r--gio/gsettingsbackend.h27
-rw-r--r--gio/gsettingsbackendinternal.h36
4 files changed, 540 insertions, 2 deletions
diff --git a/gio/gmemorysettingsbackend.c b/gio/gmemorysettingsbackend.c
index d6eeb831f..bd65e62cb 100644
--- a/gio/gmemorysettingsbackend.c
+++ b/gio/gmemorysettingsbackend.c
@@ -21,21 +21,43 @@
#include "config.h"
-#include "gsimplepermission.h"
#include "gsettingsbackendinternal.h"
+#include "gsimplepermission.h"
#include "giomodule.h"
+#include <string.h>
+
#define G_TYPE_MEMORY_SETTINGS_BACKEND (g_memory_settings_backend_get_type())
#define G_MEMORY_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_MEMORY_SETTINGS_BACKEND, \
GMemorySettingsBackend))
+typedef struct
+{
+ GSList *removes;
+ GSList *adds;
+} Directory;
+
+static void
+directory_free (gpointer data)
+{
+ Directory *d = data;
+
+ g_slist_free_full (d->removes, g_free);
+ g_slist_free_full (d->adds, g_free);
+
+ g_slice_free (Directory, d);
+}
+
+
typedef GSettingsBackendClass GMemorySettingsBackendClass;
typedef struct
{
GSettingsBackend parent_instance;
+ GHashTable *directories;
GHashTable *table;
+ guint64 unique_id;
} GMemorySettingsBackend;
G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
@@ -44,6 +66,116 @@ G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
g_define_type_id, "memory", 10))
+static gboolean
+g_memory_settings_backend_verify (GMemorySettingsBackend *memory,
+ const gchar *path)
+{
+ const gchar *end = path;
+
+ /* Given the path '/a/b:/:c/d/e:/f/g' we need to verify that each
+ * explicit item exists. An explicit item is one that is in an
+ * explicit directory (ie: directory ending with ':/'). In this case,
+ * we have to check:
+ *
+ * item ':c/' exists in explicit directory '/a/b:/'
+ *
+ * and
+ *
+ * 'f/' exists in '/a/b:/:c/d/e:/'.
+ *
+ * Items in explicit directories must be directories themselves (ie:
+ * they must be terminated with '/').
+ *
+ * We do this by looking for the first ':/' then the '/' to follow it
+ * to grab the directory name and the item name, then repeating until
+ * we have found all instances of ':/'.
+ *
+ * It is possible to directly nest explicit dirs, so we also have to
+ * take care to properly handle cases like '/a/b:/:c:/d/' by checking
+ * both that ':c:/' exists in '/a/b:/' and also that 'd/' exists in
+ * '/a/b:/:c:/' (ie: the 'item' in one iteration can become the 'dir'
+ * in the next).
+ *
+ * We also have to consider being asked for an explicit directory
+ * itself. These implicitly exist just the same as normal items --
+ * it's the items in explicit directories that must exist explicitly.
+ * Therefore, if we find ':/' at the end of the string, we should not
+ * treat it specially.
+ */
+ while (TRUE)
+ {
+ const gchar *end_item;
+ gchar *dir, *item;
+ gboolean exists;
+ Directory *d;
+
+ end = strstr (end, ":/");
+
+ /* No more ':/' instances */
+ if (end == NULL)
+ return TRUE;
+
+ /* Skip past the ':/' which is part of the explicit dir name */
+ end += 2;
+
+ /* If ':/' was at the end of the string, we're done */
+ if (*end != '\0')
+ return TRUE;
+
+ /* Find the end of the item name */
+ end_item = strchr (end, '/');
+
+ /* No trailing slash, so surely this can't exist */
+ if (end_item == NULL)
+ return FALSE;
+
+ /* Skip past the '/' which is part of the item name */
+ end_item++;
+
+ dir = g_strndup (path, end - path);
+ item = g_strndup (end, end_item - end);
+
+ /* We assume that items starting with a character other than ':'
+ * already exist (unless explicitly deleted) and that items
+ * starting with ':' only exist if they have been explicitly
+ * added.
+ */
+ exists = item[0] != ':';
+
+ d = g_hash_table_lookup (memory->directories, dir);
+ if (d != NULL)
+ {
+ GSList *list;
+
+ /* For assumed-existing items, we want to search in the
+ * removes list. For assumed-non-existing items, we want to
+ * search in the adds list.
+ */
+ if (exists)
+ list = d->removes;
+ else
+ list = d->adds;
+
+ while (list)
+ if (g_str_equal (list->data, item))
+ break;
+
+ /* If we found it in the list we were looking for, flip our
+ * initial assumption.
+ */
+ if (list)
+ exists = !exists;
+ }
+
+ g_free (item);
+ g_free (dir);
+
+ if (!exists)
+ return FALSE;
+ }
+}
+
+
static GVariant *
g_memory_settings_backend_read (GSettingsBackend *backend,
const gchar *key,
@@ -72,6 +204,9 @@ g_memory_settings_backend_write (GSettingsBackend *backend,
GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
GVariant *old_value;
+ if (!g_memory_settings_backend_verify (memory, key))
+ return FALSE;
+
old_value = g_hash_table_lookup (memory->table, key);
g_variant_ref_sink (value);
@@ -103,7 +238,9 @@ static gboolean
g_memory_settings_backend_get_writable (GSettingsBackend *backend,
const gchar *name)
{
- return TRUE;
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+ return g_memory_settings_backend_verify (memory, name);
}
static GPermission *
@@ -113,6 +250,189 @@ g_memory_settings_backend_get_permission (GSettingsBackend *backend,
return g_simple_permission_new (TRUE);
}
+static gboolean
+g_memory_settings_backend_check_exists (GSettingsBackend *backend,
+ const gchar *path)
+{
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+ gboolean exists;
+
+ exists = g_memory_settings_backend_verify (memory, path);
+
+ return exists;
+}
+
+static gchar **
+g_memory_settings_backend_list (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar * const *schema_items)
+{
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+ GPtrArray *array;
+ Directory *d;
+ GSList *node;
+
+ d = g_hash_table_lookup (memory->directories, dir);
+
+ /* fast path: by default, just return the schema items */
+ if (d == NULL)
+ return g_strdupv ((gchar **) schema_items);
+
+ array = g_ptr_array_new ();
+ while (schema_items)
+ {
+ const gchar *item = *schema_items++;
+
+ /* check if this item was removed */
+ for (node = d->removes; node; node = node->next)
+ if (g_str_equal (node->data, item))
+ break;
+
+ /* if not removed, put it in the result */
+ if (!node)
+ g_ptr_array_add (array, g_strdup (item));
+ }
+
+ for (node = d->adds; node; node = node->next)
+ g_ptr_array_add (array, g_strdup (node->data));
+
+ g_ptr_array_add (array, NULL);
+
+ return (gchar **) g_ptr_array_free (array, FALSE);
+}
+
+static gboolean
+g_memory_settings_backend_can_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before)
+{
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+ return g_memory_settings_backend_verify (memory, dir);
+}
+
+static gboolean
+g_memory_settings_backend_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before,
+ gchar **item)
+{
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+ GSList **ptr;
+ Directory *d;
+ gchar *id;
+
+ if (!g_memory_settings_backend_verify (memory, dir))
+ return FALSE;
+
+ d = g_hash_table_lookup (memory->directories, dir);
+
+ if (d == NULL)
+ {
+ d = g_slice_new0 (Directory);
+ g_hash_table_insert (memory->directories, g_strdup (dir), d);
+ }
+
+ id = g_strdup_printf (":%"G_GINT64_FORMAT, memory->unique_id++);
+
+ for (ptr = &d->adds; ptr; ptr = &(*ptr)->next)
+ if (before && g_str_equal ((*ptr)->data, before))
+ break;
+
+ *ptr = g_slist_prepend (*ptr, id);
+
+ if (item != NULL)
+ *item = g_strdup (id);
+
+ return TRUE;
+}
+
+static gboolean
+g_memory_settings_backend_can_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item)
+{
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+ return g_memory_settings_backend_verify (memory, dir);
+}
+
+static void
+g_memory_settings_backend_clear (GMemorySettingsBackend *memory,
+ const gchar *dir,
+ const gchar *item)
+{
+ GHashTableIter iter;
+ gchar *prefix;
+ gpointer key;
+
+ prefix = g_strconcat (dir, item, NULL);
+
+ /* slow and steady... */
+ g_hash_table_iter_init (&iter, memory->table);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ if (g_str_has_prefix (key, prefix))
+ g_hash_table_iter_remove (&iter);
+
+ g_hash_table_iter_init (&iter, memory->directories);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ if (g_str_has_prefix (key, prefix))
+ g_hash_table_iter_remove (&iter);
+
+ g_free (prefix);
+}
+
+static gboolean
+g_memory_settings_backend_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item)
+{
+ GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+ Directory *d;
+
+ if (!g_memory_settings_backend_verify (memory, dir))
+ return FALSE;
+
+ d = g_hash_table_lookup (memory->directories, dir);
+ if (d == NULL)
+ {
+ d = g_slice_new0 (Directory);
+ g_hash_table_insert (memory->directories, g_strdup (dir), d);
+ }
+
+ /* If the item starts with ':' that we just have to remove it from the
+ * 'adds' list. If it starts with a non-':' then we need to add a
+ * remove entry for it.
+ */
+ if (item[0] == ':')
+ {
+ GSList **ptr;
+
+ for (ptr = &d->adds; ptr; ptr = &(*ptr)->next)
+ if (g_str_equal ((*ptr)->data, item))
+ break;
+
+ g_free ((*ptr)->data);
+ *ptr = g_slist_delete_link (*ptr, *ptr);
+ }
+ else
+ {
+ GSList *node;
+
+ for (node = d->removes; node; node = node->next)
+ if (g_str_equal (node->data, item))
+ break;
+
+ if (!node)
+ d->removes = g_slist_prepend (d->removes, g_strdup (item));
+ }
+
+ /* Remove all subitems that may have been stored */
+ g_memory_settings_backend_clear (memory, dir, item);
+
+ return TRUE;
+}
+
static void
g_memory_settings_backend_finalize (GObject *object)
{
@@ -129,6 +449,7 @@ g_memory_settings_backend_init (GMemorySettingsBackend *memory)
{
memory->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_variant_unref);
+ memory->directories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, directory_free);
}
static void
@@ -142,6 +463,12 @@ g_memory_settings_backend_class_init (GMemorySettingsBackendClass *class)
backend_class->reset = g_memory_settings_backend_reset;
backend_class->get_writable = g_memory_settings_backend_get_writable;
backend_class->get_permission = g_memory_settings_backend_get_permission;
+ backend_class->check_exists = g_memory_settings_backend_check_exists;
+ backend_class->list = g_memory_settings_backend_list;
+ backend_class->can_insert = g_memory_settings_backend_can_insert;
+ backend_class->insert = g_memory_settings_backend_insert;
+ backend_class->can_remove = g_memory_settings_backend_can_remove;
+ backend_class->remove = g_memory_settings_backend_remove;
object_class->finalize = g_memory_settings_backend_finalize;
}
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index 2a24289d9..bd2147782 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -650,6 +650,72 @@ ignore_subscription (GSettingsBackend *backend,
{
}
+static gboolean
+g_settings_backend_real_check_exists (GSettingsBackend *backend,
+ const gchar *path)
+{
+ return !strstr (path, ":/:");
+}
+
+static gchar **
+g_settings_backend_real_list (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar * const *schema_items)
+{
+ return g_strdupv ((gchar **) schema_items);
+}
+
+static gboolean
+g_settings_backend_real_can_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before)
+{
+ return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before,
+ gchar **item)
+{
+ return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_can_move (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before)
+{
+ return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_move (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before)
+{
+ return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_can_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item)
+{
+ return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item)
+{
+ return FALSE;
+}
+
static GSettingsBackend *
g_settings_backend_real_delay (GSettingsBackend *backend)
{
@@ -676,6 +742,14 @@ g_settings_backend_class_init (GSettingsBackendClass *class)
class->subscribe = ignore_subscription;
class->unsubscribe = ignore_subscription;
+ class->check_exists = g_settings_backend_real_check_exists;
+ class->list = g_settings_backend_real_list;
+ class->can_insert = g_settings_backend_real_can_insert;
+ class->insert = g_settings_backend_real_insert;
+ class->can_move = g_settings_backend_real_can_move;
+ class->move = g_settings_backend_real_move;
+ class->can_remove = g_settings_backend_real_can_remove;
+ class->remove = g_settings_backend_real_remove;
class->delay = g_settings_backend_real_delay;
class->apply = ignore_apply;
class->revert = ignore_apply;
@@ -780,6 +854,80 @@ g_settings_backend_sync_default (void)
}
}
+gboolean
+g_settings_backend_check_exists (GSettingsBackend *backend,
+ const gchar *path)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->check_exists (backend, path);
+}
+
+gchar **
+g_settings_backend_list (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar * const *schema_items)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->list (backend, dir, schema_items);
+}
+
+gboolean
+g_settings_backend_can_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->can_insert (backend, dir, before);
+}
+
+gboolean
+g_settings_backend_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before,
+ gchar **item)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->insert (backend, dir, before, item);
+}
+
+gboolean
+g_settings_backend_can_move (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->can_move (backend, dir, item, before);
+}
+
+gboolean
+g_settings_backend_move (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->move (backend, dir, item, before);
+}
+
+gboolean
+g_settings_backend_can_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->can_remove (backend, dir, item);
+}
+
+gboolean
+g_settings_backend_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item)
+{
+ return G_SETTINGS_BACKEND_GET_CLASS (backend)
+ ->remove (backend, dir, item);
+}
+
GSettingsBackend *
g_settings_backend_delay (GSettingsBackend *backend)
{
diff --git a/gio/gsettingsbackend.h b/gio/gsettingsbackend.h
index b707148e7..8f1e6c627 100644
--- a/gio/gsettingsbackend.h
+++ b/gio/gsettingsbackend.h
@@ -88,6 +88,33 @@ struct _GSettingsBackendClass
GPermission * (* get_permission) (GSettingsBackend *backend,
const gchar *path);
+ gboolean (* check_exists) (GSettingsBackend *backend,
+ const gchar *path);
+ gchar ** (* list) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar * const *schema_items);
+ gboolean (* can_insert) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before);
+ gboolean (* insert) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before,
+ gchar **item);
+ gboolean (* can_move) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before);
+ gboolean (* move) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before);
+ gboolean (* can_remove) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item);
+ gboolean (* remove) (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item);
+
GSettingsBackend * (* delay) (GSettingsBackend *backend);
void (* apply) (GSettingsBackend *backend);
void (* revert) (GSettingsBackend *backend);
diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h
index c90634365..1649987fe 100644
--- a/gio/gsettingsbackendinternal.h
+++ b/gio/gsettingsbackendinternal.h
@@ -65,6 +65,42 @@ G_GNUC_INTERNAL
GPermission * g_settings_backend_get_permission (GSettingsBackend *backend,
const gchar *path);
G_GNUC_INTERNAL
+gboolean g_settings_backend_check_exists (GSettingsBackend *backend,
+ const gchar *path);
+G_GNUC_INTERNAL
+gchar ** g_settings_backend_list (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar * const *schema_items);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_can_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_insert (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *before,
+ gchar **item);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_can_move (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_move (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item,
+ const gchar *before);
+G_GNUC_INTERNAL
+GSettingsBackend * g_settings_backend_delay (GSettingsBackend *backend);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_can_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item);
+G_GNUC_INTERNAL
+gboolean g_settings_backend_remove (GSettingsBackend *backend,
+ const gchar *dir,
+ const gchar *item);
+G_GNUC_INTERNAL
GSettingsBackend * g_settings_backend_delay (GSettingsBackend *backend);
G_GNUC_INTERNAL
gboolean g_settings_backend_get_has_unapplied (GSettingsBackend *backend);