2 * gmakrup.c: Minimal XML markup reader.
4 * Unlike the GLib one, this can not be restarted with more text
5 * as the Mono use does not require it
8 * Miguel de Icaza (miguel@novell.com)
10 * (C) 2006 Novell, Inc.
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files (the
14 * "Software"), to deal in the Software without restriction, including
15 * without limitation the rights to use, copy, modify, merge, publish,
16 * distribute, sublicense, and/or sell copies of the Software, and to
17 * permit persons to whom the Software is furnished to do so, subject to
18 * the following conditions:
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 #define set_error(msg...) do { if (error != NULL) *error = g_error_new (GINT_TO_POINTER (1), 1, msg); } while (0);
44 struct _GMarkupParseContext {
47 GDestroyNotify user_data_dnotify;
50 /* Stores the name of the current element, so we can issue the end_element */
57 g_markup_parse_context_new (const GMarkupParser *parser,
58 GMarkupParseFlags flags,
60 GDestroyNotify user_data_dnotify)
62 GMarkupParseContext *context = g_new0 (GMarkupParseContext, 1);
64 context->parser = *parser;
65 context->user_data = user_data;
66 context->user_data_dnotify = user_data_dnotify;
72 g_markup_parse_context_free (GMarkupParseContext *context)
78 skip_space (const char *p, const char *end)
80 for (; p < end && isspace (*p); p++)
86 parse_value (const char *p, const char *end, char **value, GError **error)
92 set_error ("Expected the attribute value to start with a quote");
96 for (++p; p < end && *p != '"'; p++)
101 *value = malloc (l + 1);
104 strncpy (*value, start, l);
110 parse_name (const char *p, const char *end, char **value)
112 const char *start = p;
115 for (; p < end && isalnum (*p); p++)
121 *value = malloc (l + 1);
124 strncpy (*value, start, l);
130 parse_attributes (const char *p, const char *end, char ***names, char ***values, GError **error, int *full_stop)
135 p = skip_space (p, end);
143 if (*p == '/' && ((p+1) < end && *p == '>')){
149 p = parse_name (p, end, &name);
152 p = skip_space (p, end);
156 set_error ("Expected an = after the attribute name `%s'", name);
160 p = skip_space (p, end);
164 p = parse_value (p, end, &value, error);
169 *names = g_realloc (*names, sizeof (char **) * (nnames+1));
170 *values = g_realloc (*values, sizeof (char **) * (nnames+1));
171 (*names) [nnames-1] = name;
172 (*values) [nnames-1] = name;
173 (*names) [nnames] = NULL;
174 (*values) [nnames] = NULL;
180 destroy_parse_state (GMarkupParseContext *context)
184 for (p = context->level; p != NULL; p = p->next)
187 g_slist_free (context->level);
188 if (context->text != NULL)
189 g_string_free (context->text, TRUE);
190 context->text = NULL;
191 context->level = NULL;
195 g_markup_parse_context_parse (GMarkupParseContext *context,
196 const gchar *text, gssize text_len,
201 g_return_val_if_fail (context != NULL, FALSE);
202 g_return_val_if_fail (text != NULL, FALSE);
203 g_return_val_if_fail (text_len >= 0, FALSE);
205 end = text + text_len;
207 for (p = text; p < end; p++){
210 switch (context->state){
212 if (c == ' ' || c == '\t' || c == '\f' || c == '\n')
215 context->state = START_ELEMENT;
218 set_error ("Expected < to start the document");
222 case START_ELEMENT: {
223 const char *element_start = p, *element_end;
225 int full_stop = 0, l;
226 gchar **names = NULL, **values = NULL;
228 for (; p < end && isspace (*p); p++)
231 set_error ("Unfinished element");
234 if (!(isascii (*p) && isalpha (*p))){
235 set_error ("Expected an element name");
239 for (++p; p < end && isalnum (*p); p++)
242 set_error ("Expected an element");
247 for (; p < end && isspace (*p); p++)
250 set_error ("Unfinished element");
253 p = parse_attributes (p, end, &names, &values, error, &full_stop);
260 set_error ("Unfinished sequence");
263 l = element_end - element_start;
264 ename = malloc (l + 1);
267 strncpy (ename, element_start, l);
270 if (context->parser.start_element != NULL)
271 context->parser.start_element (context, ename,
272 (const gchar **) names,
273 (const gchar **) values,
274 context->user_data, error);
285 if (context->parser.end_element != NULL){
286 context->parser.end_element (context, ename, context->user_data, error);
291 context->level = g_slist_prepend (context->level, ename);
293 context->state = TEXT;
295 } /* case START_ELEMENT */
299 context->state = FLUSH_TEXT;
302 if (context->parser.text != NULL){
303 if (context->text == NULL)
304 context->text = g_string_new ("");
305 g_string_append_c (context->text, c);
311 if (context->parser.text != NULL){
312 context->parser.text (context, context->text->str, context->text->len,
313 context->user_data, error);
319 context->state = CLOSING_ELEMENT;
322 context->state = START_ELEMENT;
326 case CLOSING_ELEMENT: {
327 GSList *current = context->level;
329 if (context->level == NULL){
330 set_error ("Too many closing tags, not enough open tags");
334 if (context->parser.end_element != NULL){
335 char *text = current->data;
337 context->parser.end_element (context, text, context->user_data, error);
341 context->level = context->level->next;
342 g_slist_free (current);
344 } /* case CLOSING_ELEMENT */
352 if (context->parser.error)
353 context->parser.error (context, *error, context->user_data);
355 destroy_parse_state (context);