Commit 8d40389d authored by Matthias Clasen's avatar Matthias Clasen

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
parent 21aff13d
......@@ -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>
......
......@@ -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
......
......@@ -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;
}
......@@ -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__ */
/*
/*
* 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'/>"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment