[glib/gsettings] add GSettingsSchema and the schema cache compiler
- From: Ryan Lortie <ryanl src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [glib/gsettings] add GSettingsSchema and the schema cache compiler
- Date: Tue, 1 Sep 2009 16:39:59 +0000 (UTC)
commit 06e0da688408b498c916e3e29325180b32b3ff67
Author: Ryan Lortie <desrt desrt ca>
Date: Tue Sep 1 12:39:20 2009 -0400
add GSettingsSchema and the schema cache compiler
gio-2.0.pc.in | 2 +
gio/.gitignore | 1 +
gio/Makefile.am | 9 +-
gio/gio.symbols | 14 +
gio/gsettings-update-schema-cache.c | 875 +++++++++++++++++++++++++++++++++++
gio/gsettingsschema.c | 164 +++++++
gio/gsettingsschema.h | 67 +++
gio/pltcheck.sh | 2 +-
8 files changed, 1132 insertions(+), 2 deletions(-)
---
diff --git a/gio-2.0.pc.in b/gio-2.0.pc.in
index baefef1..e96897b 100644
--- a/gio-2.0.pc.in
+++ b/gio-2.0.pc.in
@@ -4,6 +4,8 @@ libdir= libdir@
includedir= includedir@
giomoduledir= GIO_MODULE_DIR@
+gsettingsschemadir= datadir@/gsettings/schemas
+gsettingsupdateschemacache= libexecdir@/gsettings-update-schema-cache
Name: GIO
Description: glib I/O library
diff --git a/gio/.gitignore b/gio/.gitignore
index 14da934..4a3c312 100644
--- a/gio/.gitignore
+++ b/gio/.gitignore
@@ -2,3 +2,4 @@ gio-marshal.[ch]
gioenumtypes.[ch]
gioalias.h
gioaliasdef.c
+gsettings-update-schema-cache
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 17ef981..146991e 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -50,7 +50,8 @@ AM_CPPFLAGS = \
$(GLIB_DEBUG_FLAGS) \
-DG_DISABLE_DEPRECATED \
-DGIO_COMPILATION \
- -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"
+ -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \
+ -DGSETTINGS_SCHEMAS_DIR=\"$(datadir)/gsettings/schemas\"
lib_LTLIBRARIES = libgio-2.0.la
@@ -237,6 +238,7 @@ libgio_2_0_la_SOURCES = \
gresolver.c \
gseekable.c \
gsettingsbackend.c \
+ gsettingsschema.c \
gsimpleasyncresult.c \
gsocket.c \
gsocketaddress.c \
@@ -366,6 +368,7 @@ gio_headers = \
gresolver.h \
gseekable.h \
gsettingsbackend.h \
+ gsettingsschema.h \
gsimpleasyncresult.h \
gsocket.h \
gsocketaddress.h \
@@ -436,3 +439,7 @@ install-data-hook:
rm -f $(DESTDIR)$(libdir)/libgio-2.0.so
ln -s $(GLIB_RUNTIME_LIBDIR)/libgio-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/libgio-2.0.so
endif
+
+libexec_PROGRAMS = gsettings-update-schema-cache
+gsettings_update_schema_cache_SOURCES = gsettings-update-schema-cache.c
+gsettings_update_schema_cache_LDADD = ../glib/libglib-2.0.la
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 890e1bd..fcc73b4 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1272,3 +1272,17 @@ g_settings_backend_changed
g_settings_backend_changed_tree
#endif
#endif
+
+#if IN_HEADER(_gsettingsschema_h_)
+#if IN_FILE(_gsettingsschema_c_)
+g_settings_schema_check_key
+g_settings_schema_get_inheritance
+g_settings_schema_get_key_default
+g_settings_schema_get_key_format
+g_settings_schema_get_path
+g_settings_schema_get_schema
+g_settings_schema_get_type
+g_settings_schema_new
+g_settings_schema_type_check_key
+#endif
+#endif
diff --git a/gio/gsettings-update-schema-cache.c b/gio/gsettings-update-schema-cache.c
new file mode 100644
index 0000000..0082faf
--- /dev/null
+++ b/gio/gsettings-update-schema-cache.c
@@ -0,0 +1,875 @@
+/*
+ * Copyright © 2009 Codethink Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include <glib/gstdio.h>
+#include <glib.h>
+
+#include <string.h>
+#include <errno.h>
+
+typedef enum
+{
+ TOKEN_INVALID,
+ TOKEN_EOF,
+ TOKEN_INDENT,
+ TOKEN_UNINDENT,
+ TOKEN_LEFT_PAREN,
+ TOKEN_RIGHT_PAREN,
+ TOKEN_NEXT,
+ TOKEN_FORMAT,
+ TOKEN_EQUAL,
+ TOKEN_KEY,
+ TOKEN_PATH,
+ TOKEN_CHILD,
+ TOKEN_ITEM,
+ TOKEN_SCHEMA,
+ TOKEN_WORD
+} TokenType;
+
+typedef struct
+{
+ gchar *error_message;
+ const gchar *error_start;
+ const gchar *error_end;
+
+ const gchar *stream;
+ const gchar *start;
+ const gchar *end;
+
+ GSList *indent_stack;
+ gint this_indent;
+
+ TokenType this_type;
+ gchar *this_detail;
+} TokenStream;
+
+typedef struct
+{
+ gchar *name;
+
+ gchar *format_string;
+ GVariant *value;
+} Key;
+
+typedef struct
+{
+ gboolean is_list;
+ gchar *name;
+ gchar *base_path;
+
+ GHashTable *children;
+ GHashTable *keys;
+} Schema;
+
+typedef struct
+{
+ GHashTable *schemas;
+} Cache;
+
+static void
+token_stream_range_error_literal (TokenStream *stream,
+ const gchar *start,
+ const gchar *end,
+ const gchar *message)
+{
+ g_assert (stream->error_message == NULL);
+ stream->error_message = g_strdup (message);
+ stream->error_start = start;
+ stream->error_end = end;
+}
+
+static void
+token_stream_range_error (TokenStream *stream,
+ const gchar *start,
+ const gchar *end,
+ const gchar *format,
+ ...)
+{
+ gchar *message;
+ va_list ap;
+
+ va_start (ap, format);
+ message = g_strdup_vprintf (format, ap);
+ token_stream_range_error_literal (stream, start, end, message);
+ g_free (message);
+ va_end (ap);
+}
+
+static void
+token_stream_error (TokenStream *stream,
+ const gchar *format,
+ ...)
+{
+ gchar *message;
+ va_list ap;
+
+ va_start (ap, format);
+ message = g_strdup_vprintf (format, ap);
+ token_stream_range_error_literal (stream, stream->stream,
+ stream->stream, message);
+ g_free (message);
+ va_end (ap);
+}
+
+static void
+token_stream_push_indent (TokenStream *stream,
+ gint indent)
+{
+ gpointer old_indent = GINT_TO_POINTER (stream->this_indent);
+ stream->indent_stack = g_slist_prepend (stream->indent_stack, old_indent);
+ stream->this_indent = indent;
+}
+
+static void
+token_stream_pop_indent (TokenStream *stream)
+{
+ stream->this_indent = GPOINTER_TO_INT (stream->indent_stack->data);
+ stream->indent_stack = g_slist_delete_link (stream->indent_stack,
+ stream->indent_stack);
+}
+
+static gboolean
+token_stream_prepare (TokenStream *stream)
+{
+ if (stream->this_type != TOKEN_INVALID)
+ return TRUE;
+
+ while (stream->stream < stream->end && *stream->stream == ' ')
+ stream->stream++;
+
+ if (stream->stream == stream->end)
+ {
+ if (stream->indent_stack != NULL)
+ {
+ token_stream_pop_indent (stream);
+ stream->this_type = TOKEN_UNINDENT;
+ }
+ else
+ stream->this_type = TOKEN_EOF;
+
+ return TRUE;
+ }
+
+ switch (stream->stream[0])
+ {
+ case '\n':
+ {
+ const gchar *next_line = stream->stream;
+ gint spaces = 0;
+
+ while (next_line[spaces] == '\n')
+ {
+ next_line += spaces + 1;
+
+ for (spaces = 0; next_line[spaces] == ' '; spaces++);
+ }
+
+ if (spaces > stream->this_indent)
+ {
+ token_stream_range_error (stream, stream->stream,
+ &stream->stream[spaces],
+ "unexpected indent");
+ return FALSE;
+ }
+
+ if (spaces < stream->this_indent)
+ {
+ token_stream_pop_indent (stream);
+
+ if (spaces > stream->this_indent)
+ {
+ token_stream_range_error (stream, stream->stream,
+ &stream->stream[spaces],
+ "unindent does not match any "
+ "outer indentation level");
+ return FALSE;
+ }
+
+ if (spaces == stream->this_indent)
+ stream->stream = next_line + spaces;
+
+ stream->this_type = TOKEN_UNINDENT;
+ return TRUE;
+ }
+
+ else /* spaces == stream->this_indent */
+ {
+ stream->stream = next_line + spaces;
+ stream->this_type = TOKEN_NEXT;
+
+ return TRUE;
+ }
+ }
+
+ case ':':
+ {
+ const gchar *next_line;
+ gint spaces = 0;
+
+ if (stream->stream[1] != '\n')
+ {
+ char character[2] = { stream->stream[1] };
+ gchar *escaped;
+
+ escaped = g_strescape (character, NULL);
+ token_stream_range_error (stream, stream->stream + 1,
+ stream->stream + 1,
+ "expected newline immediately "
+ "following colon (not `%s')", escaped);
+ g_free (escaped);
+
+ return FALSE;
+ }
+
+ next_line = stream->stream + 1;
+ while (next_line[spaces] == '\n')
+ {
+ next_line += spaces + 1;
+
+ for (spaces = 0; next_line[spaces] == ' '; spaces++);
+ }
+
+ if (spaces <= stream->this_indent)
+ {
+ token_stream_range_error (stream, next_line, next_line + spaces,
+ "expected an indented block");
+ return FALSE;
+ }
+
+ token_stream_push_indent (stream, spaces);
+
+ stream->stream = next_line + spaces;
+ stream->this_type = TOKEN_INDENT;
+ return TRUE;
+ }
+
+ case '=':
+ stream->this_type = TOKEN_EQUAL;
+ stream->stream++;
+ return TRUE;
+
+ case '<':
+ {
+ const gchar *format, *end;
+
+ format = stream->stream + 1;
+
+ for (end = format; end < stream->end; end++)
+ if (*end == '>' || *end == '\n')
+ break;
+
+ if (end == stream->end || *end == '\n')
+ {
+ token_stream_range_error (stream, format, end,
+ "unterminated format string");
+ return FALSE;
+ }
+
+ stream->this_detail = g_strndup (format, end - format);
+ stream->this_type = TOKEN_FORMAT;
+ stream->stream = end + 1;
+
+ return TRUE;
+ }
+
+ case '(':
+ stream->this_type = TOKEN_LEFT_PAREN;
+ stream->stream++;
+ return TRUE;
+
+ case ')':
+ stream->this_type = TOKEN_RIGHT_PAREN;
+ stream->stream++;
+ return TRUE;
+
+ default:
+ {
+ const gchar *word = stream->stream;
+ const gchar *end;
+
+ for (end = word; end < stream->end; end++)
+ if (*end == '\0' ||
+ !strchr ("0123456789abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_./", *end))
+ break;
+
+ if (end == word)
+ {
+ char character[2] = { *word, '\0' };
+ char *escaped;
+
+ escaped = g_strescape (character, NULL);
+ token_stream_range_error (stream, stream->stream,
+ stream->stream + 1,
+ "stray `%s' in schema file", escaped);
+ g_free (escaped);
+
+ return FALSE;
+ }
+
+ if (end - word == 3 && memcmp (word, "key", 3) == 0)
+ stream->this_type = TOKEN_KEY;
+
+ else if (end - word == 4 && memcmp (word, "path", 4) == 0)
+ stream->this_type = TOKEN_PATH;
+
+ else if (end - word == 4 && memcmp (word, "item", 4) == 0)
+ stream->this_type = TOKEN_ITEM;
+
+ else if (end - word == 5 && memcmp (word, "child", 5) == 0)
+ stream->this_type = TOKEN_CHILD;
+
+ else if (end - word == 6 && memcmp (word, "schema", 6) == 0)
+ stream->this_type = TOKEN_SCHEMA;
+
+ else
+ {
+ stream->this_detail = g_strndup (word, end - word);
+ stream->this_type = TOKEN_WORD;
+ }
+
+ stream->stream = end;
+ return TRUE;
+ }
+ }
+}
+
+static gboolean
+token_stream_get (TokenStream *stream,
+ TokenType *type,
+ const gchar **detail)
+{
+ token_stream_prepare (stream);
+
+ if (detail)
+ *detail = stream->this_detail;
+ *type = stream->this_type;
+
+ stream->this_detail = 0;
+ stream->this_type = 0;
+
+ return TRUE;
+}
+
+static gboolean
+token_stream_consume_word (TokenStream *stream,
+ gchar **result,
+ const gchar *role)
+{
+ const gchar *detail;
+ TokenType type;
+
+ if (!token_stream_get (stream, &type, &detail))
+ return FALSE;
+
+ if (type != TOKEN_WORD)
+ {
+ token_stream_error (stream, "expected %s", role);
+ return FALSE;
+ }
+
+ *result = g_strdup (detail);
+
+ return TRUE;
+}
+
+static TokenType
+token_stream_consume_symbol (TokenStream *stream)
+{
+ TokenType type;
+
+ if (!token_stream_get (stream, &type, NULL))
+ return FALSE;
+
+ return type;
+}
+
+static gboolean
+token_stream_expect (TokenStream *stream,
+ TokenType expected_type,
+ const gchar *symbol)
+{
+ TokenType type;
+
+ if (!token_stream_get (stream, &type, NULL))
+ return FALSE;
+
+ if (type != expected_type)
+ {
+ token_stream_error (stream, "expected `%s'", symbol);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+free_key (gpointer data)
+{
+ Key *key = data;
+
+ g_variant_unref (key->value);
+ g_free (key->format_string);
+ g_slice_free (Key, key);
+}
+
+static gboolean
+read_key (TokenStream *stream,
+ GHashTable *table)
+{
+ GVariantType *format_type = NULL;
+ GVariantParseError error = { };
+ const gchar *format;
+ TokenType type;
+ Key *key;
+
+ key = g_slice_new (Key);
+ key->name = NULL;
+ key->format_string = NULL;
+
+ if (!token_stream_consume_word (stream, &key->name, "key name"))
+ goto fail;
+
+ if (!token_stream_get (stream, &type, &format))
+ goto fail;
+
+ if (type == TOKEN_FORMAT)
+ {
+ key->format_string = g_strdup (format);
+ format_type = g_variant_format_string_scan_type (key->format_string,
+ NULL, NULL);
+
+ if (format_type == NULL)
+ {
+ token_stream_error (stream, "invalid GVariant format string");
+ goto fail;
+ }
+
+ if (!token_stream_get (stream, &type, NULL))
+ goto fail;
+ }
+ else
+ format_type = NULL;
+
+ if (type != TOKEN_EQUAL)
+ {
+ if (key->format_string == NULL)
+ token_stream_error (stream, "expected `<' or `='");
+ else
+ token_stream_error (stream, "expected `='");
+
+ goto fail;
+ }
+
+ key->value = g_variant_parse_full (stream->stream, stream->end,
+ &stream->stream, format_type,
+ &error);
+
+ if (key->value == NULL)
+ {
+ token_stream_range_error_literal (stream, error.start,
+ error.end, error.error);
+ goto fail;
+ }
+
+ if (key->format_string == NULL)
+ key->format_string = g_strdup (g_variant_get_type_string (key->value));
+
+ g_hash_table_insert (table, key->name, key);
+ g_variant_type_free (format_type);
+
+ return TRUE;
+
+ fail:
+ if (format_type != NULL)
+ g_variant_type_free (format_type);
+
+ g_free (key->format_string);
+
+ g_slice_free (Key, key);
+
+ return FALSE;
+}
+
+static gboolean
+read_schema (TokenStream *stream,
+ GHashTable *table,
+ const gchar *your_name)
+{
+ TokenType type;
+ Schema *schema;
+
+ schema = g_slice_new (Schema);
+ schema->name = g_strdup (your_name);
+ schema->is_list = FALSE;
+ schema->base_path = NULL;
+ schema->keys = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, free_key);
+ schema->children = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ if (schema->name == NULL &&
+ !token_stream_consume_word (stream, &schema->name, "schema name"))
+ goto fail;
+
+ if (!token_stream_get (stream, &type, NULL))
+ goto fail;
+
+ if (type == TOKEN_LEFT_PAREN)
+ {
+ gchar *parent_name;
+
+ if (!token_stream_consume_word (stream, &parent_name,
+ "parent schema name"))
+ goto fail;
+
+ if (strcmp (parent_name, "list") != 0)
+ {
+ token_stream_error (stream,
+ "inheritance only supported for 'list'");
+ g_free (parent_name);
+ goto fail;
+ }
+
+ schema->is_list = TRUE;
+ g_free (parent_name);
+
+ if (!token_stream_expect (stream, TOKEN_RIGHT_PAREN, ")"))
+ goto fail;
+
+ if (!token_stream_get (stream, &type, NULL))
+ goto fail;
+ }
+
+ if (type != TOKEN_INDENT)
+ {
+ if (schema->is_list == FALSE)
+ token_stream_error (stream, "expected `:' or `('");
+ else
+ token_stream_error (stream, "expected `:'");
+
+ goto fail;
+ }
+
+ while (type != TOKEN_UNINDENT)
+ {
+ switch (token_stream_consume_symbol (stream))
+ {
+ case TOKEN_PATH:
+ if (schema->base_path != NULL)
+ {
+ token_stream_error (stream,
+ "path already given for this schema");
+ goto fail;
+ }
+
+ if (!token_stream_consume_word (stream, &schema->base_path,
+ "schema path"))
+ goto fail;
+
+ break;
+
+ case TOKEN_CHILD:
+ {
+ gchar *item_name;
+ gchar *name;
+
+ if (!token_stream_consume_word (stream, &name, "child name"))
+ goto fail;
+
+ item_name = g_strdup_printf ("%s/%s", schema->name, name);
+ if (!read_schema (stream, table, item_name))
+ goto fail;
+
+ g_hash_table_insert (schema->children, name, item_name);
+ }
+ break;
+
+ case TOKEN_ITEM:
+ {
+ gchar *item_name;
+ item_name = g_strdup_printf ("%s/*item", schema->name);
+ if (!read_schema (stream, table, item_name))
+ goto fail;
+ g_free (item_name);
+ }
+ break;
+
+ case TOKEN_KEY:
+ read_key (stream, schema->keys);
+ break;
+
+ default:
+ token_stream_error (stream, "expected something here");
+ goto fail;
+ }
+
+ if (!token_stream_get (stream, &type, NULL))
+ goto fail;
+
+ if (type != TOKEN_UNINDENT && type != TOKEN_NEXT)
+ {
+ token_stream_error (stream, "expected end of line");
+ goto fail;
+ }
+ }
+
+ g_hash_table_insert (table, schema->name, schema);
+
+ return TRUE;
+
+ fail:
+ g_hash_table_unref (schema->children);
+ g_hash_table_unref (schema->keys);
+ g_free (schema->base_path);
+
+ g_slice_free (Schema, schema);
+
+ return FALSE;
+}
+
+static gboolean
+read_file (TokenStream *stream,
+ Cache *cache)
+{
+ TokenType type;
+
+ for (;;)
+ {
+ if (!token_stream_get (stream, &type, NULL))
+ return FALSE;
+
+ if (type == TOKEN_EOF)
+ return TRUE;
+
+ if (type != TOKEN_SCHEMA)
+ {
+ token_stream_error (stream, "expected `schema' or end of file");
+ return FALSE;
+ }
+
+ if (!read_schema (stream, cache->schemas, NULL))
+ return FALSE;
+ }
+}
+
+static GVariant *
+serialise_key (Key *key)
+{
+ return g_variant_new ("(sv)", key->format_string, key->value);
+}
+
+static void
+append_keys_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ g_variant_builder_add (user_data, "{s@(sv)}", key, serialise_key (value));
+}
+
+static void
+append_children_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ g_variant_builder_add (user_data, "{ss}", key, value);
+}
+
+static GVariant *
+serialise_schema (Schema *schema)
+{
+ GVariantBuilder *keys, *children, *parents;
+
+ keys = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+ G_VARIANT_TYPE ("a{s(sv)}"));
+ children = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+ G_VARIANT_TYPE ("a{ss}"));
+ parents = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+ G_VARIANT_TYPE ("as"));
+ g_hash_table_foreach (schema->keys, append_keys_entry, keys);
+ g_hash_table_foreach (schema->children, append_children_entry, children);
+
+ if (schema->is_list)
+ g_variant_builder_add (parents, "s", "list");
+
+ return g_variant_new ("(asmsa{s(sv)}a{ss})",
+ parents, schema->base_path, keys, children);
+}
+
+static void
+append_cache_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ g_variant_builder_add (user_data, "{s@(asmsa{s(sv)}a{ss})}",
+ key, serialise_schema (value));
+}
+
+static GVariant *
+serialise_cache (Cache *cache)
+{
+ GVariantBuilder *list;
+
+ list = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+ G_VARIANT_TYPE ("a{s(asmsa{s(sv)}a{ss})}"));
+ g_hash_table_foreach (cache->schemas, append_cache_entry, list);
+
+ return g_variant_builder_end (list);
+}
+
+static void
+schema_free (gpointer data)
+{
+ Schema *schema = data;
+
+ g_hash_table_unref (schema->keys);
+ g_hash_table_unref (schema->children);
+ g_free (schema->base_path);
+
+ g_slice_free (Schema, schema);
+}
+
+Cache *
+cache_new (void)
+{
+ Cache *cache;
+
+ cache = g_slice_new (Cache);
+ cache->schemas = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, schema_free);
+
+ return cache;
+}
+
+static void
+show_error_message (TokenStream *stream,
+ const gchar *filename)
+{
+ const gchar *position, *line_start;
+ gint line, column;
+ gchar *err_line;
+ gint err_len;
+ gint i;
+
+ line_start = position = stream->start;
+ column = line = 1;
+
+ while (position < stream->error_start)
+ if (*position++ == '\n')
+ {
+ line_start = position;
+ column = 1;
+ line++;
+ }
+ else
+ column++;
+
+ while (position < stream->error_end)
+ {
+ g_assert (*position != '\n'); /* XXX not true anymore */
+ position++;
+ }
+
+ while (position < stream->end && *position != '\n')
+ position++;
+
+ err_line = g_strndup (line_start, position - line_start);
+ err_len = stream->error_end - stream->error_start;
+ if (err_len == 0)
+ err_len = 1;
+
+ g_print ("\n\n%s:%d.%d-%d.%d: %s\n",
+ filename, line, column, line, column + err_len - 1,
+ stream->error_message);
+
+ g_print ("%s\n", err_line);
+ for (i = 1; i < column; i++)
+ g_print (" ");
+ for (i = 0; i < err_len; i++)
+ g_print ("^");
+ g_print ("\n");
+
+ g_error ("fatal error\n");
+}
+
+static void
+write_cache (Cache *cache)
+{
+ GVariant *value = serialise_cache (cache);
+ GError *error = NULL;
+ gconstpointer data;
+ gsize length;
+
+ data = g_variant_get_data (value);
+ length = g_variant_get_size (value);
+
+ if (!g_file_set_contents ("schemas-cache", data, length, &error))
+ g_error ("unable to write schema cache: %s\n", error->message);
+}
+
+static void
+compile_directory (const gchar *dirname)
+{
+ const gchar *filename;
+ GError *error = NULL;
+ Cache *cache;
+ GDir *dir;
+
+ if (g_chdir (dirname) < 0)
+ g_error ("unable to change to directory %s: %s\n",
+ dirname, g_strerror (errno));
+
+ dir = g_dir_open (".", 0, &error);
+
+ if (dir == NULL)
+ g_error ("opening %s: %s\n", dirname, error->message);
+
+ cache = cache_new ();
+
+ while ((filename = g_dir_read_name (dir)))
+ if (g_str_has_suffix (filename, ".schemas"))
+ {
+ TokenStream stream = { };
+ gchar *contents;
+ gsize length;
+
+ if (!g_file_get_contents (filename, &contents, &length, &error))
+ g_error ("%s/%s: %s\n", dirname, filename, error->message);
+
+ stream.stream = contents;
+ stream.start = contents;
+ stream.end = contents + length;
+
+ if (!read_file (&stream, cache))
+ {
+ gchar *path = g_strdup_printf ("%s/%s", dirname, filename);
+ show_error_message (&stream, path);
+ g_assert_not_reached ();
+ }
+
+ g_free (contents);
+ }
+
+ g_dir_close (dir);
+
+ write_cache (cache);
+}
+
+int
+main (int argc, char **argv)
+{
+ if (argc == 1)
+ g_warning ("nothing to do");
+
+ while (*++argv)
+ compile_directory (*argv);
+
+ return 0;
+}
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
new file mode 100644
index 0000000..4d87980
--- /dev/null
+++ b/gio/gsettingsschema.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2009 Codethink Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include "gsettingsschema.h"
+
+#include <string.h>
+
+#include "gioalias.h"
+
+G_DEFINE_TYPE (GSettingsSchema, g_settings_schema, G_TYPE_OBJECT);
+
+struct _GSettingsSchemaPrivate
+{
+ GVariant *schema;
+
+ /* cache these */
+ GVariant *keys;
+ GVariant *children;
+};
+
+static void
+g_settings_schema_init (GSettingsSchema *schema)
+{
+ schema->priv = G_TYPE_INSTANCE_GET_PRIVATE (schema,
+ G_TYPE_SETTINGS_SCHEMA,
+ GSettingsSchemaPrivate);
+}
+
+GSettingsSchema *
+g_settings_schema_new (const gchar *schema_name)
+{
+ static GVariant *schemas;
+ GSettingsSchema *schema;
+ GVariant *value;
+
+ if G_UNLIKELY (g_once_init_enter ((gsize *) &schemas))
+ {
+ GError *error = NULL;
+ GVariant *tmp;
+
+ tmp = g_variant_from_file (G_VARIANT_TYPE ("a{s(asmsa{s(sv)}a{ss})}"),
+ GSETTINGS_SCHEMAS_DIR "/schemas-cache", 0,
+ &error);
+
+ if G_UNLIKELY (tmp == NULL)
+ g_error ("open schema file: %s\n", error->message);
+
+ g_once_init_leave ((gsize *) &schemas, (gsize) tmp);
+ }
+
+ schema = g_object_new (G_TYPE_SETTINGS_SCHEMA, NULL);
+ value = g_variant_lookup_value (schemas, schema_name);
+
+ if (value == NULL)
+ g_error ("Invalid schema name '%s'", schema_name);
+
+ schema->priv->schema = value;
+ schema->priv->keys = g_variant_get_child_value (value, 2);
+ schema->priv->children = g_variant_get_child_value (value, 3);
+
+ return schema;
+}
+
+static void
+g_settings_schema_finalize (GObject *object)
+{
+ GSettingsSchema *schema = G_SETTINGS_SCHEMA (object);
+
+ g_variant_unref (schema->priv->keys);
+ g_variant_unref (schema->priv->children);
+ g_variant_unref (schema->priv->schema);
+}
+
+static void
+g_settings_schema_class_init (GSettingsSchemaClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (GSettingsSchemaPrivate));
+
+ object_class->finalize = g_settings_schema_finalize;
+}
+
+const gchar *
+g_settings_schema_get_path (GSettingsSchema *schema)
+{
+ const gchar *path;
+
+ g_variant_get_child (schema->priv->schema, 1, "m&s", &path);
+
+ return path;
+}
+
+const gchar *
+g_settings_schema_get_key_format (GSettingsSchema *schema,
+ const gchar *key)
+{
+ const gchar *format_string;
+
+ if (!g_variant_lookup (schema->priv->keys, key,
+ "(&sv)", &format_string, NULL))
+ g_error ("Can not look up format string for key '%s'\n", key);
+
+ return format_string;
+}
+
+GVariant *
+g_settings_schema_get_key_default (GSettingsSchema *schema,
+ const gchar *key)
+{
+ GVariant *value;
+
+ if (!g_variant_lookup (schema->priv->keys, key, "(sv)", NULL, &value))
+ g_error ("Can not look up default value for key '%s'\n", key);
+
+ return value;
+}
+
+gboolean
+g_settings_schema_type_check_key (GSettingsSchema *schema,
+ const gchar *key,
+ GVariant *value)
+{
+ const gchar *fmt;
+
+ fmt = g_settings_schema_get_key_format (schema, key);
+ /* XXX arg. fmt->type is not public API. fix this somehow. */
+ return g_variant_matches (value, G_VARIANT_TYPE (fmt));
+}
+
+gboolean
+g_settings_schema_check_key (GSettingsSchema *schema,
+ const gchar *key)
+{
+ return g_variant_lookup (schema->priv->keys, key, NULL);
+}
+
+const gchar *
+g_settings_schema_get_schema (GSettingsSchema *schema,
+ const gchar *item)
+{
+ const gchar *schema_name;
+
+ if (!g_variant_lookup (schema->priv->children, item, "&s", &schema_name))
+ return NULL;
+
+ return schema_name;
+}
+
+GVariant *
+g_settings_schema_get_inheritance (GSettingsSchema *schema)
+{
+ return g_variant_get_child_value (schema->priv->schema, 0);
+}
+
+#define _gsettingsschema_c_
+#include "gioaliasdef.c"
diff --git a/gio/gsettingsschema.h b/gio/gsettingsschema.h
new file mode 100644
index 0000000..e01059b
--- /dev/null
+++ b/gio/gsettingsschema.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2009 Codethink Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef _gsettingsschema_h_
+#define _gsettingsschema_h_
+
+#include <glib-object.h>
+
+#define G_TYPE_SETTINGS_SCHEMA (g_settings_schema_get_type ())
+#define G_SETTINGS_SCHEMA(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ G_TYPE_SETTINGS_SCHEMA, GSettingsSchema))
+#define G_SETTINGS_SCHEMA_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
+ G_TYPE_SETTINGS_SCHEMA, GSettingsSchemaClass))
+#define G_IS_SETTINGS_SCHEMA(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ G_TYPE_SETTINGS_SCHEMA))
+#define G_IS_SETTINGS_SCHEMA_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+ G_TYPE_SETTINGS_SCHEMA))
+#define G_SETTINGS_SCHEMA_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
+ G_TYPE_SETTINGS_SCHEMA, GSettingsSchemaClass))
+
+typedef struct _GSettingsSchemaPrivate GSettingsSchemaPrivate;
+typedef struct _GSettingsSchemaClass GSettingsSchemaClass;
+typedef struct _GSettingsSchema GSettingsSchema;
+
+struct _GSettingsSchemaClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GSettingsSchema
+{
+ GObject parent_instance;
+ GSettingsSchemaPrivate *priv;
+};
+
+G_BEGIN_DECLS
+
+GType g_settings_schema_get_type (void);
+GSettingsSchema * g_settings_schema_new (const gchar *schema_name);
+GVariant * g_settings_schema_get_inheritance (GSettingsSchema *schema);
+const gchar * g_settings_schema_get_path (GSettingsSchema *schema);
+
+const gchar * g_settings_schema_get_key_format (GSettingsSchema *schema,
+ const gchar *key);
+GVariant * g_settings_schema_get_key_default (GSettingsSchema *schema,
+ const gchar *key);
+gboolean g_settings_schema_type_check_key (GSettingsSchema *schema,
+ const gchar *key,
+ GVariant *value);
+gboolean g_settings_schema_check_key (GSettingsSchema *schema,
+ const gchar *key);
+
+const gchar * g_settings_schema_get_schema (GSettingsSchema *schema,
+ const gchar *item);
+
+GVariant * g_settings_schema_get_list_items (GSettingsSchema *schema);
+
+G_END_DECLS
+
+#endif /* _gsettings_h_ */
diff --git a/gio/pltcheck.sh b/gio/pltcheck.sh
index 3e18f3a..eb1121c 100755
--- a/gio/pltcheck.sh
+++ b/gio/pltcheck.sh
@@ -9,7 +9,7 @@ if ! which readelf 2>/dev/null >/dev/null; then
exit 0
fi
-SKIP='\<g_access\|\<g_array_\|\<g_ascii\|\<g_list_\|\<g_tree_\|\<g_assertion_message\|\<g_warn_message\|\<g_atomic\|\<g_bit_\|\<g_boxed\|\<g_build_filename\|\<g_byte_array\|\<g_checksum\|\<g_child_watch\|\<g_clear_error\|\<g_convert\|\<g_dir_\|\<g_enum_\|\<g_error_\|\<g_prefix_error\|\<g_file_error_quark\|\<g_file_get_contents\|\<g_file_set_contents\|\<g_file_test\|\<g_file_read_link\|\<g_filename_\|\<g_find_program_in_path\|\<g_flags_\|\<g_free\|\<g_get_\|\<g_getenv\|\<g_setenv\|\<g_hash_table_\|\<g_hostname_\|\<g_idle_\|\<g_intern_static_string\|\<g_io_add_watch\|\<g_io_channel_\|\<g_io_create_watch\|\<g_key_file_\|\<g_listenv\|\<g_locale_to_utf8\|\<g_log\|\<g_main_context_\|\<g_main_current_source\|\<g_main_loop_\|\<g_malloc\|\<g_markup_\|\<g_mkdir_\|\<g_mkstemp\|\<g_module_\|\<g_object_\|\<g_once_\|\<g_param_spec_\|\<g_path_\|\<g_poll\|\<g_printerr\|\<g_propagate_error\|\<g_ptr_array_\|\<g_qsort_\|\<g_quark_\|\<g_queue_\|\<g_random_int_range\|\<g_realloc\|\<g_return_if_f
ail\|\<g_set_error\|\<g_shell_\|\<g_signal_\|\<g_slice_\|\<g_slist_\|\<g_snprintf\|\<g_source_\|\<g_spawn_\|\<g_static_\|\<g_str\|\<g_thread_pool_\|\<g_time_val_add\|\<g_timeout_\|\<g_type_\|\<g_unlink\|\<g_uri_\|\<g_utf8_\|\<g_value_'
+SKIP='\<g_variant\|\<g_access\|\<g_array_\|\<g_ascii\|\<g_list_\|\<g_tree_\|\<g_assertion_message\|\<g_warn_message\|\<g_atomic\|\<g_bit_\|\<g_boxed\|\<g_build_filename\|\<g_byte_array\|\<g_checksum\|\<g_child_watch\|\<g_clear_error\|\<g_convert\|\<g_dir_\|\<g_enum_\|\<g_error_\|\<g_prefix_error\|\<g_file_error_quark\|\<g_file_get_contents\|\<g_file_set_contents\|\<g_file_test\|\<g_file_read_link\|\<g_filename_\|\<g_find_program_in_path\|\<g_flags_\|\<g_free\|\<g_get_\|\<g_getenv\|\<g_setenv\|\<g_hash_table_\|\<g_hostname_\|\<g_idle_\|\<g_intern_static_string\|\<g_io_add_watch\|\<g_io_channel_\|\<g_io_create_watch\|\<g_key_file_\|\<g_listenv\|\<g_locale_to_utf8\|\<g_log\|\<g_main_context_\|\<g_main_current_source\|\<g_main_loop_\|\<g_malloc\|\<g_markup_\|\<g_mkdir_\|\<g_mkstemp\|\<g_module_\|\<g_object_\|\<g_once_\|\<g_param_spec_\|\<g_path_\|\<g_poll\|\<g_printerr\|\<g_propagate_error\|\<g_ptr_array_\|\<g_qsort_\|\<g_quark_\|\<g_queue_\|\<g_random_int_range\|\<g_realloc\|\<
g_return_if_fail\|\<g_set_error\|\<g_shell_\|\<g_signal_\|\<g_slice_\|\<g_slist_\|\<g_snprintf\|\<g_source_\|\<g_spawn_\|\<g_static_\|\<g_str\|\<g_thread_pool_\|\<g_time_val_add\|\<g_timeout_\|\<g_type_\|\<g_unlink\|\<g_uri_\|\<g_utf8_\|\<g_value_'
for so in .libs/lib*.so; do
echo Checking $so for local PLT entries
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]