[glib/gsettings] add GSettingsSchema and the schema cache compiler



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]