typedef enum {
START,
START_ELEMENT,
- TEXT
+ TEXT,
+ FLUSH_TEXT,
+ CLOSING_ELEMENT
} ParseState;
struct _GMarkupParseContext {
gpointer user_data;
GDestroyNotify user_data_dnotify;
ParseState state;
+
+ /* Stores the name of the current element, so we can issue the end_element */
+ GSList *level;
+
+ GString *text;
};
GMarkupParseContext *
}
}
+static void
+destroy_parse_state (GMarkupParseContext *context)
+{
+ GSList *p;
+
+ for (p = context->level; p != NULL; p = p->next)
+ g_free (p->data);
+
+ g_slist_free (context->level);
+ if (context->text != NULL)
+ g_string_free (context->text, TRUE);
+ context->text = NULL;
+ context->level = NULL;
+}
+
gboolean
g_markup_parse_context_parse (GMarkupParseContext *context,
const gchar *text, gssize text_len,
continue;
}
set_error ("Expected < to start the document");
-
- return FALSE;
+ goto fail;
case START_ELEMENT: {
const char *element_start = p, *element_end;
- int full_stop = 0;
+ char *ename = NULL;
+ int full_stop = 0, l;
gchar **names = NULL, **values = NULL;
- if (!(isascii (*p) && isalpha (*p)))
- set_error ("Must start with a letter");
+ for (; p < end && isspace (*p); p++)
+ ;
+ if (p == end){
+ set_error ("Unfinished element");
+ goto fail;
+ }
+ if (!(isascii (*p) && isalpha (*p))){
+ set_error ("Expected an element name");
+ goto fail;
+ }
for (++p; p < end && isalnum (*p); p++)
;
if (p == end){
set_error ("Expected an element");
- return FALSE;
+ goto fail;
}
element_end = p;
;
if (p == end){
set_error ("Unfinished element");
- return FALSE;
+ goto fail;
}
p = parse_attributes (p, end, &names, &values, error, &full_stop);
if (p == end){
- if (*error == NULL)
- set_error ("Unfinished sequence");
+ if (names != NULL) {
+ g_strfreev (names);
+ g_strfreev (values);
+ }
- return FALSE;
+ set_error ("Unfinished sequence");
+ goto fail;
}
- if (context->parser.start_element != NULL){
- int l = element_end - element_start;
- char *ename = malloc (l + 1);
-
- if (ename == NULL)
- return FALSE;
- strncpy (ename, element_start, l);
- ename [l] = 0;
-
+ l = element_end - element_start;
+ ename = malloc (l + 1);
+ if (ename == NULL)
+ goto fail;
+ strncpy (ename, element_start, l);
+ ename [l] = 0;
+
+ if (context->parser.start_element != NULL)
context->parser.start_element (context, ename,
(const gchar **) names,
(const gchar **) values,
context->user_data, error);
- free (ename);
- }
+
if (names != NULL){
g_strfreev (names);
g_strfreev (values);
}
+
if (*error != NULL)
- return FALSE;
- context->state = full_stop ? START : TEXT;
+ goto fail;
+
+ if (full_stop){
+ if (context->parser.end_element != NULL){
+ context->parser.end_element (context, ename, context->user_data, error);
+ if (*error != NULL)
+ goto fail;
+ }
+ } else
+ context->level = g_slist_prepend (context->level, ename);
+
+ context->state = TEXT;
break;
} /* case START_ELEMENT */
case TEXT: {
+ if (c == '<'){
+ context->state = FLUSH_TEXT;
+ break;
+ }
+ if (context->parser.text != NULL){
+ if (context->text == NULL)
+ context->text = g_string_new ("");
+ g_string_append_c (context->text, c);
+ }
break;
}
+
+ case FLUSH_TEXT:
+ if (context->parser.text != NULL){
+ context->parser.text (context, context->text->str, context->text->len,
+ context->user_data, error);
+ if (*error != NULL)
+ goto fail;
+ }
- }
+ if (c == '/')
+ context->state = CLOSING_ELEMENT;
+ else {
+ p--;
+ context->state = START_ELEMENT;
+ }
+ break;
+
+ case CLOSING_ELEMENT: {
+ GSList *current = context->level;
+
+ if (context->level == NULL){
+ set_error ("Too many closing tags, not enough open tags");
+ goto fail;
+ }
+
+ if (context->parser.end_element != NULL){
+ char *text = current->data;
+
+ context->parser.end_element (context, text, context->user_data, error);
+ if (*error != NULL)
+ goto fail;
+ }
+ context->level = context->level->next;
+ g_slist_free (current);
+ break;
+ } /* case CLOSING_ELEMENT */
+
+ } /* switch */
}
+
return TRUE;
+ fail:
+ if (context->parser.error)
+ context->parser.error (context, *error, context->user_data);
+
+ destroy_parse_state (context);
+ return FALSE;
}
+