From 477106292a13666e1b97921957f84ef35312c56f Mon Sep 17 00:00:00 2001 From: CEST 2001 Paolo Molaro Date: Thu, 24 May 2001 19:30:40 +0000 Subject: [PATCH] back out change by mitch@convergence.de and apply patch in bug id #52067 Thu May 24 21:24:16 CEST 2001 Paolo Molaro * gmarkup.c: back out change by mitch@convergence.de and apply patch in bug id #52067 that fixes the same problem in a more complete manner. This fixes also a segfault for a malformed XML file and adds a new test case. --- ChangeLog | 7 ++ ChangeLog.pre-2-0 | 7 ++ ChangeLog.pre-2-10 | 7 ++ ChangeLog.pre-2-12 | 7 ++ ChangeLog.pre-2-2 | 7 ++ ChangeLog.pre-2-4 | 7 ++ ChangeLog.pre-2-6 | 7 ++ ChangeLog.pre-2-8 | 7 ++ glib/gmarkup.c | 201 +++++++++++----------------------- gmarkup.c | 201 +++++++++++----------------------- tests/markups/valid-3.gmarkup | 10 ++ 11 files changed, 194 insertions(+), 274 deletions(-) create mode 100644 tests/markups/valid-3.gmarkup diff --git a/ChangeLog b/ChangeLog index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index ffde3ab0a..9656ae27e 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,10 @@ +Thu May 24 21:24:16 CEST 2001 Paolo Molaro + + * gmarkup.c: back out change by mitch@convergence.de and apply patch in + bug id #52067 that fixes the same problem in a more complete manner. + This fixes also a segfault for a malformed XML file and adds a new + test case. + 2001-05-24 Hans Breuer * grel.c : reflect renaming of g_string_sprintfa to g_string_printfa diff --git a/glib/gmarkup.c b/glib/gmarkup.c index 87d60914e..e141db6b1 100644 --- a/glib/gmarkup.c +++ b/glib/gmarkup.c @@ -38,36 +38,6 @@ g_markup_error_quark () return error_quark; } -typedef struct _GMarkupAttribute GMarkupAttribute; - -struct _GMarkupAttribute -{ - gchar *name; - gchar *value; -}; - -static GMarkupAttribute* -attribute_new (const gchar *name, const gchar *value) -{ - GMarkupAttribute *attr; - - attr = g_new (GMarkupAttribute, 1); - - /* name/value are allowed to be NULL */ - attr->name = g_strdup (name); - attr->value = g_strdup (value); - - return attr; -} - -static void -attribute_free (GMarkupAttribute *attr) -{ - g_free (attr->name); - g_free (attr->value); - g_free (attr); -} - typedef enum { STATE_START, @@ -106,7 +76,10 @@ struct _GMarkupParseContext GMarkupParseState state; GSList *tag_stack; - GSList *attributes; + gchar **attr_names; + gchar **attr_values; + gint cur_attr; + gint alloc_attrs; const gchar *current_text; gint current_text_len; @@ -162,7 +135,10 @@ g_markup_parse_context_new (const GMarkupParser *parser, context->state = STATE_START; context->tag_stack = NULL; - context->attributes = NULL; + context->attr_names = NULL; + context->attr_values = NULL; + context->cur_attr = -1; + context->alloc_attrs = 0; context->current_text = NULL; context->current_text_len = -1; @@ -195,8 +171,8 @@ g_markup_parse_context_free (GMarkupParseContext *context) if (context->dnotify) (* context->dnotify) (context->user_data); - g_slist_foreach (context->attributes, (GFunc)attribute_free, NULL); - g_slist_free (context->attributes); + g_strfreev (context->attr_names); + g_strfreev (context->attr_values); g_slist_foreach (context->tag_stack, (GFunc)g_free, NULL); g_slist_free (context->tag_stack); @@ -210,68 +186,6 @@ g_markup_parse_context_free (GMarkupParseContext *context) g_free (context); } -static void -attribute_list_to_arrays (GSList *attributes, - gchar ***namesp, - gchar ***valuesp, - gint *n_attributes) -{ - GSList *tmp_list; - gint len; - gchar **names; - gchar **values; - gint i; - - len = g_slist_length (attributes); - - if (namesp) - { - names = g_new (gchar*, len + 1); - names[len] = NULL; - } - else - names = NULL; - - if (valuesp) - { - values = g_new (gchar*, len + 1); - values[len] = NULL; - } - else - values = NULL; - - /* We want to reverse the list, since it's - * backward from the order the attributes appeared - * in the file. - */ - i = len - 1; - tmp_list = attributes; - while (tmp_list) - { - GMarkupAttribute *attr = tmp_list->data; - - g_assert (i >= 0); - - if (namesp) - names[i] = attr->name; - - if (valuesp) - values[i] = attr->value; - - tmp_list = g_slist_next (tmp_list); - --i; - } - - if (n_attributes) - *n_attributes = len; - - if (namesp) - *namesp = names; - - if (valuesp) - *valuesp = values; -} - static void mark_error (GMarkupParseContext *context, GError *error) @@ -748,12 +662,11 @@ add_to_partial (GMarkupParseContext *context, } static void -free_partial (GMarkupParseContext *context) +truncate_partial (GMarkupParseContext *context) { if (context->partial_chunk != NULL) { - g_string_free (context->partial_chunk, TRUE); - context->partial_chunk = NULL; + context->partial_chunk = g_string_truncate (context->partial_chunk, 0); } } @@ -766,7 +679,8 @@ current_element (GMarkupParseContext *context) static const gchar* current_attribute (GMarkupParseContext *context) { - return ((GMarkupAttribute*)context->attributes->data)->name; + g_assert (context->cur_attr >= 0); + return context->attr_names[context->cur_attr]; } static void @@ -806,6 +720,21 @@ find_current_text_end (GMarkupParseContext *context) } } +static void +add_attribute (GMarkupParseContext *context, char *name) +{ + if (context->cur_attr + 2 >= context->alloc_attrs) + { + context->alloc_attrs += 5; /* silly magic number */ + context->attr_names = g_realloc (context->attr_names, sizeof(char*)*context->alloc_attrs); + context->attr_values = g_realloc (context->attr_values, sizeof(char*)*context->alloc_attrs); + } + context->cur_attr++; + context->attr_names[context->cur_attr] = name; + context->attr_values[context->cur_attr] = NULL; + context->attr_names[context->cur_attr+1] = NULL; +} + /** * g_markup_parse_context_parse: * @context: a #GMarkupParseContext @@ -1073,10 +1002,6 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->user_data, &tmp_error); - g_free (context->tag_stack->data); - context->tag_stack = g_slist_delete_link (context->tag_stack, - context->tag_stack); - if (tmp_error) { mark_error (context, tmp_error); @@ -1102,6 +1027,10 @@ g_markup_parse_context_parse (GMarkupParseContext *context, current_element (context)); } } + + g_free (context->tag_stack->data); + context->tag_stack = g_slist_delete_link (context->tag_stack, + context->tag_stack); } break; @@ -1161,20 +1090,13 @@ g_markup_parse_context_parse (GMarkupParseContext *context, /* The name has ended. Combine it with the partial chunk * if any; push it on the stack; enter next state. */ - GMarkupAttribute *attr; add_to_partial (context, context->start, context->iter); - attr = attribute_new (NULL, NULL); - - attr->name = g_string_free (context->partial_chunk, - FALSE); + add_attribute (context, g_string_free (context->partial_chunk, FALSE)); context->partial_chunk = NULL; context->start = NULL; - context->attributes = - g_slist_prepend (context->attributes, attr); - if (*context->iter == '=') { advance_char (context); @@ -1189,7 +1111,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, _("Odd character '%s', expected a '=' after " "attribute name '%s' of element '%s'"), utf8_str (context->iter, buf), - attr->name, + current_attribute (context), current_element (context)); } @@ -1243,17 +1165,20 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->state == STATE_AFTER_CLOSE_ANGLE) { const gchar *start_name; - gchar **attr_names = NULL; - gchar **attr_values = NULL; + /* Ugly, but the current code expects an empty array instead of NULL */ + const gchar *empty = NULL; + const gchar **attr_names = ∅ + const gchar **attr_values = ∅ GError *tmp_error; /* Call user callback for element start */ start_name = current_element (context); - attribute_list_to_arrays (context->attributes, - &attr_names, - &attr_values, - NULL); + if (context->cur_attr >= 0) + { + attr_names = (const gchar**)context->attr_names; + attr_values = (const gchar**)context->attr_values; + } tmp_error = NULL; if (context->parser->start_element) @@ -1264,17 +1189,15 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->user_data, &tmp_error); - /* Free only the string arrays, as we didn't g_strdup() the attribute - * list's strings - */ - g_free (attr_names); - g_free (attr_values); - - /* Go ahead and free this. */ - g_slist_foreach (context->attributes, (GFunc)attribute_free, - NULL); - g_slist_free (context->attributes); - context->attributes = NULL; + /* Go ahead and free the attributes. */ + for (; context->cur_attr >= 0; context->cur_attr--) + { + int pos = context->cur_attr; + g_free (context->attr_names[pos]); + g_free (context->attr_values[pos]); + context->attr_names[pos] = context->attr_values[pos] = NULL; + } + context->cur_attr = -1; if (tmp_error != NULL) { @@ -1330,17 +1253,15 @@ g_markup_parse_context_parse (GMarkupParseContext *context, * with the partial chunk if any; set it for the current * attribute. */ - GMarkupAttribute *attr; - add_to_partial (context, context->start, context->iter); - attr = context->attributes->data; + g_assert (context->cur_attr >= 0); if (unescape_text (context, context->partial_chunk->str, context->partial_chunk->str + context->partial_chunk->len, - &attr->value, + &context->attr_values[context->cur_attr], error)) { /* success, advance past quote and set state. */ @@ -1349,7 +1270,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->start = NULL; } - free_partial (context); + truncate_partial (context); } break; @@ -1409,7 +1330,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, } } - free_partial (context); + truncate_partial (context); } break; @@ -1558,7 +1479,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->user_data, &tmp_error); - free_partial (context); + truncate_partial (context); if (tmp_error == NULL) { @@ -1609,6 +1530,12 @@ g_markup_parse_context_end_parse (GMarkupParseContext *context, g_return_val_if_fail (!context->parsing, FALSE); g_return_val_if_fail (context->state != STATE_ERROR, FALSE); + if (context->partial_chunk != NULL) + { + g_string_free (context->partial_chunk, TRUE); + context->partial_chunk = NULL; + } + if (context->document_empty) { set_error (context, error, G_MARKUP_ERROR_EMPTY, diff --git a/gmarkup.c b/gmarkup.c index 87d60914e..e141db6b1 100644 --- a/gmarkup.c +++ b/gmarkup.c @@ -38,36 +38,6 @@ g_markup_error_quark () return error_quark; } -typedef struct _GMarkupAttribute GMarkupAttribute; - -struct _GMarkupAttribute -{ - gchar *name; - gchar *value; -}; - -static GMarkupAttribute* -attribute_new (const gchar *name, const gchar *value) -{ - GMarkupAttribute *attr; - - attr = g_new (GMarkupAttribute, 1); - - /* name/value are allowed to be NULL */ - attr->name = g_strdup (name); - attr->value = g_strdup (value); - - return attr; -} - -static void -attribute_free (GMarkupAttribute *attr) -{ - g_free (attr->name); - g_free (attr->value); - g_free (attr); -} - typedef enum { STATE_START, @@ -106,7 +76,10 @@ struct _GMarkupParseContext GMarkupParseState state; GSList *tag_stack; - GSList *attributes; + gchar **attr_names; + gchar **attr_values; + gint cur_attr; + gint alloc_attrs; const gchar *current_text; gint current_text_len; @@ -162,7 +135,10 @@ g_markup_parse_context_new (const GMarkupParser *parser, context->state = STATE_START; context->tag_stack = NULL; - context->attributes = NULL; + context->attr_names = NULL; + context->attr_values = NULL; + context->cur_attr = -1; + context->alloc_attrs = 0; context->current_text = NULL; context->current_text_len = -1; @@ -195,8 +171,8 @@ g_markup_parse_context_free (GMarkupParseContext *context) if (context->dnotify) (* context->dnotify) (context->user_data); - g_slist_foreach (context->attributes, (GFunc)attribute_free, NULL); - g_slist_free (context->attributes); + g_strfreev (context->attr_names); + g_strfreev (context->attr_values); g_slist_foreach (context->tag_stack, (GFunc)g_free, NULL); g_slist_free (context->tag_stack); @@ -210,68 +186,6 @@ g_markup_parse_context_free (GMarkupParseContext *context) g_free (context); } -static void -attribute_list_to_arrays (GSList *attributes, - gchar ***namesp, - gchar ***valuesp, - gint *n_attributes) -{ - GSList *tmp_list; - gint len; - gchar **names; - gchar **values; - gint i; - - len = g_slist_length (attributes); - - if (namesp) - { - names = g_new (gchar*, len + 1); - names[len] = NULL; - } - else - names = NULL; - - if (valuesp) - { - values = g_new (gchar*, len + 1); - values[len] = NULL; - } - else - values = NULL; - - /* We want to reverse the list, since it's - * backward from the order the attributes appeared - * in the file. - */ - i = len - 1; - tmp_list = attributes; - while (tmp_list) - { - GMarkupAttribute *attr = tmp_list->data; - - g_assert (i >= 0); - - if (namesp) - names[i] = attr->name; - - if (valuesp) - values[i] = attr->value; - - tmp_list = g_slist_next (tmp_list); - --i; - } - - if (n_attributes) - *n_attributes = len; - - if (namesp) - *namesp = names; - - if (valuesp) - *valuesp = values; -} - static void mark_error (GMarkupParseContext *context, GError *error) @@ -748,12 +662,11 @@ add_to_partial (GMarkupParseContext *context, } static void -free_partial (GMarkupParseContext *context) +truncate_partial (GMarkupParseContext *context) { if (context->partial_chunk != NULL) { - g_string_free (context->partial_chunk, TRUE); - context->partial_chunk = NULL; + context->partial_chunk = g_string_truncate (context->partial_chunk, 0); } } @@ -766,7 +679,8 @@ current_element (GMarkupParseContext *context) static const gchar* current_attribute (GMarkupParseContext *context) { - return ((GMarkupAttribute*)context->attributes->data)->name; + g_assert (context->cur_attr >= 0); + return context->attr_names[context->cur_attr]; } static void @@ -806,6 +720,21 @@ find_current_text_end (GMarkupParseContext *context) } } +static void +add_attribute (GMarkupParseContext *context, char *name) +{ + if (context->cur_attr + 2 >= context->alloc_attrs) + { + context->alloc_attrs += 5; /* silly magic number */ + context->attr_names = g_realloc (context->attr_names, sizeof(char*)*context->alloc_attrs); + context->attr_values = g_realloc (context->attr_values, sizeof(char*)*context->alloc_attrs); + } + context->cur_attr++; + context->attr_names[context->cur_attr] = name; + context->attr_values[context->cur_attr] = NULL; + context->attr_names[context->cur_attr+1] = NULL; +} + /** * g_markup_parse_context_parse: * @context: a #GMarkupParseContext @@ -1073,10 +1002,6 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->user_data, &tmp_error); - g_free (context->tag_stack->data); - context->tag_stack = g_slist_delete_link (context->tag_stack, - context->tag_stack); - if (tmp_error) { mark_error (context, tmp_error); @@ -1102,6 +1027,10 @@ g_markup_parse_context_parse (GMarkupParseContext *context, current_element (context)); } } + + g_free (context->tag_stack->data); + context->tag_stack = g_slist_delete_link (context->tag_stack, + context->tag_stack); } break; @@ -1161,20 +1090,13 @@ g_markup_parse_context_parse (GMarkupParseContext *context, /* The name has ended. Combine it with the partial chunk * if any; push it on the stack; enter next state. */ - GMarkupAttribute *attr; add_to_partial (context, context->start, context->iter); - attr = attribute_new (NULL, NULL); - - attr->name = g_string_free (context->partial_chunk, - FALSE); + add_attribute (context, g_string_free (context->partial_chunk, FALSE)); context->partial_chunk = NULL; context->start = NULL; - context->attributes = - g_slist_prepend (context->attributes, attr); - if (*context->iter == '=') { advance_char (context); @@ -1189,7 +1111,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, _("Odd character '%s', expected a '=' after " "attribute name '%s' of element '%s'"), utf8_str (context->iter, buf), - attr->name, + current_attribute (context), current_element (context)); } @@ -1243,17 +1165,20 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->state == STATE_AFTER_CLOSE_ANGLE) { const gchar *start_name; - gchar **attr_names = NULL; - gchar **attr_values = NULL; + /* Ugly, but the current code expects an empty array instead of NULL */ + const gchar *empty = NULL; + const gchar **attr_names = ∅ + const gchar **attr_values = ∅ GError *tmp_error; /* Call user callback for element start */ start_name = current_element (context); - attribute_list_to_arrays (context->attributes, - &attr_names, - &attr_values, - NULL); + if (context->cur_attr >= 0) + { + attr_names = (const gchar**)context->attr_names; + attr_values = (const gchar**)context->attr_values; + } tmp_error = NULL; if (context->parser->start_element) @@ -1264,17 +1189,15 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->user_data, &tmp_error); - /* Free only the string arrays, as we didn't g_strdup() the attribute - * list's strings - */ - g_free (attr_names); - g_free (attr_values); - - /* Go ahead and free this. */ - g_slist_foreach (context->attributes, (GFunc)attribute_free, - NULL); - g_slist_free (context->attributes); - context->attributes = NULL; + /* Go ahead and free the attributes. */ + for (; context->cur_attr >= 0; context->cur_attr--) + { + int pos = context->cur_attr; + g_free (context->attr_names[pos]); + g_free (context->attr_values[pos]); + context->attr_names[pos] = context->attr_values[pos] = NULL; + } + context->cur_attr = -1; if (tmp_error != NULL) { @@ -1330,17 +1253,15 @@ g_markup_parse_context_parse (GMarkupParseContext *context, * with the partial chunk if any; set it for the current * attribute. */ - GMarkupAttribute *attr; - add_to_partial (context, context->start, context->iter); - attr = context->attributes->data; + g_assert (context->cur_attr >= 0); if (unescape_text (context, context->partial_chunk->str, context->partial_chunk->str + context->partial_chunk->len, - &attr->value, + &context->attr_values[context->cur_attr], error)) { /* success, advance past quote and set state. */ @@ -1349,7 +1270,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->start = NULL; } - free_partial (context); + truncate_partial (context); } break; @@ -1409,7 +1330,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, } } - free_partial (context); + truncate_partial (context); } break; @@ -1558,7 +1479,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context, context->user_data, &tmp_error); - free_partial (context); + truncate_partial (context); if (tmp_error == NULL) { @@ -1609,6 +1530,12 @@ g_markup_parse_context_end_parse (GMarkupParseContext *context, g_return_val_if_fail (!context->parsing, FALSE); g_return_val_if_fail (context->state != STATE_ERROR, FALSE); + if (context->partial_chunk != NULL) + { + g_string_free (context->partial_chunk, TRUE); + context->partial_chunk = NULL; + } + if (context->document_empty) { set_error (context, error, G_MARKUP_ERROR_EMPTY, diff --git a/tests/markups/valid-3.gmarkup b/tests/markups/valid-3.gmarkup new file mode 100644 index 000000000..aed984e2f --- /dev/null +++ b/tests/markups/valid-3.gmarkup @@ -0,0 +1,10 @@ + + + + + + + + + +