[glib] Add range restriction to gschema-compiler



commit 22ce6ed82d9d1de823fab071d6dc288abedc1735
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Apr 25 19:59:59 2010 -0500

    Add range restriction to gschema-compiler
    
    Patch from Christian Persch, with minor modifications.
    
    Bug #616102

 gio/gschema-compile.c |  174 ++++++++++++++++++++++++++++++++++++++++++++-----
 gio/gschema.dtd       |    6 +-
 gio/gsettings.c       |    6 +-
 3 files changed, 163 insertions(+), 23 deletions(-)
---
diff --git a/gio/gschema-compile.c b/gio/gschema-compile.c
index 359d721..f8b535a 100644
--- a/gio/gschema-compile.c
+++ b/gio/gschema-compile.c
@@ -47,7 +47,7 @@ typedef struct
   GvdbItem *key;
   GVariant *value;
   GVariant *min, *max;
-  GVariant *strings;
+  GString *choices;
   gchar l10n;
   gchar *context;
   GVariantType *type;
@@ -119,6 +119,67 @@ is_valid_keyname (const gchar  *key,
   return TRUE;
 }
 
+static gboolean
+type_allows_choices (const GVariantType *type)
+{
+  if (g_variant_type_is_array (type) ||
+      g_variant_type_is_maybe (type))
+    return type_allows_choices (g_variant_type_element (type));
+
+  return g_variant_type_equal (type, G_VARIANT_TYPE_STRING);
+}
+
+static gboolean
+type_allows_range (const GVariantType *type)
+{
+  static const char range_types[] = "ynqiuxtd";
+
+  return strchr (range_types, *(const char*) type) != NULL;
+}
+
+static gboolean
+is_valid_choices (GVariant    *variant,
+                  const gchar *choices)
+{
+  switch (g_variant_classify (variant))
+    {
+      case G_VARIANT_CLASS_MAYBE:
+      case G_VARIANT_CLASS_ARRAY:
+        {
+          gsize i, n;
+          GVariant *child;
+          gboolean is_valid;
+
+          n = g_variant_n_children (variant);
+          for (i = 0; i < n; ++i)
+            {
+              child = g_variant_get_child_value (variant, i);
+              is_valid = is_valid_choices (child, choices);
+              g_variant_unref (child);
+
+              if (!is_valid)
+                return FALSE;
+            }
+
+          return TRUE;
+        }
+
+      case G_VARIANT_CLASS_STRING:
+        {
+          const gchar *string;
+
+          g_variant_get (variant, "&s", &string);
+
+          while ((choices = strstr (choices, string)) && choices[-1] != 0xff);
+
+          return choices != NULL;
+        }
+
+      default:
+        g_assert_not_reached ();
+    }
+}
+
 static void
 start_element (GMarkupParseContext  *context,
                const gchar          *element_name,
@@ -317,36 +378,75 @@ start_element (GMarkupParseContext  *context,
         }
       else if (strcmp (element_name, "range") == 0)
         {
-          NO_ATTRS ();
-          return;
-        }
-    }
-  else if (strcmp (container, "range") == 0)
-    {
-      if (strcmp (element_name, "choice") == 0)
-        {
-          gchar *value;
+          const gchar *min_str, *max_str;
 
-          if (COLLECT (STRDUP, "value", &value))
+          if (!type_allows_range (state->type))
             {
+              gchar *type = g_variant_type_dup_string (state->type);
+              g_set_error (error, G_MARKUP_ERROR,
+                          G_MARKUP_ERROR_INVALID_CONTENT,
+                          "Element <%s> not allowed for keys of type \"%s\"\n",
+                          element_name, type);
+              g_free (type);
+              return;
             }
 
+          if (!COLLECT (STRING, "min", &min_str,
+                        STRING, "max", &max_str))
+            return;
+
+          state->min = g_variant_parse (state->type, min_str, NULL, NULL, error);
+          if (state->min == NULL)
+            return;
+
+          state->max = g_variant_parse (state->type, max_str, NULL, NULL, error);
+          if (state->max == NULL)
+            return;
+
+          if (g_variant_compare (state->min, state->max) > 0)
+            {
+              g_set_error (error, G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_INVALID_CONTENT,
+                           "Element <%s> specified minimum is greater than maxmimum",
+                           element_name);
+              return;
+            }
+
+          g_variant_builder_add (&state->key_options, "{sv}", "range",
+                                 g_variant_new ("(@? ?)", state->min, state->max));
           return;
         }
-      else if (strcmp (element_name, "min") == 0)
+      else if (strcmp (element_name, "choices") == 0)
         {
+          if (!type_allows_choices (state->type))
+            {
+              gchar *type = g_variant_type_dup_string (state->type);
+              g_set_error (error, G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_INVALID_CONTENT,
+                           "Element <%s> not allowed for keys of type \"%s\"\n",
+                           element_name, type);
+              g_free (type);
+              return;
+            }
+
+          state->choices = g_string_new ("\xff");
+
           NO_ATTRS ();
           return;
         }
-      else if (strcmp (element_name, "max") == 0)
+    }
+  else if (strcmp (container, "choices") == 0)
+    {
+      if (strcmp (element_name, "choice") == 0)
         {
-          NO_ATTRS ();
+          const gchar *value;
+
+          if (COLLECT (STRING, "value", &value))
+            g_string_append_printf (state->choices, "%s\xff", value);
+
           return;
         }
     }
-  else if (strcmp (container, "choice") == 0)
-    {
-    }
 
   if (container)
     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
@@ -369,6 +469,8 @@ end_element (GMarkupParseContext  *context,
     {
       state->value = g_variant_parse (state->type, state->string->str,
                                       NULL, NULL, error);
+      if (state->value == NULL)
+        return;
 
       if (state->l10n)
         {
@@ -410,9 +512,37 @@ end_element (GMarkupParseContext  *context,
           return;
         }
 
+      if (state->min != NULL)
+        {
+          if (g_variant_compare (state->value, state->min) < 0 ||
+              g_variant_compare (state->value, state->max) > 0)
+            {
+              g_set_error (error, G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_INVALID_CONTENT,
+                           "<default> is not contained in the specified range");
+              return;
+            }
+
+          state->min = state->max = NULL;
+        }
+      else if (state->choices != NULL)
+        {
+          if (!is_valid_choices (state->value, state->choices->str))
+            {
+              g_set_error_literal (error, G_MARKUP_ERROR,
+                                   G_MARKUP_ERROR_INVALID_CONTENT,
+                                   "<default> contains string not in <choices>");
+              return;
+            }
+
+          state->choices = NULL;
+        }
+
       gvdb_item_set_value (state->key, state->value);
       gvdb_item_set_options (state->key,
                              g_variant_builder_end (&state->key_options));
+
+      state->value = NULL;
     }
 
   else if (strcmp (element_name, "summary") == 0 ||
@@ -421,6 +551,16 @@ end_element (GMarkupParseContext  *context,
       g_string_free (state->string, TRUE);
       state->string = NULL;
     }
+
+  else if (strcmp (element_name, "choices") == 0)
+    {
+      gchar *choices;
+
+      choices = g_string_free (state->choices, FALSE);
+      g_variant_builder_add (&state->key_options, "{sv}", "choices",
+                             g_variant_new_byte_array (choices, -1));
+      g_free (choices);
+    }
 }
 
 static void
diff --git a/gio/gschema.dtd b/gio/gschema.dtd
index 9f7bcd7..c6fa5a6 100644
--- a/gio/gschema.dtd
+++ b/gio/gschema.dtd
@@ -24,9 +24,9 @@
 <!ELEMENT summary (#PCDATA) >
 <!ELEMENT description (#PCDATA) >
 
-<!ELEMENT range (min,max)  >
-<!ELEMENT min (#PCDATA) >
-<!ELEMENT max (#PCDATA) >
+<!ELEMENT range EMPTY >
+<!ATTLIST range min CDATA #REQUIRED
+                max CDATA #REQUIRED >
 
 <!ELEMENT choices (choice+) >
 <!ELEMENT choice (alias?) >
diff --git a/gio/gsettings.c b/gio/gsettings.c
index 3817be0..30450a8 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -100,9 +100,9 @@
  * <!ELEMENT summary (#PCDATA) >
  * <!ELEMENT description (#PCDATA) >
  *
- * <!ELEMENT range (min,max)  >
- * <!ELEMENT min (#PCDATA) >
- * <!ELEMENT max (#PCDATA) >
+ * <!ELEMENT range EMPTY >
+ * <!ATTLIST range min CDATA #REQUIRED
+ *                 max CDATA #REQUIRED >
  *
  * <!ELEMENT choices (choice+) >
  * <!ELEMENT choice (alias?) >



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]