[glib] gmarkup: Add g_markup_collect_known_attributes()



commit 8d40389d15544bdc612989157f80380badce52f7
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Aug 16 23:40:08 2012 -0400

    gmarkup: Add g_markup_collect_known_attributes()
    
    Add a variant of g_markup_collect_attributes() which will
    ignore unknown attributes (such as those from different XML
    namespaces) when parsing markup, rather than returning
    G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE as g_markup_collect_attributes()
    does.
    
    Patch by Philip Withnall,
    https://bugzilla.gnome.org/show_bug.cgi?id=665634

 docs/reference/glib/glib-sections.txt |    1 +
 glib/glib.symbols                     |    1 +
 glib/gmarkup.c                        |  218 ++++++++++++++++++++++-----------
 glib/gmarkup.h                        |    9 ++
 glib/tests/markup-collect.c           |  125 +++++++++++++-------
 5 files changed, 242 insertions(+), 112 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index dacacbb..4c0071c 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -1125,6 +1125,7 @@ g_markup_parse_context_pop
 <SUBSECTION>
 GMarkupCollectType
 g_markup_collect_attributes
+g_markup_collect_known_attributes
 <SUBSECTION Private>
 g_markup_error_quark
 </SECTION>
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 0490b66..dbd9246 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -683,6 +683,7 @@ g_markup_parse_context_pop
 g_markup_printf_escaped
 g_markup_vprintf_escaped
 g_markup_collect_attributes
+g_markup_collect_known_attributes
 g_free
 g_clear_pointer
 g_malloc
diff --git a/glib/gmarkup.c b/glib/gmarkup.c
index b0c28a9..4fda725 100644
--- a/glib/gmarkup.c
+++ b/glib/gmarkup.c
@@ -2549,84 +2549,39 @@ g_markup_parse_boolean (const char  *string,
  *     is set depending on what value type is used
  *
  * A mixed enumerated type and flags field. You must specify one type
- * (string, strdup, boolean, tristate).  Additionally, you may  optionally
+ * (string, strdup, boolean, tristate). Additionally, you may optionally
  * bitwise OR the type with the flag %G_MARKUP_COLLECT_OPTIONAL.
  *
  * It is likely that this enum will be extended in the future to
  * support other types.
  */
 
-/**
- * g_markup_collect_attributes:
- * @element_name: the current tag name
- * @attribute_names: the attribute names
- * @attribute_values: the attribute values
- * @error: a pointer to a #GError or %NULL
- * @first_type: the #GMarkupCollectType of the first attribute
- * @first_attr: the name of the first attribute
- * @...: a pointer to the storage location of the first attribute
- *     (or %NULL), followed by more types names and pointers, ending
- *     with %G_MARKUP_COLLECT_INVALID
- *
- * Collects the attributes of the element from the data passed to the
- * #GMarkupParser start_element function, dealing with common error
- * conditions and supporting boolean values.
- *
- * This utility function is not required to write a parser but can save
- * a lot of typing.
- *
- * The @element_name, @attribute_names, @attribute_values and @error
- * parameters passed to the start_element callback should be passed
- * unmodified to this function.
- *
- * Following these arguments is a list of "supported" attributes to collect.
- * It is an error to specify multiple attributes with the same name. If any
- * attribute not in the list appears in the @attribute_names array then an
- * unknown attribute error will result.
- *
- * The #GMarkupCollectType field allows specifying the type of collection
- * to perform and if a given attribute must appear or is optional.
- *
- * The attribute name is simply the name of the attribute to collect.
- *
- * The pointer should be of the appropriate type (see the descriptions
- * under #GMarkupCollectType) and may be %NULL in case a particular
- * attribute is to be allowed but ignored.
- *
- * This function deals with issuing errors for missing attributes
- * (of type %G_MARKUP_ERROR_MISSING_ATTRIBUTE), unknown attributes
- * (of type %G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE) and duplicate
- * attributes (of type %G_MARKUP_ERROR_INVALID_CONTENT) as well
- * as parse errors for boolean-valued attributes (again of type
- * %G_MARKUP_ERROR_INVALID_CONTENT). In all of these cases %FALSE
- * will be returned and @error will be set as appropriate.
- *
- * Return value: %TRUE if successful
- *
- * Since: 2.16
- **/
-gboolean
-g_markup_collect_attributes (const gchar         *element_name,
-                             const gchar        **attribute_names,
-                             const gchar        **attribute_values,
-                             GError             **error,
-                             GMarkupCollectType   first_type,
-                             const gchar         *first_attr,
-                             ...)
+static gboolean
+_g_markup_collect_attributesv (const gchar         *element_name,
+                               const gchar        **attribute_names,
+                               const gchar        **attribute_values,
+                               gboolean             reject_unknown_attributes,
+                               GError             **error,
+                               GMarkupCollectType   first_type,
+                               const gchar         *first_attr,
+                               va_list              ap)
 {
   GMarkupCollectType type;
   const gchar *attr;
   guint64 collected;
   int written;
-  va_list ap;
   int i;
+  va_list ap2;
 
   type = first_type;
   attr = first_attr;
   collected = 0;
   written = 0;
 
-  va_start (ap, first_attr);
+  /* Take a copy of the va_list so that we can iterate back over it in case of
+   * errors. */
+  va_copy (ap2, ap);
+
   while (type != G_MARKUP_COLLECT_INVALID)
     {
       gboolean mandatory;
@@ -2671,7 +2626,6 @@ g_markup_collect_attributes (const gchar         *element_name,
                        "element '%s' requires attribute '%s'",
                        element_name, attr);
 
-          va_end (ap);
           goto failure;
         }
 
@@ -2729,7 +2683,6 @@ g_markup_collect_attributes (const gchar         *element_name,
                                "cannot be parsed as a boolean value",
                                element_name, attr, value);
 
-                  va_end (ap);
                   goto failure;
                 }
             }
@@ -2744,7 +2697,6 @@ g_markup_collect_attributes (const gchar         *element_name,
       attr = va_arg (ap, const char *);
       written++;
     }
-  va_end (ap);
 
   /* ensure we collected all the arguments */
   for (i = 0; attribute_names[i]; i++)
@@ -2765,19 +2717,22 @@ g_markup_collect_attributes (const gchar         *element_name,
             break;
 
         /* j is now the first occurrence of attribute_names[i] */
-        if (i == j)
+        if (i == j && reject_unknown_attributes)
           g_set_error (error, G_MARKUP_ERROR,
                        G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
                        "attribute '%s' invalid for element '%s'",
                        attribute_names[i], element_name);
-        else
+        else if (i != j)
           g_set_error (error, G_MARKUP_ERROR,
                        G_MARKUP_ERROR_INVALID_CONTENT,
                        "attribute '%s' given multiple times for element '%s'",
                        attribute_names[i], element_name);
+        else
+          continue; /* accepting unknown attributes */
 
         goto failure;
       }
+  va_end (ap2);
 
   return TRUE;
 
@@ -2786,12 +2741,11 @@ failure:
   type = first_type;
   attr = first_attr;
 
-  va_start (ap, first_attr);
   while (type != G_MARKUP_COLLECT_INVALID)
     {
       gpointer ptr;
 
-      ptr = va_arg (ap, gpointer);
+      ptr = va_arg (ap2, gpointer);
 
       if (ptr != NULL)
         {
@@ -2815,10 +2769,134 @@ failure:
             }
         }
 
-      type = va_arg (ap, GMarkupCollectType);
-      attr = va_arg (ap, const char *);
+      type = va_arg (ap2, GMarkupCollectType);
+      attr = va_arg (ap2, const char *);
     }
-  va_end (ap);
+  va_end (ap2);
 
   return FALSE;
 }
+
+/**
+ * g_markup_collect_attributes:
+ * @element_name: the current tag name
+ * @attribute_names: the attribute names
+ * @attribute_values: the attribute values
+ * @error: a pointer to a #GError or %NULL
+ * @first_type: the #GMarkupCollectType of the first attribute
+ * @first_attr: the name of the first attribute
+ * @...: a pointer to the storage location of the first attribute
+ *     (or %NULL), followed by more types names and pointers, ending
+ *     with %G_MARKUP_COLLECT_INVALID
+ *
+ * Collects the attributes of the element from the data passed to the
+ * #GMarkupParser start_element function, dealing with common error
+ * conditions and supporting boolean values.
+ *
+ * This utility function is not required to write a parser but can save
+ * a lot of typing.
+ *
+ * The @element_name, @attribute_names, @attribute_values and @error
+ * parameters passed to the start_element callback should be passed
+ * unmodified to this function.
+ *
+ * Following these arguments is a list of "supported" attributes to collect.
+ * It is an error to specify multiple attributes with the same name. If any
+ * attribute not in the list appears in the @attribute_names array then an
+ * unknown attribute error will result.
+ *
+ * The #GMarkupCollectType field allows specifying the type of collection
+ * to perform and if a given attribute must appear or is optional.
+ *
+ * The attribute name is simply the name of the attribute to collect.
+ *
+ * The pointer should be of the appropriate type (see the descriptions
+ * under #GMarkupCollectType) and may be %NULL in case a particular
+ * attribute is to be allowed but ignored.
+ *
+ * This function deals with issuing errors for missing attributes
+ * (of type %G_MARKUP_ERROR_MISSING_ATTRIBUTE), unknown attributes
+ * (of type %G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE) and duplicate
+ * attributes (of type %G_MARKUP_ERROR_INVALID_CONTENT) as well
+ * as parse errors for boolean-valued attributes (again of type
+ * %G_MARKUP_ERROR_INVALID_CONTENT). In all of these cases %FALSE
+ * will be returned and @error will be set as appropriate.
+ *
+ * Return value: %TRUE if successful
+ *
+ * Since: 2.16
+ **/
+gboolean
+g_markup_collect_attributes (const gchar         *element_name,
+                             const gchar        **attribute_names,
+                             const gchar        **attribute_values,
+                             GError             **error,
+                             GMarkupCollectType   first_type,
+                             const gchar         *first_attr,
+                             ...)
+{
+  gboolean retval;
+  va_list ap;
+
+  va_start (ap, first_attr);
+  retval = _g_markup_collect_attributesv (element_name,
+                                          attribute_names, attribute_values,
+                                          TRUE, error,
+                                          first_type, first_attr,
+                                          ap);
+  va_end (ap);
+
+  return retval;
+}
+
+/**
+ * g_markup_collect_known_attributes:
+ * @element_name: the current tag name
+ * @attribute_names: (array zero-terminated=1): the attribute names
+ * @attribute_values: (array zero-terminated=1): the attribute values
+ * @error: (allow-none): a pointer to a #GError or %NULL
+ * @first_type: the #GMarkupCollectType of the first attribute
+ * @first_attr: the name of the first attribute
+ * @...: a pointer to the storage location of the first attribute
+ *     (or %NULL), followed by more types names and pointers, ending
+ *     with %G_MARKUP_COLLECT_INVALID
+ *
+ * Collects the attributes of the element from the data passed to the
+ * #GMarkupParser start_element function, dealing with common error
+ * conditions and supporting boolean values.
+ *
+ * This is a more relaxed version of g_markup_collect_attributes(), which
+ * ignores attributes found in @attribute_names but not listed in @first_attr
+ * or @...; by comparison g_markup_collect_attributes() will return
+ * %G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE instead. Otherwise, this function behaves
+ * identically.
+ *
+ * This is intended for situations where the markup being parsed may use
+ * extensions in other namespaces and thus contain extra, unknown, attributes.
+ *
+ * Return value: %TRUE if successful
+ *
+ * Since: 2.34
+ */
+gboolean
+g_markup_collect_known_attributes (const gchar         *element_name,
+                                   const gchar        **attribute_names,
+                                   const gchar        **attribute_values,
+                                   GError             **error,
+                                   GMarkupCollectType   first_type,
+                                   const gchar         *first_attr,
+                                   ...)
+{
+  gboolean retval;
+  va_list ap;
+
+  va_start (ap, first_attr);
+  retval = _g_markup_collect_attributesv (element_name,
+                                          attribute_names, attribute_values,
+                                          FALSE, error,
+                                          first_type, first_attr,
+                                          ap);
+  va_end (ap);
+
+  return retval;
+}
diff --git a/glib/gmarkup.h b/glib/gmarkup.h
index a8865da..ee4ee8c 100644
--- a/glib/gmarkup.h
+++ b/glib/gmarkup.h
@@ -233,6 +233,15 @@ gboolean   g_markup_collect_attributes (const gchar         *element_name,
                                         const gchar         *first_attr,
                                         ...);
 
+GLIB_AVAILABLE_IN_2_34
+gboolean   g_markup_collect_known_attributes (const gchar         *element_name,
+                                              const gchar        **attribute_names,
+                                              const gchar        **attribute_values,
+                                              GError             **error,
+                                              GMarkupCollectType   first_type,
+                                              const gchar         *first_attr,
+                                              ...);
+
 G_END_DECLS
 
 #endif /* __G_MARKUP_H__ */
diff --git a/glib/tests/markup-collect.c b/glib/tests/markup-collect.c
index 3b2e2bd..65cc67b 100644
--- a/glib/tests/markup-collect.c
+++ b/glib/tests/markup-collect.c
@@ -1,11 +1,11 @@
-/* 
+/*
  * Copyright  2007 Ryan Lortie
- * 
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
- * 
+ *
  * See the included COPYING file for more information.
  */
 
@@ -13,6 +13,19 @@
 #include <string.h>
 #include <glib.h>
 
+enum test_type
+{
+  COLLECT_ATTRIBUTES = 0,
+  COLLECT_KNOWN_ATTRIBUTES,
+  MAX_TEST_TYPE
+};
+
+struct test_data
+{
+  enum test_type test_type;
+  GString *string;
+};
+
 static void
 start (GMarkupParseContext  *context,
        const char           *element_name,
@@ -21,13 +34,26 @@ start (GMarkupParseContext  *context,
        gpointer              user_data,
        GError              **error)
 {
-  GString *string = user_data;
+  struct test_data *data = user_data;
   gboolean result;
 
-#define collect(...) \
-  g_markup_collect_attributes (element_name, attribute_names, \
-                               attribute_values, error, __VA_ARGS__, \
-                               G_MARKUP_COLLECT_INVALID)
+#define collect(...) G_STMT_START { \
+  if (data->test_type == COLLECT_ATTRIBUTES) \
+    { \
+      result = \
+        g_markup_collect_attributes (element_name, attribute_names, \
+                                     attribute_values, error, __VA_ARGS__, \
+                                     G_MARKUP_COLLECT_INVALID); \
+    } \
+  else \
+    { \
+      result = \
+        g_markup_collect_known_attributes (element_name, attribute_names, \
+                                           attribute_values, error, \
+                                           __VA_ARGS__, \
+                                           G_MARKUP_COLLECT_INVALID); \
+    } \
+  } G_STMT_END
 #define BOOL    G_MARKUP_COLLECT_BOOLEAN
 #define OPTBOOL G_MARKUP_COLLECT_BOOLEAN | G_MARKUP_COLLECT_OPTIONAL
 #define TRI     G_MARKUP_COLLECT_TRISTATE
@@ -41,9 +67,9 @@ start (GMarkupParseContext  *context,
     {
       gboolean mb = 2, ob = 2, tri = 2;
 
-      result = collect (BOOL,    "mb", &mb,
-                        OPTBOOL, "ob", &ob,
-                        TRI,     "tri", &tri);
+      collect (BOOL,    "mb", &mb,
+               OPTBOOL, "ob", &ob,
+               TRI,     "tri", &tri);
 
       g_assert (result ||
                 (mb == FALSE && ob == FALSE && tri != TRUE && tri != FALSE));
@@ -51,7 +77,7 @@ start (GMarkupParseContext  *context,
       if (tri != FALSE && tri != TRUE)
         tri = -1;
 
-      g_string_append_printf (string, "<bool(%d) %d %d %d>",
+      g_string_append_printf (data->string, "<bool(%d) %d %d %d>",
                               result, mb, ob, tri);
     }
 
@@ -60,15 +86,15 @@ start (GMarkupParseContext  *context,
       const char *cm, *co;
       char *am, *ao;
 
-      result = collect (STR,    "cm", &cm,
-                        STRDUP, "am", &am,
-                        OPTDUP, "ao", &ao,
-                        OPTSTR, "co", &co);
+      collect (STR,    "cm", &cm,
+               STRDUP, "am", &am,
+               OPTDUP, "ao", &ao,
+               OPTSTR, "co", &co);
 
       g_assert (result ||
                 (cm == NULL && am == NULL && ao == NULL && co == NULL));
 
-      g_string_append_printf (string, "<str(%d) %s %s %s %s>",
+      g_string_append_printf (data->string, "<str(%d) %s %s %s %s>",
                               result, n (cm), n (am), n (ao), n (co));
 
       g_free (am);
@@ -140,34 +166,49 @@ static void
 test_collect (gconstpointer d)
 {
   const struct test *test = d;
+  enum test_type t;
 
-  GMarkupParseContext *ctx;
-  GError *error = NULL;
-  GString *string;
-  gboolean result;
-
-  string = g_string_new ("");
-  ctx = g_markup_parse_context_new (&parser, 0, string, NULL);
-  result = g_markup_parse_context_parse (ctx,
-                                         test->document,
-                                         -1, &error);
-  if (result)
-    result = g_markup_parse_context_end_parse (ctx, &error);
-
-  if (result)
+  for (t = 0; t < MAX_TEST_TYPE; t++)
     {
-      g_assert_no_error (error);
-      g_assert_cmpint (test->error_code, ==, 0);
-      g_assert_cmpstr (test->result, ==, string->str);
+      GMarkupParseContext *ctx;
+      GError *error = NULL;
+      gboolean result;
+      struct test_data data;
+
+      data.test_type = t;
+      data.string = g_string_new ("");
+
+      ctx = g_markup_parse_context_new (&parser, 0, &data, NULL);
+      result = g_markup_parse_context_parse (ctx,
+                                             test->document,
+                                             -1, &error);
+      if (result)
+        result = g_markup_parse_context_end_parse (ctx, &error);
+
+      if (result &&
+          !(t == COLLECT_KNOWN_ATTRIBUTES &&
+            test->error_code == G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE))
+        {
+          /* Normal test */
+          g_assert_no_error (error);
+          g_assert_cmpint (test->error_code, ==, 0);
+          g_assert_cmpstr (test->result, ==, data.string->str);
+        }
+      else if (result)
+        {
+          /* Test expecting UNKNOWN_ATTRIBUTE, and we're parsing with
+           * collect_known_attributes(). */
+          g_assert_no_error (error);
+        }
+      else
+        {
+          g_assert_error (error, G_MARKUP_ERROR, test->error_code);
+        }
+
+      g_markup_parse_context_free (ctx);
+      g_string_free (data.string, TRUE);
+      g_clear_error (&error);
     }
-  else
-    {
-      g_assert_error (error, G_MARKUP_ERROR, test->error_code);
-    }
-
-  g_markup_parse_context_free (ctx);
-  g_string_free (string, TRUE);
-  g_clear_error (&error);
 }
 
 #define XML "<element a='1' b='2' c='3'/>"



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