Fix a typo in the docs
[platform/upstream/glib.git] / glib / gmarkup.c
1 /* gmarkup.c - Simple XML-like parser
2  *
3  *  Copyright 2000, 2003 Red Hat, Inc.
4  *  Copyright 2007, 2008 Ryan Lortie <desrt@desrt.ca>
5  *
6  * GLib is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * GLib is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GLib; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  *   Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29
30 #include "glib.h"
31 #include "glibintl.h"
32 #include "galias.h"
33
34 GQuark
35 g_markup_error_quark (void)
36 {
37   return g_quark_from_static_string ("g-markup-error-quark");
38 }
39
40 typedef enum
41 {
42   STATE_START,
43   STATE_AFTER_OPEN_ANGLE,
44   STATE_AFTER_CLOSE_ANGLE,
45   STATE_AFTER_ELISION_SLASH, /* the slash that obviates need for end element */
46   STATE_INSIDE_OPEN_TAG_NAME,
47   STATE_INSIDE_ATTRIBUTE_NAME,
48   STATE_AFTER_ATTRIBUTE_NAME,
49   STATE_BETWEEN_ATTRIBUTES,
50   STATE_AFTER_ATTRIBUTE_EQUALS_SIGN,
51   STATE_INSIDE_ATTRIBUTE_VALUE_SQ,
52   STATE_INSIDE_ATTRIBUTE_VALUE_DQ,
53   STATE_INSIDE_TEXT,
54   STATE_AFTER_CLOSE_TAG_SLASH,
55   STATE_INSIDE_CLOSE_TAG_NAME,
56   STATE_AFTER_CLOSE_TAG_NAME,
57   STATE_INSIDE_PASSTHROUGH,
58   STATE_ERROR
59 } GMarkupParseState;
60
61 typedef struct
62 {
63   const char *prev_element;
64   const GMarkupParser *prev_parser;
65   gpointer prev_user_data;
66 } GMarkupRecursionTracker;
67
68 struct _GMarkupParseContext
69 {
70   const GMarkupParser *parser;
71
72   GMarkupParseFlags flags;
73
74   gint line_number;
75   gint char_number;
76
77   gpointer user_data;
78   GDestroyNotify dnotify;
79
80   /* A piece of character data or an element that
81    * hasn't "ended" yet so we haven't yet called
82    * the callback for it.
83    */
84   GString *partial_chunk;
85   GSList *spare_chunks;
86
87   GMarkupParseState state;
88   GSList *tag_stack;
89   GSList *tag_stack_gstr;
90   GSList *spare_list_nodes;
91
92   GString **attr_names;
93   GString **attr_values;
94   gint cur_attr;
95   gint alloc_attrs;
96
97   const gchar *current_text;
98   gssize       current_text_len;      
99   const gchar *current_text_end;
100
101   /* used to save the start of the last interesting thingy */
102   const gchar *start;
103
104   const gchar *iter;
105
106   guint document_empty : 1;
107   guint parsing : 1;
108   guint awaiting_pop : 1;
109   gint balance;
110
111   /* subparser support */
112   GSList *subparser_stack; /* (GMarkupRecursionTracker *) */
113   const char *subparser_element;
114   gpointer held_user_data;
115 };
116
117 /*
118  * Helpers to reduce our allocation overhead, we have
119  * a well defined allocation lifecycle.
120  */
121 static GSList *
122 get_list_node (GMarkupParseContext *context, gpointer data)
123 {
124   GSList *node;
125   if (context->spare_list_nodes != NULL)
126     {
127       node = context->spare_list_nodes;
128       context->spare_list_nodes = g_slist_remove_link (context->spare_list_nodes, node);
129     }
130   else
131     node = g_slist_alloc();
132   node->data = data;
133   return node;
134 }
135
136 static void
137 free_list_node (GMarkupParseContext *context, GSList *node)
138 {
139   node->data = NULL;
140   context->spare_list_nodes = g_slist_concat (node, context->spare_list_nodes);
141 }
142
143 static inline void
144 string_blank (GString *string)
145 {
146   string->str[0] = '\0';
147   string->len = 0;
148 }
149
150 /**
151  * g_markup_parse_context_new:
152  * @parser: a #GMarkupParser
153  * @flags: one or more #GMarkupParseFlags
154  * @user_data: user data to pass to #GMarkupParser functions
155  * @user_data_dnotify: user data destroy notifier called when the parse context is freed
156  * 
157  * Creates a new parse context. A parse context is used to parse
158  * marked-up documents. You can feed any number of documents into
159  * a context, as long as no errors occur; once an error occurs,
160  * the parse context can't continue to parse text (you have to free it
161  * and create a new parse context).
162  * 
163  * Return value: a new #GMarkupParseContext
164  **/
165 GMarkupParseContext *
166 g_markup_parse_context_new (const GMarkupParser *parser,
167                             GMarkupParseFlags    flags,
168                             gpointer             user_data,
169                             GDestroyNotify       user_data_dnotify)
170 {
171   GMarkupParseContext *context;
172
173   g_return_val_if_fail (parser != NULL, NULL);
174
175   context = g_new (GMarkupParseContext, 1);
176
177   context->parser = parser;
178   context->flags = flags;
179   context->user_data = user_data;
180   context->dnotify = user_data_dnotify;
181
182   context->line_number = 1;
183   context->char_number = 1;
184
185   context->partial_chunk = NULL;
186   context->spare_chunks = NULL;
187   context->spare_list_nodes = NULL;
188
189   context->state = STATE_START;
190   context->tag_stack = NULL;
191   context->tag_stack_gstr = NULL;
192   context->attr_names = NULL;
193   context->attr_values = NULL;
194   context->cur_attr = -1;
195   context->alloc_attrs = 0;
196
197   context->current_text = NULL;
198   context->current_text_len = -1;
199   context->current_text_end = NULL;
200
201   context->start = NULL;
202   context->iter = NULL;
203
204   context->document_empty = TRUE;
205   context->parsing = FALSE;
206
207   context->awaiting_pop = FALSE;
208   context->subparser_stack = NULL;
209   context->subparser_element = NULL;
210
211   /* this is only looked at if awaiting_pop = TRUE.  initialise anyway. */
212   context->held_user_data = NULL;
213
214   context->balance = 0;
215
216   return context;
217 }
218
219 static void
220 string_full_free (gpointer ptr, gpointer user_data)
221 {
222   g_string_free (ptr, TRUE);
223 }
224
225 static void clear_attributes (GMarkupParseContext *context);
226
227 /**
228  * g_markup_parse_context_free:
229  * @context: a #GMarkupParseContext
230  * 
231  * Frees a #GMarkupParseContext. Can't be called from inside
232  * one of the #GMarkupParser functions. Can't be called while
233  * a subparser is pushed.
234  **/
235 void
236 g_markup_parse_context_free (GMarkupParseContext *context)
237 {
238   g_return_if_fail (context != NULL);
239   g_return_if_fail (!context->parsing);
240   g_return_if_fail (!context->subparser_stack);
241   g_return_if_fail (!context->awaiting_pop);
242
243   if (context->dnotify)
244     (* context->dnotify) (context->user_data);
245
246   clear_attributes (context);
247   g_free (context->attr_names);
248   g_free (context->attr_values);
249
250   g_slist_foreach (context->tag_stack_gstr, string_full_free, NULL);
251   g_slist_free (context->tag_stack_gstr);
252   g_slist_free (context->tag_stack);
253
254   g_slist_foreach (context->spare_chunks, string_full_free, NULL);
255   g_slist_free (context->spare_chunks);
256   g_slist_free (context->spare_list_nodes);
257
258   if (context->partial_chunk)
259     g_string_free (context->partial_chunk, TRUE);
260
261   g_free (context);
262 }
263
264 static void pop_subparser_stack (GMarkupParseContext *context);
265
266 static void
267 mark_error (GMarkupParseContext *context,
268             GError              *error)
269 {
270   context->state = STATE_ERROR;
271
272   if (context->parser->error)
273     (*context->parser->error) (context, error, context->user_data);
274
275   /* report the error all the way up to free all the user-data */
276   while (context->subparser_stack)
277     {
278       pop_subparser_stack (context);
279       context->awaiting_pop = FALSE; /* already been freed */
280
281       if (context->parser->error)
282         (*context->parser->error) (context, error, context->user_data);
283     }
284 }
285
286 static void set_error (GMarkupParseContext *context,
287                        GError             **error,
288                        GMarkupError         code,
289                        const gchar         *format,
290                        ...) G_GNUC_PRINTF (4, 5);
291
292 static void
293 set_error_literal (GMarkupParseContext *context,
294                    GError             **error,
295                    GMarkupError         code,
296                    const gchar         *message)
297 {
298   GError *tmp_error;
299
300   tmp_error = g_error_new_literal (G_MARKUP_ERROR, code, message);
301
302   g_prefix_error (&tmp_error,
303                   _("Error on line %d char %d: "),
304                   context->line_number,
305                   context->char_number);
306
307   mark_error (context, tmp_error);
308
309   g_propagate_error (error, tmp_error);
310 }
311
312 static void
313 set_error (GMarkupParseContext *context,
314            GError             **error,
315            GMarkupError         code,
316            const gchar         *format,
317            ...)
318 {
319   gchar *s;
320   gchar *s_valid;
321   va_list args;
322
323   va_start (args, format);
324   s = g_strdup_vprintf (format, args);
325   va_end (args);
326
327   /* Make sure that the GError message is valid UTF-8 even if it is
328    * complaining about invalid UTF-8 in the markup: */
329   s_valid = _g_utf8_make_valid (s);
330   set_error_literal (context, error, code, s);
331
332   g_free (s);
333   g_free (s_valid);
334 }
335
336 static void
337 propagate_error (GMarkupParseContext  *context,
338                  GError              **dest,
339                  GError               *src)
340 {
341   if (context->flags & G_MARKUP_PREFIX_ERROR_POSITION)
342     g_prefix_error (&src,
343                     _("Error on line %d char %d: "),
344                     context->line_number,
345                     context->char_number);
346
347   mark_error (context, src);
348
349   g_propagate_error (dest, src);
350 }
351
352 #define IS_COMMON_NAME_END_CHAR(c) \
353   ((c) == '=' || (c) == '/' || (c) == '>' || (c) == ' ')
354
355 static gboolean
356 slow_name_validate (GMarkupParseContext *context, const char *name, GError **error)
357 {
358   const char *p = name;
359
360   if (!g_utf8_validate (name, strlen (name), NULL))
361     {
362       set_error (context, error, G_MARKUP_ERROR_BAD_UTF8,
363                  _("Invalid UTF-8 encoded text in name - not valid '%s'"), name);
364       return FALSE;
365     }
366
367   if (!(g_ascii_isalpha (*p) ||
368         (!IS_COMMON_NAME_END_CHAR (*p) &&
369          (*p == '_' || 
370           *p == ':' ||
371           g_unichar_isalpha (g_utf8_get_char (p))))))
372     {
373       set_error (context, error, G_MARKUP_ERROR_PARSE,
374                  _("'%s' is not a valid name "), name);
375       return FALSE;
376     }
377
378   for (p = g_utf8_next_char (name); *p != '\0'; p = g_utf8_next_char (p))
379     {
380       /* is_name_char */
381       if (!(g_ascii_isalnum (*p) ||
382             (!IS_COMMON_NAME_END_CHAR (*p) &&
383              (*p == '.' || 
384               *p == '-' ||
385               *p == '_' ||
386               *p == ':' ||
387               g_unichar_isalpha (g_utf8_get_char (p))))))
388         {
389           set_error (context, error, G_MARKUP_ERROR_PARSE,
390                      _("'%s' is not a valid name: '%c' "), name, *p);
391           return FALSE;
392         }
393     }
394   return TRUE;
395 }
396
397 /* 
398  * Use me for elements, attributes etc.
399  */
400 static gboolean
401 name_validate (GMarkupParseContext *context, const char *name, GError **error)
402 {
403   char mask;
404   const char *p;
405
406   /* name start char */
407   p = name;
408   if (G_UNLIKELY (IS_COMMON_NAME_END_CHAR (*p) ||
409                   !(g_ascii_isalpha (*p) || *p == '_' || *p == ':')))
410     goto slow_validate;
411   
412   for (mask = *p++; *p != '\0'; p++)
413     {
414       mask |= *p;
415
416       /* is_name_char */
417       if (G_UNLIKELY (!(g_ascii_isalnum (*p) ||
418                         (!IS_COMMON_NAME_END_CHAR (*p) &&
419                          (*p == '.' || 
420                           *p == '-' ||
421                           *p == '_' ||
422                           *p == ':')))))
423         goto slow_validate;
424     }
425
426   if (mask & 0x80) /* un-common / non-ascii */
427     goto slow_validate;
428
429   return TRUE;
430
431  slow_validate:
432   return slow_name_validate (context, name, error);
433 }
434
435 static gboolean
436 text_validate (GMarkupParseContext *context, const char *p, int len, GError **error)
437 {
438   if (!g_utf8_validate (p, len, NULL))
439     {
440       set_error (context, error, G_MARKUP_ERROR_BAD_UTF8,
441                  _("Invalid UTF-8 encoded text in name - not valid '%s'"), p);
442       return FALSE;
443     }
444   else
445     return TRUE;
446 }
447
448 static gchar*
449 char_str (gunichar c,
450           gchar   *buf)
451 {
452   memset (buf, 0, 8);
453   g_unichar_to_utf8 (c, buf);
454   return buf;
455 }
456
457 static gchar*
458 utf8_str (const gchar *utf8,
459           gchar       *buf)
460 {
461   char_str (g_utf8_get_char (utf8), buf);
462   return buf;
463 }
464
465 static void
466 set_unescape_error (GMarkupParseContext *context,
467                     GError             **error,
468                     const gchar         *remaining_text,
469                     GMarkupError         code,
470                     const gchar         *format,
471                     ...)
472 {
473   GError *tmp_error;
474   gchar *s;
475   va_list args;
476   gint remaining_newlines;
477   const gchar *p;
478
479   remaining_newlines = 0;
480   p = remaining_text;
481   while (*p != '\0')
482     {
483       if (*p == '\n')
484         ++remaining_newlines;
485       ++p;
486     }
487
488   va_start (args, format);
489   s = g_strdup_vprintf (format, args);
490   va_end (args);
491
492   tmp_error = g_error_new (G_MARKUP_ERROR,
493                            code,
494                            _("Error on line %d: %s"),
495                            context->line_number - remaining_newlines,
496                            s);
497
498   g_free (s);
499
500   mark_error (context, tmp_error);
501
502   g_propagate_error (error, tmp_error);
503 }
504
505 /*
506  * re-write the GString in-place, unescaping anything that escaped.
507  * most XML does not contain entities, or escaping.
508  */
509 static gboolean
510 unescape_gstring_inplace (GMarkupParseContext *context,
511                           GString             *string,
512                           gboolean            *is_ascii,
513                           GError             **error)
514 {
515   char mask, *to;
516   int line_num = 1;
517   const char *from;
518   gboolean normalize_attribute;
519
520   *is_ascii = FALSE;
521
522   /* are we unescaping an attribute or not ? */
523   if (context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ ||
524       context->state == STATE_INSIDE_ATTRIBUTE_VALUE_DQ)
525     normalize_attribute = TRUE;
526   else
527     normalize_attribute = FALSE;
528
529   /*
530    * Meeks' theorum: unescaping can only shrink text.
531    * for &lt; etc. this is obvious, for &#xffff; more
532    * thought is required, but this is patently so.
533    */
534   mask = 0;
535   for (from = to = string->str; *from != '\0'; from++, to++) 
536     {
537       *to = *from;
538
539       mask |= *to;
540       if (*to == '\n')
541         line_num++;
542       if (normalize_attribute && (*to == '\t' || *to == '\n'))
543         *to = ' ';
544       if (*to == '\r')
545         {
546           *to = normalize_attribute ? ' ' : '\n';
547           if (from[1] == '\n')
548             from++;
549         }
550       if (*from == '&')
551         {
552           from++;
553           if (*from == '#')
554             {
555               gboolean is_hex = FALSE;
556               gulong l;
557               gchar *end = NULL;
558
559               from++;
560
561               if (*from == 'x')
562                 {
563                   is_hex = TRUE;
564                   from++;
565                 }
566
567               /* digit is between start and p */
568               errno = 0;
569               if (is_hex)
570                 l = strtoul (from, &end, 16);
571               else
572                 l = strtoul (from, &end, 10);
573               
574               if (end == from || errno != 0)
575                 {
576                   set_unescape_error (context, error,
577                                       from, G_MARKUP_ERROR_PARSE,
578                                       _("Failed to parse '%-.*s', which "
579                                         "should have been a digit "
580                                         "inside a character reference "
581                                         "(&#234; for example) - perhaps "
582                                         "the digit is too large"),
583                                       end - from, from);
584                   return FALSE;
585                 }
586               else if (*end != ';')
587                 {
588                   set_unescape_error (context, error,
589                                       from, G_MARKUP_ERROR_PARSE,
590                                       _("Character reference did not end with a "
591                                         "semicolon; "
592                                         "most likely you used an ampersand "
593                                         "character without intending to start "
594                                         "an entity - escape ampersand as &amp;"));
595                   return FALSE;
596                 }
597               else
598                 {
599                   /* characters XML 1.1 permits */
600                   if ((0 < l && l <= 0xD7FF) ||
601                       (0xE000 <= l && l <= 0xFFFD) ||
602                       (0x10000 <= l && l <= 0x10FFFF))
603                     {
604                       gchar buf[8];
605                       char_str (l, buf);
606                       strcpy (to, buf);
607                       to += strlen (buf) - 1;
608                       from = end;
609                       if (l >= 0x80) /* not ascii */
610                         mask |= 0x80;
611                     }
612                   else
613                     {
614                       set_unescape_error (context, error,
615                                           from, G_MARKUP_ERROR_PARSE,
616                                           _("Character reference '%-.*s' does not "
617                                             "encode a permitted character"),
618                                           end - from, from);
619                       return FALSE;
620                     }
621                 }
622             }
623
624           else if (strncmp (from, "lt;", 3) == 0)
625             {
626               *to = '<';
627               from += 2;
628             }
629           else if (strncmp (from, "gt;", 3) == 0)
630             {
631               *to = '>';
632               from += 2;
633             }
634           else if (strncmp (from, "amp;", 4) == 0)
635             {
636               *to = '&';
637               from += 3;
638             }
639           else if (strncmp (from, "quot;", 5) == 0)
640             {
641               *to = '"';
642               from += 4;
643             }
644           else if (strncmp (from, "apos;", 5) == 0)
645             {
646               *to = '\'';
647               from += 4;
648             }
649           else
650             {
651               if (*from == ';')
652                 set_unescape_error (context, error,
653                                     from, G_MARKUP_ERROR_PARSE,
654                                     _("Empty entity '&;' seen; valid "
655                                       "entities are: &amp; &quot; &lt; &gt; &apos;"));
656               else
657                 {
658                   const char *end = strchr (from, ';');
659                   if (end)
660                     set_unescape_error (context, error,
661                                         from, G_MARKUP_ERROR_PARSE,
662                                         _("Entity name '%-.*s' is not known"),
663                                         end-from, from);
664                   else
665                     set_unescape_error (context, error,
666                                         from, G_MARKUP_ERROR_PARSE,
667                                         _("Entity did not end with a semicolon; "
668                                           "most likely you used an ampersand "
669                                           "character without intending to start "
670                                           "an entity - escape ampersand as &amp;"));
671                 }
672               return FALSE;
673             }
674         }
675     }
676
677   g_assert (to - string->str <= string->len);
678   if (to - string->str != string->len)
679     g_string_truncate (string, to - string->str);
680
681   *is_ascii = !(mask & 0x80);
682
683   return TRUE;
684 }
685
686 static inline gboolean
687 advance_char (GMarkupParseContext *context)
688 {  
689   context->iter++;
690   context->char_number++;
691
692   if (G_UNLIKELY (context->iter == context->current_text_end))
693       return FALSE;
694
695   else if (G_UNLIKELY (*context->iter == '\n'))
696     {
697       context->line_number++;
698       context->char_number = 1;
699     }
700   
701   return TRUE;
702 }
703
704 static inline gboolean
705 xml_isspace (char c)
706 {
707   return c == ' ' || c == '\t' || c == '\n' || c == '\r';
708 }
709
710 static void
711 skip_spaces (GMarkupParseContext *context)
712 {
713   do
714     {
715       if (!xml_isspace (*context->iter))
716         return;
717     }
718   while (advance_char (context));
719 }
720
721 static void
722 advance_to_name_end (GMarkupParseContext *context)
723 {
724   do
725     {
726       if (IS_COMMON_NAME_END_CHAR (*(context->iter)))
727         return;
728       if (xml_isspace (*(context->iter)))
729         return;
730     }
731   while (advance_char (context));
732 }
733
734 static void
735 release_chunk (GMarkupParseContext *context, GString *str)
736 {
737   GSList *node;
738   if (!str)
739     return;
740   if (str->allocated_len > 256)
741     { /* large strings are unusual and worth freeing */
742       g_string_free (str, TRUE);
743       return;
744     }
745   string_blank (str);
746   node = get_list_node (context, str);
747   context->spare_chunks = g_slist_concat (node, context->spare_chunks);
748 }
749
750 static void
751 add_to_partial (GMarkupParseContext *context,
752                 const gchar         *text_start,
753                 const gchar         *text_end)
754 {
755   if (context->partial_chunk == NULL)
756     { /* allocate a new chunk to parse into */
757
758       if (context->spare_chunks != NULL)
759         {
760           GSList *node = context->spare_chunks;
761           context->spare_chunks = g_slist_remove_link (context->spare_chunks, node);
762           context->partial_chunk = node->data;
763           free_list_node (context, node);
764         }
765       else
766         context->partial_chunk = g_string_sized_new (MAX (28, text_end - text_start));
767     }
768
769   if (text_start != text_end)
770     g_string_insert_len (context->partial_chunk, -1,
771                          text_start, text_end - text_start);
772 }
773
774 static inline void
775 truncate_partial (GMarkupParseContext *context)
776 {
777   if (context->partial_chunk != NULL)
778     string_blank (context->partial_chunk);
779 }
780
781 static inline const gchar*
782 current_element (GMarkupParseContext *context)
783 {
784   return context->tag_stack->data;
785 }
786
787 static void
788 pop_subparser_stack (GMarkupParseContext *context)
789 {
790   GMarkupRecursionTracker *tracker;
791
792   g_assert (context->subparser_stack);
793
794   tracker = context->subparser_stack->data;
795
796   context->awaiting_pop = TRUE;
797   context->held_user_data = context->user_data;
798
799   context->user_data = tracker->prev_user_data;
800   context->parser = tracker->prev_parser;
801   context->subparser_element = tracker->prev_element;
802   g_slice_free (GMarkupRecursionTracker, tracker);
803
804   context->subparser_stack = g_slist_delete_link (context->subparser_stack,
805                                                   context->subparser_stack);
806 }
807
808 static void
809 push_partial_as_tag (GMarkupParseContext *context)
810 {
811   GString *str = context->partial_chunk;
812   /* sadly, this is exported by gmarkup_get_element_stack as-is */
813   context->tag_stack = g_slist_concat (get_list_node (context, str->str), context->tag_stack);
814   context->tag_stack_gstr = g_slist_concat (get_list_node (context, str), context->tag_stack_gstr);
815   context->partial_chunk = NULL;
816 }
817
818 static void
819 pop_tag (GMarkupParseContext *context)
820 {
821   GSList *nodea, *nodeb;
822
823   nodea = context->tag_stack;
824   nodeb = context->tag_stack_gstr;
825   release_chunk (context, nodeb->data);
826   context->tag_stack = g_slist_remove_link (context->tag_stack, nodea);
827   context->tag_stack_gstr = g_slist_remove_link (context->tag_stack_gstr, nodeb);
828   free_list_node (context, nodea);
829   free_list_node (context, nodeb);
830 }
831
832 static void
833 possibly_finish_subparser (GMarkupParseContext *context)
834 {
835   if (current_element (context) == context->subparser_element)
836     pop_subparser_stack (context);
837 }
838
839 static void
840 ensure_no_outstanding_subparser (GMarkupParseContext *context)
841 {
842   if (context->awaiting_pop)
843     g_critical ("During the first end_element call after invoking a "
844                 "subparser you must pop the subparser stack and handle "
845                 "the freeing of the subparser user_data.  This can be "
846                 "done by calling the end function of the subparser.  "
847                 "Very probably, your program just leaked memory.");
848
849   /* let valgrind watch the pointer disappear... */
850   context->held_user_data = NULL;
851   context->awaiting_pop = FALSE;
852 }
853
854 static const gchar*
855 current_attribute (GMarkupParseContext *context)
856 {
857   g_assert (context->cur_attr >= 0);
858   return context->attr_names[context->cur_attr]->str;
859 }
860
861 static void
862 add_attribute (GMarkupParseContext *context, GString *str)
863 {
864   if (context->cur_attr + 2 >= context->alloc_attrs)
865     {
866       context->alloc_attrs += 5; /* silly magic number */
867       context->attr_names = g_realloc (context->attr_names, sizeof(GString*)*context->alloc_attrs);
868       context->attr_values = g_realloc (context->attr_values, sizeof(GString*)*context->alloc_attrs);
869     }
870   context->cur_attr++;
871   context->attr_names[context->cur_attr] = str;
872   context->attr_values[context->cur_attr] = NULL;
873   context->attr_names[context->cur_attr+1] = NULL;
874   context->attr_values[context->cur_attr+1] = NULL;
875 }
876
877 static void
878 clear_attributes (GMarkupParseContext *context)
879 {
880   /* Go ahead and free the attributes. */
881   for (; context->cur_attr >= 0; context->cur_attr--)
882     {
883       int pos = context->cur_attr;
884       release_chunk (context, context->attr_names[pos]);
885       release_chunk (context, context->attr_values[pos]);
886       context->attr_names[pos] = context->attr_values[pos] = NULL;
887     }
888   g_assert (context->cur_attr == -1);
889   g_assert (context->attr_names == NULL ||
890             context->attr_names[0] == NULL);
891   g_assert (context->attr_values == NULL ||
892             context->attr_values[0] == NULL);
893 }
894
895 /* This has to be a separate function to ensure the alloca's
896    are unwound on exit - otherwise we grow & blow the stack
897    with large documents */
898 static inline void
899 emit_start_element (GMarkupParseContext *context, GError **error)
900 {
901   int i;
902   const gchar *start_name;
903   const gchar **attr_names;
904   const gchar **attr_values;
905   GError *tmp_error;
906   
907   attr_names = g_newa (const gchar *, context->cur_attr + 2);
908   attr_values = g_newa (const gchar *, context->cur_attr + 2);
909   for (i = 0; i < context->cur_attr + 1; i++)
910     {
911       attr_names[i] = context->attr_names[i]->str;
912       attr_values[i] = context->attr_values[i]->str;
913     }
914   attr_names[i] = NULL;
915   attr_values[i] = NULL;
916   
917   /* Call user callback for element start */
918   tmp_error = NULL;
919   start_name = current_element (context);
920   
921   if (context->parser->start_element &&
922       name_validate (context, start_name, error))
923     (* context->parser->start_element) (context,
924                                         start_name,
925                                         (const gchar **)attr_names,
926                                         (const gchar **)attr_values,
927                                         context->user_data,
928                                         &tmp_error);
929   clear_attributes (context);
930   
931   if (tmp_error != NULL)
932     propagate_error (context, error, tmp_error);
933 }
934
935 /**
936  * g_markup_parse_context_parse:
937  * @context: a #GMarkupParseContext
938  * @text: chunk of text to parse
939  * @text_len: length of @text in bytes
940  * @error: return location for a #GError
941  * 
942  * Feed some data to the #GMarkupParseContext. The data need not
943  * be valid UTF-8; an error will be signaled if it's invalid.
944  * The data need not be an entire document; you can feed a document
945  * into the parser incrementally, via multiple calls to this function.
946  * Typically, as you receive data from a network connection or file,
947  * you feed each received chunk of data into this function, aborting
948  * the process if an error occurs. Once an error is reported, no further
949  * data may be fed to the #GMarkupParseContext; all errors are fatal.
950  * 
951  * Return value: %FALSE if an error occurred, %TRUE on success
952  **/
953 gboolean
954 g_markup_parse_context_parse (GMarkupParseContext *context,
955                               const gchar         *text,
956                               gssize               text_len,
957                               GError             **error)
958 {
959   g_return_val_if_fail (context != NULL, FALSE);
960   g_return_val_if_fail (text != NULL, FALSE);
961   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
962   g_return_val_if_fail (!context->parsing, FALSE);
963   
964   if (text_len < 0)
965     text_len = strlen (text);
966
967   if (text_len == 0)
968     return TRUE;
969   
970   context->parsing = TRUE;
971   
972
973   context->current_text = text;
974   context->current_text_len = text_len;
975   context->current_text_end = context->current_text + text_len;
976   context->iter = context->current_text;
977   context->start = context->iter;
978
979   if (context->current_text_len == 0)
980     goto finished;
981
982   while (context->iter != context->current_text_end)
983     {
984       switch (context->state)
985         {
986         case STATE_START:
987           /* Possible next state: AFTER_OPEN_ANGLE */
988
989           g_assert (context->tag_stack == NULL);
990
991           /* whitespace is ignored outside of any elements */
992           skip_spaces (context);
993
994           if (context->iter != context->current_text_end)
995             {
996               if (*context->iter == '<')
997                 {
998                   /* Move after the open angle */
999                   advance_char (context);
1000
1001                   context->state = STATE_AFTER_OPEN_ANGLE;
1002
1003                   /* this could start a passthrough */
1004                   context->start = context->iter;
1005
1006                   /* document is now non-empty */
1007                   context->document_empty = FALSE;
1008                 }
1009               else
1010                 {
1011                   set_error_literal (context,
1012                                      error,
1013                                      G_MARKUP_ERROR_PARSE,
1014                                      _("Document must begin with an element (e.g. <book>)"));
1015                 }
1016             }
1017           break;
1018
1019         case STATE_AFTER_OPEN_ANGLE:
1020           /* Possible next states: INSIDE_OPEN_TAG_NAME,
1021            *  AFTER_CLOSE_TAG_SLASH, INSIDE_PASSTHROUGH
1022            */
1023           if (*context->iter == '?' ||
1024               *context->iter == '!')
1025             {
1026               /* include < in the passthrough */
1027               const gchar *openangle = "<";
1028               add_to_partial (context, openangle, openangle + 1);
1029               context->start = context->iter;
1030               context->balance = 1;
1031               context->state = STATE_INSIDE_PASSTHROUGH;
1032             }
1033           else if (*context->iter == '/')
1034             {
1035               /* move after it */
1036               advance_char (context);
1037
1038               context->state = STATE_AFTER_CLOSE_TAG_SLASH;
1039             }
1040           else if (!IS_COMMON_NAME_END_CHAR (*(context->iter)))
1041             {
1042               context->state = STATE_INSIDE_OPEN_TAG_NAME;
1043
1044               /* start of tag name */
1045               context->start = context->iter;
1046             }
1047           else
1048             {
1049               gchar buf[8];
1050
1051               set_error (context,
1052                          error,
1053                          G_MARKUP_ERROR_PARSE,
1054                          _("'%s' is not a valid character following "
1055                            "a '<' character; it may not begin an "
1056                            "element name"),
1057                          utf8_str (context->iter, buf));
1058             }
1059           break;
1060
1061           /* The AFTER_CLOSE_ANGLE state is actually sort of
1062            * broken, because it doesn't correspond to a range
1063            * of characters in the input stream as the others do,
1064            * and thus makes things harder to conceptualize
1065            */
1066         case STATE_AFTER_CLOSE_ANGLE:
1067           /* Possible next states: INSIDE_TEXT, STATE_START */
1068           if (context->tag_stack == NULL)
1069             {
1070               context->start = NULL;
1071               context->state = STATE_START;
1072             }
1073           else
1074             {
1075               context->start = context->iter;
1076               context->state = STATE_INSIDE_TEXT;
1077             }
1078           break;
1079
1080         case STATE_AFTER_ELISION_SLASH:
1081           /* Possible next state: AFTER_CLOSE_ANGLE */
1082
1083           {
1084             /* We need to pop the tag stack and call the end_element
1085              * function, since this is the close tag
1086              */
1087             GError *tmp_error = NULL;
1088           
1089             g_assert (context->tag_stack != NULL);
1090
1091             possibly_finish_subparser (context);
1092
1093             tmp_error = NULL;
1094             if (context->parser->end_element)
1095               (* context->parser->end_element) (context,
1096                                                 current_element (context),
1097                                                 context->user_data,
1098                                                 &tmp_error);
1099
1100             ensure_no_outstanding_subparser (context);
1101           
1102             if (tmp_error)
1103               {
1104                 mark_error (context, tmp_error);
1105                 g_propagate_error (error, tmp_error);
1106               }          
1107             else
1108               {
1109                 if (*context->iter == '>')
1110                   {
1111                     /* move after the close angle */
1112                     advance_char (context);
1113                     context->state = STATE_AFTER_CLOSE_ANGLE;
1114                   }
1115                 else
1116                   {
1117                     gchar buf[8];
1118
1119                     set_error (context,
1120                                error,
1121                                G_MARKUP_ERROR_PARSE,
1122                                _("Odd character '%s', expected a '>' character "
1123                                  "to end the empty-element tag '%s'"),
1124                                utf8_str (context->iter, buf),
1125                                current_element (context));
1126                   }
1127               }
1128             pop_tag (context);
1129           }
1130           break;
1131
1132         case STATE_INSIDE_OPEN_TAG_NAME:
1133           /* Possible next states: BETWEEN_ATTRIBUTES */
1134
1135           /* if there's a partial chunk then it's the first part of the
1136            * tag name. If there's a context->start then it's the start
1137            * of the tag name in current_text, the partial chunk goes
1138            * before that start though.
1139            */
1140           advance_to_name_end (context);
1141
1142           if (context->iter == context->current_text_end)
1143             {
1144               /* The name hasn't necessarily ended. Merge with
1145                * partial chunk, leave state unchanged.
1146                */
1147               add_to_partial (context, context->start, context->iter);
1148             }
1149           else
1150             {
1151               /* The name has ended. Combine it with the partial chunk
1152                * if any; push it on the stack; enter next state.
1153                */
1154               add_to_partial (context, context->start, context->iter);
1155               push_partial_as_tag (context);
1156
1157               context->state = STATE_BETWEEN_ATTRIBUTES;
1158               context->start = NULL;
1159             }
1160           break;
1161
1162         case STATE_INSIDE_ATTRIBUTE_NAME:
1163           /* Possible next states: AFTER_ATTRIBUTE_NAME */
1164
1165           advance_to_name_end (context);
1166           add_to_partial (context, context->start, context->iter);
1167
1168           /* read the full name, if we enter the equals sign state
1169            * then add the attribute to the list (without the value),
1170            * otherwise store a partial chunk to be prepended later.
1171            */
1172           if (context->iter != context->current_text_end)
1173             context->state = STATE_AFTER_ATTRIBUTE_NAME;
1174           break;
1175
1176         case STATE_AFTER_ATTRIBUTE_NAME:
1177           /* Possible next states: AFTER_ATTRIBUTE_EQUALS_SIGN */
1178
1179           skip_spaces (context);
1180
1181           if (context->iter != context->current_text_end)
1182             {
1183               /* The name has ended. Combine it with the partial chunk
1184                * if any; push it on the stack; enter next state.
1185                */
1186               if (!name_validate (context, context->partial_chunk->str, error))
1187                 break;
1188
1189               add_attribute (context, context->partial_chunk);
1190               
1191               context->partial_chunk = NULL;
1192               context->start = NULL;
1193               
1194               if (*context->iter == '=')
1195                 {
1196                   advance_char (context);
1197                   context->state = STATE_AFTER_ATTRIBUTE_EQUALS_SIGN;
1198                 }
1199               else
1200                 {
1201                   gchar buf[8];
1202
1203                   set_error (context,
1204                              error,
1205                              G_MARKUP_ERROR_PARSE,
1206                              _("Odd character '%s', expected a '=' after "
1207                                "attribute name '%s' of element '%s'"),
1208                              utf8_str (context->iter, buf),
1209                              current_attribute (context),
1210                              current_element (context));
1211                   
1212                 }
1213             }
1214           break;
1215
1216         case STATE_BETWEEN_ATTRIBUTES:
1217           /* Possible next states: AFTER_CLOSE_ANGLE,
1218            * AFTER_ELISION_SLASH, INSIDE_ATTRIBUTE_NAME
1219            */
1220           skip_spaces (context);
1221
1222           if (context->iter != context->current_text_end)
1223             {
1224               if (*context->iter == '/')
1225                 {
1226                   advance_char (context);
1227                   context->state = STATE_AFTER_ELISION_SLASH;
1228                 }
1229               else if (*context->iter == '>')
1230                 {
1231                   advance_char (context);
1232                   context->state = STATE_AFTER_CLOSE_ANGLE;
1233                 }
1234               else if (!IS_COMMON_NAME_END_CHAR (*(context->iter)))
1235                 {
1236                   context->state = STATE_INSIDE_ATTRIBUTE_NAME;
1237                   /* start of attribute name */
1238                   context->start = context->iter;
1239                 }
1240               else
1241                 {
1242                   gchar buf[8];
1243
1244                   set_error (context,
1245                              error,
1246                              G_MARKUP_ERROR_PARSE,
1247                              _("Odd character '%s', expected a '>' or '/' "
1248                                "character to end the start tag of "
1249                                "element '%s', or optionally an attribute; "
1250                                "perhaps you used an invalid character in "
1251                                "an attribute name"),
1252                              utf8_str (context->iter, buf),
1253                              current_element (context));
1254                 }
1255
1256               /* If we're done with attributes, invoke
1257                * the start_element callback
1258                */
1259               if (context->state == STATE_AFTER_ELISION_SLASH ||
1260                   context->state == STATE_AFTER_CLOSE_ANGLE)
1261                 emit_start_element (context, error);
1262             }
1263           break;
1264
1265         case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1266           /* Possible next state: INSIDE_ATTRIBUTE_VALUE_[SQ/DQ] */
1267
1268           skip_spaces (context);
1269
1270           if (context->iter != context->current_text_end)
1271             {
1272               if (*context->iter == '"')
1273                 {
1274                   advance_char (context);
1275                   context->state = STATE_INSIDE_ATTRIBUTE_VALUE_DQ;
1276                   context->start = context->iter;
1277                 }
1278               else if (*context->iter == '\'')
1279                 {
1280                   advance_char (context);
1281                   context->state = STATE_INSIDE_ATTRIBUTE_VALUE_SQ;
1282                   context->start = context->iter;
1283                 }
1284               else
1285                 {
1286                   gchar buf[8];
1287                   
1288                   set_error (context,
1289                              error,
1290                              G_MARKUP_ERROR_PARSE,
1291                              _("Odd character '%s', expected an open quote mark "
1292                                "after the equals sign when giving value for "
1293                                "attribute '%s' of element '%s'"),
1294                              utf8_str (context->iter, buf),
1295                              current_attribute (context),
1296                              current_element (context));
1297                 }
1298             }
1299           break;
1300
1301         case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1302         case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1303           /* Possible next states: BETWEEN_ATTRIBUTES */
1304           {
1305             gchar delim;
1306
1307             if (context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ) 
1308               {
1309                 delim = '\'';
1310               }
1311             else 
1312               {
1313                 delim = '"';
1314               }
1315
1316             do
1317               {
1318                 if (*context->iter == delim)
1319                   break;
1320               }
1321             while (advance_char (context));
1322           }
1323           if (context->iter == context->current_text_end)
1324             {
1325               /* The value hasn't necessarily ended. Merge with
1326                * partial chunk, leave state unchanged.
1327                */
1328               add_to_partial (context, context->start, context->iter);
1329             }
1330           else
1331             {
1332               gboolean is_ascii;
1333               /* The value has ended at the quote mark. Combine it
1334                * with the partial chunk if any; set it for the current
1335                * attribute.
1336                */
1337               add_to_partial (context, context->start, context->iter);
1338
1339               g_assert (context->cur_attr >= 0);
1340               
1341               if (unescape_gstring_inplace (context, context->partial_chunk, &is_ascii, error) &&
1342                   (is_ascii || text_validate (context, context->partial_chunk->str,
1343                                               context->partial_chunk->len, error)))
1344                 {
1345                   /* success, advance past quote and set state. */
1346                   context->attr_values[context->cur_attr] = context->partial_chunk;
1347                   context->partial_chunk = NULL;
1348                   advance_char (context);
1349                   context->state = STATE_BETWEEN_ATTRIBUTES;
1350                   context->start = NULL;
1351                 }
1352               
1353               truncate_partial (context);
1354             }
1355           break;
1356
1357         case STATE_INSIDE_TEXT:
1358           /* Possible next states: AFTER_OPEN_ANGLE */
1359           do
1360             {
1361               if (*context->iter == '<')
1362                 break;
1363             }
1364           while (advance_char (context));
1365
1366           /* The text hasn't necessarily ended. Merge with
1367            * partial chunk, leave state unchanged.
1368            */
1369
1370           add_to_partial (context, context->start, context->iter);
1371
1372           if (context->iter != context->current_text_end)
1373             {
1374               gboolean is_ascii;
1375
1376               /* The text has ended at the open angle. Call the text
1377                * callback.
1378                */
1379               
1380               if (unescape_gstring_inplace (context, context->partial_chunk, &is_ascii, error) &&
1381                   (is_ascii || text_validate (context, context->partial_chunk->str,
1382                                               context->partial_chunk->len, error)))
1383                 {
1384                   GError *tmp_error = NULL;
1385
1386                   if (context->parser->text)
1387                     (*context->parser->text) (context,
1388                                               context->partial_chunk->str,
1389                                               context->partial_chunk->len,
1390                                               context->user_data,
1391                                               &tmp_error);
1392                   
1393                   if (tmp_error == NULL)
1394                     {
1395                       /* advance past open angle and set state. */
1396                       advance_char (context);
1397                       context->state = STATE_AFTER_OPEN_ANGLE;
1398                       /* could begin a passthrough */
1399                       context->start = context->iter;
1400                     }
1401                   else
1402                     propagate_error (context, error, tmp_error);
1403                 }
1404
1405               truncate_partial (context);
1406             }
1407           break;
1408
1409         case STATE_AFTER_CLOSE_TAG_SLASH:
1410           /* Possible next state: INSIDE_CLOSE_TAG_NAME */
1411           if (!IS_COMMON_NAME_END_CHAR (*(context->iter)))
1412             {
1413               context->state = STATE_INSIDE_CLOSE_TAG_NAME;
1414
1415               /* start of tag name */
1416               context->start = context->iter;
1417             }
1418           else
1419             {
1420               gchar buf[8];
1421
1422               set_error (context,
1423                          error,
1424                          G_MARKUP_ERROR_PARSE,
1425                          _("'%s' is not a valid character following "
1426                            "the characters '</'; '%s' may not begin an "
1427                            "element name"),
1428                          utf8_str (context->iter, buf),
1429                          utf8_str (context->iter, buf));
1430             }
1431           break;
1432
1433         case STATE_INSIDE_CLOSE_TAG_NAME:
1434           /* Possible next state: AFTER_CLOSE_TAG_NAME */
1435           advance_to_name_end (context);
1436           add_to_partial (context, context->start, context->iter);
1437
1438           if (context->iter != context->current_text_end)
1439             context->state = STATE_AFTER_CLOSE_TAG_NAME;
1440           break;
1441
1442         case STATE_AFTER_CLOSE_TAG_NAME:
1443           /* Possible next state: AFTER_CLOSE_TAG_SLASH */
1444
1445           skip_spaces (context);
1446           
1447           if (context->iter != context->current_text_end)
1448             {
1449               GString *close_name;
1450
1451               close_name = context->partial_chunk;
1452               context->partial_chunk = NULL;
1453               
1454               if (*context->iter != '>')
1455                 {
1456                   gchar buf[8];
1457
1458                   set_error (context,
1459                              error,
1460                              G_MARKUP_ERROR_PARSE,
1461                              _("'%s' is not a valid character following "
1462                                "the close element name '%s'; the allowed "
1463                                "character is '>'"),
1464                              utf8_str (context->iter, buf),
1465                              close_name->str);
1466                 }
1467               else if (context->tag_stack == NULL)
1468                 {
1469                   set_error (context,
1470                              error,
1471                              G_MARKUP_ERROR_PARSE,
1472                              _("Element '%s' was closed, no element "
1473                                "is currently open"),
1474                              close_name->str);
1475                 }
1476               else if (strcmp (close_name->str, current_element (context)) != 0)
1477                 {
1478                   set_error (context,
1479                              error,
1480                              G_MARKUP_ERROR_PARSE,
1481                              _("Element '%s' was closed, but the currently "
1482                                "open element is '%s'"),
1483                              close_name->str,
1484                              current_element (context));
1485                 }
1486               else
1487                 {
1488                   GError *tmp_error;
1489                   advance_char (context);
1490                   context->state = STATE_AFTER_CLOSE_ANGLE;
1491                   context->start = NULL;
1492                   
1493                   possibly_finish_subparser (context);
1494
1495                   /* call the end_element callback */
1496                   tmp_error = NULL;
1497                   if (context->parser->end_element)
1498                     (* context->parser->end_element) (context,
1499                                                       close_name->str,
1500                                                       context->user_data,
1501                                                       &tmp_error);
1502                   
1503                   ensure_no_outstanding_subparser (context);
1504                   pop_tag (context);
1505                   
1506                   if (tmp_error)
1507                     propagate_error (context, error, tmp_error);
1508                 }
1509               context->partial_chunk = close_name;
1510               truncate_partial (context);
1511             }
1512           break;
1513           
1514         case STATE_INSIDE_PASSTHROUGH:
1515           /* Possible next state: AFTER_CLOSE_ANGLE */
1516           do
1517             {
1518               if (*context->iter == '<') 
1519                 context->balance++;
1520               if (*context->iter == '>') 
1521                 {                               
1522                   gchar *str;
1523                   gsize len;
1524
1525                   context->balance--;
1526                   add_to_partial (context, context->start, context->iter);
1527                   context->start = context->iter;
1528
1529                   str = context->partial_chunk->str;
1530                   len = context->partial_chunk->len;
1531
1532                   if (str[1] == '?' && str[len - 1] == '?')
1533                     break;
1534                   if (strncmp (str, "<!--", 4) == 0 && 
1535                       strcmp (str + len - 2, "--") == 0)
1536                     break;
1537                   if (strncmp (str, "<![CDATA[", 9) == 0 && 
1538                       strcmp (str + len - 2, "]]") == 0)
1539                     break;
1540                   if (strncmp (str, "<!DOCTYPE", 9) == 0 &&
1541                       context->balance == 0)
1542                     break;
1543                 }
1544             }
1545           while (advance_char (context));
1546
1547           if (context->iter == context->current_text_end)
1548             {
1549               /* The passthrough hasn't necessarily ended. Merge with
1550                * partial chunk, leave state unchanged.
1551                */
1552                add_to_partial (context, context->start, context->iter);
1553             }
1554           else
1555             {
1556               /* The passthrough has ended at the close angle. Combine
1557                * it with the partial chunk if any. Call the passthrough
1558                * callback. Note that the open/close angles are
1559                * included in the text of the passthrough.
1560                */
1561               GError *tmp_error = NULL;
1562
1563               advance_char (context); /* advance past close angle */
1564               add_to_partial (context, context->start, context->iter);
1565
1566               if (context->flags & G_MARKUP_TREAT_CDATA_AS_TEXT &&
1567                   strncmp (context->partial_chunk->str, "<![CDATA[", 9) == 0)
1568                 {
1569                   if (context->parser->text &&
1570                       text_validate (context,
1571                                      context->partial_chunk->str + 9,
1572                                      context->partial_chunk->len - 12,
1573                                      error))
1574                     (*context->parser->text) (context,
1575                                               context->partial_chunk->str + 9,
1576                                               context->partial_chunk->len - 12,
1577                                               context->user_data,
1578                                               &tmp_error);
1579                 }
1580               else if (context->parser->passthrough &&
1581                        text_validate (context,
1582                                       context->partial_chunk->str,
1583                                       context->partial_chunk->len,
1584                                       error))
1585                 (*context->parser->passthrough) (context,
1586                                                  context->partial_chunk->str,
1587                                                  context->partial_chunk->len,
1588                                                  context->user_data,
1589                                                  &tmp_error);
1590                   
1591               truncate_partial (context);
1592
1593               if (tmp_error == NULL)
1594                 {
1595                   context->state = STATE_AFTER_CLOSE_ANGLE;
1596                   context->start = context->iter; /* could begin text */
1597                 }
1598               else
1599                 propagate_error (context, error, tmp_error);
1600             }
1601           break;
1602
1603         case STATE_ERROR:
1604           goto finished;
1605           break;
1606
1607         default:
1608           g_assert_not_reached ();
1609           break;
1610         }
1611     }
1612
1613  finished:
1614   context->parsing = FALSE;
1615
1616   return context->state != STATE_ERROR;
1617 }
1618
1619 /**
1620  * g_markup_parse_context_end_parse:
1621  * @context: a #GMarkupParseContext
1622  * @error: return location for a #GError
1623  * 
1624  * Signals to the #GMarkupParseContext that all data has been
1625  * fed into the parse context with g_markup_parse_context_parse().
1626  * This function reports an error if the document isn't complete,
1627  * for example if elements are still open.
1628  * 
1629  * Return value: %TRUE on success, %FALSE if an error was set
1630  **/
1631 gboolean
1632 g_markup_parse_context_end_parse (GMarkupParseContext *context,
1633                                   GError             **error)
1634 {
1635   g_return_val_if_fail (context != NULL, FALSE);
1636   g_return_val_if_fail (!context->parsing, FALSE);
1637   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
1638
1639   if (context->partial_chunk != NULL)
1640     {
1641       g_string_free (context->partial_chunk, TRUE);
1642       context->partial_chunk = NULL;
1643     }
1644
1645   if (context->document_empty)
1646     {
1647       set_error_literal (context, error, G_MARKUP_ERROR_EMPTY,
1648                          _("Document was empty or contained only whitespace"));
1649       return FALSE;
1650     }
1651   
1652   context->parsing = TRUE;
1653   
1654   switch (context->state)
1655     {
1656     case STATE_START:
1657       /* Nothing to do */
1658       break;
1659
1660     case STATE_AFTER_OPEN_ANGLE:
1661       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1662                          _("Document ended unexpectedly just after an open angle bracket '<'"));
1663       break;
1664
1665     case STATE_AFTER_CLOSE_ANGLE:
1666       if (context->tag_stack != NULL)
1667         {
1668           /* Error message the same as for INSIDE_TEXT */
1669           set_error (context, error, G_MARKUP_ERROR_PARSE,
1670                      _("Document ended unexpectedly with elements still open - "
1671                        "'%s' was the last element opened"),
1672                      current_element (context));
1673         }
1674       break;
1675       
1676     case STATE_AFTER_ELISION_SLASH:
1677       set_error (context, error, G_MARKUP_ERROR_PARSE,
1678                  _("Document ended unexpectedly, expected to see a close angle "
1679                    "bracket ending the tag <%s/>"), current_element (context));
1680       break;
1681
1682     case STATE_INSIDE_OPEN_TAG_NAME:
1683       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1684                          _("Document ended unexpectedly inside an element name"));
1685       break;
1686
1687     case STATE_INSIDE_ATTRIBUTE_NAME:
1688     case STATE_AFTER_ATTRIBUTE_NAME:
1689       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1690                          _("Document ended unexpectedly inside an attribute name"));
1691       break;
1692
1693     case STATE_BETWEEN_ATTRIBUTES:
1694       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1695                          _("Document ended unexpectedly inside an element-opening "
1696                            "tag."));
1697       break;
1698
1699     case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1700       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1701                          _("Document ended unexpectedly after the equals sign "
1702                            "following an attribute name; no attribute value"));
1703       break;
1704
1705     case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1706     case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1707       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1708                          _("Document ended unexpectedly while inside an attribute "
1709                            "value"));
1710       break;
1711
1712     case STATE_INSIDE_TEXT:
1713       g_assert (context->tag_stack != NULL);
1714       set_error (context, error, G_MARKUP_ERROR_PARSE,
1715                  _("Document ended unexpectedly with elements still open - "
1716                    "'%s' was the last element opened"),
1717                  current_element (context));
1718       break;
1719
1720     case STATE_AFTER_CLOSE_TAG_SLASH:
1721     case STATE_INSIDE_CLOSE_TAG_NAME:
1722     case STATE_AFTER_CLOSE_TAG_NAME:
1723       set_error (context, error, G_MARKUP_ERROR_PARSE,
1724                  _("Document ended unexpectedly inside the close tag for "
1725                    "element '%s'"), current_element (context));
1726       break;
1727
1728     case STATE_INSIDE_PASSTHROUGH:
1729       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1730                          _("Document ended unexpectedly inside a comment or "
1731                            "processing instruction"));
1732       break;
1733
1734     case STATE_ERROR:
1735     default:
1736       g_assert_not_reached ();
1737       break;
1738     }
1739
1740   context->parsing = FALSE;
1741
1742   return context->state != STATE_ERROR;
1743 }
1744
1745 /**
1746  * g_markup_parse_context_get_element:
1747  * @context: a #GMarkupParseContext
1748  * @returns: the name of the currently open element, or %NULL
1749  *
1750  * Retrieves the name of the currently open element.
1751  *
1752  * If called from the start_element or end_element handlers this will
1753  * give the element_name as passed to those functions. For the parent
1754  * elements, see g_markup_parse_context_get_element_stack().
1755  *
1756  * Since: 2.2
1757  **/
1758 G_CONST_RETURN gchar *
1759 g_markup_parse_context_get_element (GMarkupParseContext *context)
1760 {
1761   g_return_val_if_fail (context != NULL, NULL);
1762
1763   if (context->tag_stack == NULL) 
1764     return NULL;
1765   else
1766     return current_element (context);
1767
1768
1769 /**
1770  * g_markup_parse_context_get_element_stack:
1771  * @context: a #GMarkupParseContext
1772  *
1773  * Retrieves the element stack from the internal state of the parser.
1774  * The returned #GSList is a list of strings where the first item is
1775  * the currently open tag (as would be returned by
1776  * g_markup_parse_context_get_element()) and the next item is its
1777  * immediate parent.
1778  *
1779  * This function is intended to be used in the start_element and
1780  * end_element handlers where g_markup_parse_context_get_element()
1781  * would merely return the name of the element that is being
1782  * processed.
1783  *
1784  * Returns: the element stack, which must not be modified
1785  *
1786  * Since: 2.16
1787  **/
1788 G_CONST_RETURN GSList *
1789 g_markup_parse_context_get_element_stack (GMarkupParseContext *context)
1790 {
1791   g_return_val_if_fail (context != NULL, NULL);
1792   return context->tag_stack;
1793 }
1794
1795 /**
1796  * g_markup_parse_context_get_position:
1797  * @context: a #GMarkupParseContext
1798  * @line_number: return location for a line number, or %NULL
1799  * @char_number: return location for a char-on-line number, or %NULL
1800  *
1801  * Retrieves the current line number and the number of the character on
1802  * that line. Intended for use in error messages; there are no strict
1803  * semantics for what constitutes the "current" line number other than
1804  * "the best number we could come up with for error messages."
1805  * 
1806  **/
1807 void
1808 g_markup_parse_context_get_position (GMarkupParseContext *context,
1809                                      gint                *line_number,
1810                                      gint                *char_number)
1811 {
1812   g_return_if_fail (context != NULL);
1813
1814   if (line_number)
1815     *line_number = context->line_number;
1816
1817   if (char_number)
1818     *char_number = context->char_number;
1819 }
1820
1821 /**
1822  * g_markup_parse_context_get_user_data:
1823  * @context: a #GMarkupParseContext
1824  *
1825  * Returns the user_data associated with @context.  This will either
1826  * be the user_data that was provided to g_markup_parse_context_new()
1827  * or to the most recent call of g_markup_parse_context_push().
1828  *
1829  * Returns: the provided user_data. The returned data belongs to
1830  *     the markup context and will be freed when g_markup_context_free()
1831  *     is called.
1832  *
1833  * Since: 2.18
1834  **/
1835 gpointer
1836 g_markup_parse_context_get_user_data (GMarkupParseContext *context)
1837 {
1838   return context->user_data;
1839 }
1840
1841 /**
1842  * g_markup_parse_context_push:
1843  * @context: a #GMarkupParseContext
1844  * @parser: a #GMarkupParser
1845  * @user_data: user data to pass to #GMarkupParser functions
1846  *
1847  * Temporarily redirects markup data to a sub-parser.
1848  *
1849  * This function may only be called from the start_element handler of
1850  * a #GMarkupParser.  It must be matched with a corresponding call to
1851  * g_markup_parse_context_pop() in the matching end_element handler
1852  * (except in the case that the parser aborts due to an error).
1853  *
1854  * All tags, text and other data between the matching tags is
1855  * redirected to the subparser given by @parser.  @user_data is used
1856  * as the user_data for that parser.  @user_data is also passed to the
1857  * error callback in the event that an error occurs.  This includes
1858  * errors that occur in subparsers of the subparser.
1859  *
1860  * The end tag matching the start tag for which this call was made is
1861  * handled by the previous parser (which is given its own user_data)
1862  * which is why g_markup_parse_context_pop() is provided to allow "one
1863  * last access" to the @user_data provided to this function.  In the
1864  * case of error, the @user_data provided here is passed directly to
1865  * the error callback of the subparser and g_markup_parse_context()
1866  * should not be called.  In either case, if @user_data was allocated
1867  * then it ought to be freed from both of these locations.
1868  *
1869  * This function is not intended to be directly called by users
1870  * interested in invoking subparsers.  Instead, it is intended to be
1871  * used by the subparsers themselves to implement a higher-level
1872  * interface.
1873  *
1874  * As an example, see the following implementation of a simple
1875  * parser that counts the number of tags encountered.
1876  *
1877  * |[
1878  * typedef struct
1879  * {
1880  *   gint tag_count;
1881  * } CounterData;
1882  * 
1883  * static void
1884  * counter_start_element (GMarkupParseContext  *context,
1885  *                        const gchar          *element_name,
1886  *                        const gchar         **attribute_names,
1887  *                        const gchar         **attribute_values,
1888  *                        gpointer              user_data,
1889  *                        GError              **error)
1890  * {
1891  *   CounterData *data = user_data;
1892  * 
1893  *   data->tag_count++;
1894  * }
1895  * 
1896  * static void
1897  * counter_error (GMarkupParseContext *context,
1898  *                GError              *error,
1899  *                gpointer             user_data)
1900  * {
1901  *   CounterData *data = user_data;
1902  * 
1903  *   g_slice_free (CounterData, data);
1904  * }
1905  * 
1906  * static GMarkupParser counter_subparser =
1907  * {
1908  *   counter_start_element,
1909  *   NULL,
1910  *   NULL,
1911  *   NULL,
1912  *   counter_error
1913  * };
1914  * ]|
1915  *
1916  * In order to allow this parser to be easily used as a subparser, the
1917  * following interface is provided:
1918  *
1919  * |[
1920  * void
1921  * start_counting (GMarkupParseContext *context)
1922  * {
1923  *   CounterData *data = g_slice_new (CounterData);
1924  * 
1925  *   data->tag_count = 0;
1926  *   g_markup_parse_context_push (context, &counter_subparser, data);
1927  * }
1928  * 
1929  * gint
1930  * end_counting (GMarkupParseContext *context)
1931  * {
1932  *   CounterData *data = g_markup_parse_context_pop (context);
1933  *   int result;
1934  * 
1935  *   result = data->tag_count;
1936  *   g_slice_free (CounterData, data);
1937  * 
1938  *   return result;
1939  * }
1940  * ]|
1941  *
1942  * The subparser would then be used as follows:
1943  *
1944  * |[
1945  * static void start_element (context, element_name, ...)
1946  * {
1947  *   if (strcmp (element_name, "count-these") == 0)
1948  *     start_counting (context);
1949  * 
1950  *   /&ast; else, handle other tags... &ast;/
1951  * }
1952  * 
1953  * static void end_element (context, element_name, ...)
1954  * {
1955  *   if (strcmp (element_name, "count-these") == 0)
1956  *     g_print ("Counted %d tags\n", end_counting (context));
1957  * 
1958  *   /&ast; else, handle other tags... &ast;/
1959  * }
1960  * ]|
1961  *
1962  * Since: 2.18
1963  **/
1964 void
1965 g_markup_parse_context_push (GMarkupParseContext *context,
1966                              GMarkupParser       *parser,
1967                              gpointer             user_data)
1968 {
1969   GMarkupRecursionTracker *tracker;
1970
1971   tracker = g_slice_new (GMarkupRecursionTracker);
1972   tracker->prev_element = context->subparser_element;
1973   tracker->prev_parser = context->parser;
1974   tracker->prev_user_data = context->user_data;
1975
1976   context->subparser_element = current_element (context);
1977   context->parser = parser;
1978   context->user_data = user_data;
1979
1980   context->subparser_stack = g_slist_prepend (context->subparser_stack,
1981                                               tracker);
1982 }
1983
1984 /**
1985  * g_markup_parse_context_pop:
1986  * @context: a #GMarkupParseContext
1987  *
1988  * Completes the process of a temporary sub-parser redirection.
1989  *
1990  * This function exists to collect the user_data allocated by a
1991  * matching call to g_markup_parse_context_push().  It must be called
1992  * in the end_element handler corresponding to the start_element
1993  * handler during which g_markup_parse_context_push() was called.  You
1994  * must not call this function from the error callback -- the
1995  * @user_data is provided directly to the callback in that case.
1996  *
1997  * This function is not intended to be directly called by users
1998  * interested in invoking subparsers.  Instead, it is intended to be
1999  * used by the subparsers themselves to implement a higher-level
2000  * interface.
2001  *
2002  * Returns: the user_data passed to g_markup_parse_context_push().
2003  *
2004  * Since: 2.18
2005  **/
2006 gpointer
2007 g_markup_parse_context_pop (GMarkupParseContext *context)
2008 {
2009   gpointer user_data;
2010
2011   if (!context->awaiting_pop)
2012     possibly_finish_subparser (context);
2013
2014   g_assert (context->awaiting_pop);
2015
2016   context->awaiting_pop = FALSE;
2017   
2018   /* valgrind friendliness */
2019   user_data = context->held_user_data;
2020   context->held_user_data = NULL;
2021
2022   return user_data;
2023 }
2024
2025 static void
2026 append_escaped_text (GString     *str,
2027                      const gchar *text,
2028                      gssize       length)    
2029 {
2030   const gchar *p;
2031   const gchar *end;
2032   gunichar c;
2033
2034   p = text;
2035   end = text + length;
2036
2037   while (p != end)
2038     {
2039       const gchar *next;
2040       next = g_utf8_next_char (p);
2041
2042       switch (*p)
2043         {
2044         case '&':
2045           g_string_append (str, "&amp;");
2046           break;
2047
2048         case '<':
2049           g_string_append (str, "&lt;");
2050           break;
2051
2052         case '>':
2053           g_string_append (str, "&gt;");
2054           break;
2055
2056         case '\'':
2057           g_string_append (str, "&apos;");
2058           break;
2059
2060         case '"':
2061           g_string_append (str, "&quot;");
2062           break;
2063
2064         default:
2065           c = g_utf8_get_char (p);
2066           if ((0x1 <= c && c <= 0x8) ||
2067               (0xb <= c && c  <= 0xc) ||
2068               (0xe <= c && c <= 0x1f) ||
2069               (0x7f <= c && c <= 0x84) ||
2070               (0x86 <= c && c <= 0x9f))
2071             g_string_append_printf (str, "&#x%x;", c);
2072           else
2073             g_string_append_len (str, p, next - p);
2074           break;
2075         }
2076
2077       p = next;
2078     }
2079 }
2080
2081 /**
2082  * g_markup_escape_text:
2083  * @text: some valid UTF-8 text
2084  * @length: length of @text in bytes, or -1 if the text is nul-terminated
2085  * 
2086  * Escapes text so that the markup parser will parse it verbatim.
2087  * Less than, greater than, ampersand, etc. are replaced with the
2088  * corresponding entities. This function would typically be used
2089  * when writing out a file to be parsed with the markup parser.
2090  * 
2091  * Note that this function doesn't protect whitespace and line endings
2092  * from being processed according to the XML rules for normalization
2093  * of line endings and attribute values.
2094  *
2095  * Note also that if given a string containing them, this function
2096  * will produce character references in the range of &amp;#x1; ..
2097  * &amp;#x1f; for all control sequences except for tabstop, newline
2098  * and carriage return.  The character references in this range are
2099  * not valid XML 1.0, but they are valid XML 1.1 and will be accepted
2100  * by the GMarkup parser.
2101  * 
2102  * Return value: a newly allocated string with the escaped text
2103  **/
2104 gchar*
2105 g_markup_escape_text (const gchar *text,
2106                       gssize       length)  
2107 {
2108   GString *str;
2109
2110   g_return_val_if_fail (text != NULL, NULL);
2111
2112   if (length < 0)
2113     length = strlen (text);
2114
2115   /* prealloc at least as long as original text */
2116   str = g_string_sized_new (length);
2117   append_escaped_text (str, text, length);
2118
2119   return g_string_free (str, FALSE);
2120 }
2121
2122 /**
2123  * find_conversion:
2124  * @format: a printf-style format string
2125  * @after: location to store a pointer to the character after
2126  *   the returned conversion. On a %NULL return, returns the
2127  *   pointer to the trailing NUL in the string
2128  * 
2129  * Find the next conversion in a printf-style format string.
2130  * Partially based on code from printf-parser.c,
2131  * Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
2132  * 
2133  * Return value: pointer to the next conversion in @format,
2134  *  or %NULL, if none.
2135  **/
2136 static const char *
2137 find_conversion (const char  *format,
2138                  const char **after)
2139 {
2140   const char *start = format;
2141   const char *cp;
2142   
2143   while (*start != '\0' && *start != '%')
2144     start++;
2145
2146   if (*start == '\0')
2147     {
2148       *after = start;
2149       return NULL;
2150     }
2151
2152   cp = start + 1;
2153
2154   if (*cp == '\0')
2155     {
2156       *after = cp;
2157       return NULL;
2158     }
2159   
2160   /* Test for positional argument.  */
2161   if (*cp >= '0' && *cp <= '9')
2162     {
2163       const char *np;
2164       
2165       for (np = cp; *np >= '0' && *np <= '9'; np++)
2166         ;
2167       if (*np == '$')
2168         cp = np + 1;
2169     }
2170
2171   /* Skip the flags.  */
2172   for (;;)
2173     {
2174       if (*cp == '\'' ||
2175           *cp == '-' ||
2176           *cp == '+' ||
2177           *cp == ' ' ||
2178           *cp == '#' ||
2179           *cp == '0')
2180         cp++;
2181       else
2182         break;
2183     }
2184
2185   /* Skip the field width.  */
2186   if (*cp == '*')
2187     {
2188       cp++;
2189
2190       /* Test for positional argument.  */
2191       if (*cp >= '0' && *cp <= '9')
2192         {
2193           const char *np;
2194
2195           for (np = cp; *np >= '0' && *np <= '9'; np++)
2196             ;
2197           if (*np == '$')
2198             cp = np + 1;
2199         }
2200     }
2201   else
2202     {
2203       for (; *cp >= '0' && *cp <= '9'; cp++)
2204         ;
2205     }
2206
2207   /* Skip the precision.  */
2208   if (*cp == '.')
2209     {
2210       cp++;
2211       if (*cp == '*')
2212         {
2213           /* Test for positional argument.  */
2214           if (*cp >= '0' && *cp <= '9')
2215             {
2216               const char *np;
2217
2218               for (np = cp; *np >= '0' && *np <= '9'; np++)
2219                 ;
2220               if (*np == '$')
2221                 cp = np + 1;
2222             }
2223         }
2224       else
2225         {
2226           for (; *cp >= '0' && *cp <= '9'; cp++)
2227             ;
2228         }
2229     }
2230
2231   /* Skip argument type/size specifiers.  */
2232   while (*cp == 'h' ||
2233          *cp == 'L' ||
2234          *cp == 'l' ||
2235          *cp == 'j' ||
2236          *cp == 'z' ||
2237          *cp == 'Z' ||
2238          *cp == 't')
2239     cp++;
2240           
2241   /* Skip the conversion character.  */
2242   cp++;
2243
2244   *after = cp;
2245   return start;
2246 }
2247
2248 /**
2249  * g_markup_vprintf_escaped:
2250  * @format: printf() style format string
2251  * @args: variable argument list, similar to vprintf()
2252  * 
2253  * Formats the data in @args according to @format, escaping
2254  * all string and character arguments in the fashion
2255  * of g_markup_escape_text(). See g_markup_printf_escaped().
2256  * 
2257  * Return value: newly allocated result from formatting
2258  *  operation. Free with g_free().
2259  *
2260  * Since: 2.4
2261  **/
2262 char *
2263 g_markup_vprintf_escaped (const char *format,
2264                           va_list     args)
2265 {
2266   GString *format1;
2267   GString *format2;
2268   GString *result = NULL;
2269   gchar *output1 = NULL;
2270   gchar *output2 = NULL;
2271   const char *p, *op1, *op2;
2272   va_list args2;
2273
2274   /* The technique here, is that we make two format strings that
2275    * have the identical conversions in the identical order to the
2276    * original strings, but differ in the text in-between. We
2277    * then use the normal g_strdup_vprintf() to format the arguments
2278    * with the two new format strings. By comparing the results,
2279    * we can figure out what segments of the output come from
2280    * the the original format string, and what from the arguments,
2281    * and thus know what portions of the string to escape.
2282    *
2283    * For instance, for:
2284    *
2285    *  g_markup_printf_escaped ("%s ate %d apples", "Susan & Fred", 5);
2286    *
2287    * We form the two format strings "%sX%dX" and %sY%sY". The results
2288    * of formatting with those two strings are
2289    *
2290    * "%sX%dX" => "Susan & FredX5X"
2291    * "%sY%dY" => "Susan & FredY5Y"
2292    *
2293    * To find the span of the first argument, we find the first position
2294    * where the two arguments differ, which tells us that the first
2295    * argument formatted to "Susan & Fred". We then escape that
2296    * to "Susan &amp; Fred" and join up with the intermediate portions
2297    * of the format string and the second argument to get
2298    * "Susan &amp; Fred ate 5 apples".
2299    */
2300
2301   /* Create the two modified format strings
2302    */
2303   format1 = g_string_new (NULL);
2304   format2 = g_string_new (NULL);
2305   p = format;
2306   while (TRUE)
2307     {
2308       const char *after;
2309       const char *conv = find_conversion (p, &after);
2310       if (!conv)
2311         break;
2312
2313       g_string_append_len (format1, conv, after - conv);
2314       g_string_append_c (format1, 'X');
2315       g_string_append_len (format2, conv, after - conv);
2316       g_string_append_c (format2, 'Y');
2317
2318       p = after;
2319     }
2320
2321   /* Use them to format the arguments
2322    */
2323   G_VA_COPY (args2, args);
2324   
2325   output1 = g_strdup_vprintf (format1->str, args);
2326   if (!output1)
2327     {
2328       va_end (args2);
2329       goto cleanup;
2330     }
2331   
2332   output2 = g_strdup_vprintf (format2->str, args2);
2333   va_end (args2);
2334   if (!output2)
2335     goto cleanup;
2336
2337   result = g_string_new (NULL);
2338
2339   /* Iterate through the original format string again,
2340    * copying the non-conversion portions and the escaped
2341    * converted arguments to the output string.
2342    */
2343   op1 = output1;
2344   op2 = output2;
2345   p = format;
2346   while (TRUE)
2347     {
2348       const char *after;
2349       const char *output_start;
2350       const char *conv = find_conversion (p, &after);
2351       char *escaped;
2352       
2353       if (!conv)        /* The end, after points to the trailing \0 */
2354         {
2355           g_string_append_len (result, p, after - p);
2356           break;
2357         }
2358
2359       g_string_append_len (result, p, conv - p);
2360       output_start = op1;
2361       while (*op1 == *op2)
2362         {
2363           op1++;
2364           op2++;
2365         }
2366       
2367       escaped = g_markup_escape_text (output_start, op1 - output_start);
2368       g_string_append (result, escaped);
2369       g_free (escaped);
2370       
2371       p = after;
2372       op1++;
2373       op2++;
2374     }
2375
2376  cleanup:
2377   g_string_free (format1, TRUE);
2378   g_string_free (format2, TRUE);
2379   g_free (output1);
2380   g_free (output2);
2381
2382   if (result)
2383     return g_string_free (result, FALSE);
2384   else
2385     return NULL;
2386 }
2387
2388 /**
2389  * g_markup_printf_escaped:
2390  * @format: printf() style format string
2391  * @Varargs: the arguments to insert in the format string
2392  * 
2393  * Formats arguments according to @format, escaping
2394  * all string and character arguments in the fashion
2395  * of g_markup_escape_text(). This is useful when you
2396  * want to insert literal strings into XML-style markup
2397  * output, without having to worry that the strings
2398  * might themselves contain markup.
2399  *
2400  * |[
2401  * const char *store = "Fortnum &amp; Mason";
2402  * const char *item = "Tea";
2403  * char *output;
2404  * &nbsp;
2405  * output = g_markup_printf_escaped ("&lt;purchase&gt;"
2406  *                                   "&lt;store&gt;&percnt;s&lt;/store&gt;"
2407  *                                   "&lt;item&gt;&percnt;s&lt;/item&gt;"
2408  *                                   "&lt;/purchase&gt;",
2409  *                                   store, item);
2410  * ]|
2411  * 
2412  * Return value: newly allocated result from formatting
2413  *  operation. Free with g_free().
2414  *
2415  * Since: 2.4
2416  **/
2417 char *
2418 g_markup_printf_escaped (const char *format, ...)
2419 {
2420   char *result;
2421   va_list args;
2422   
2423   va_start (args, format);
2424   result = g_markup_vprintf_escaped (format, args);
2425   va_end (args);
2426
2427   return result;
2428 }
2429
2430 static gboolean
2431 g_markup_parse_boolean (const char  *string,
2432                         gboolean    *value)
2433 {
2434   char const * const falses[] = { "false", "f", "no", "n", "0" };
2435   char const * const trues[] = { "true", "t", "yes", "y", "1" };
2436   int i;
2437
2438   for (i = 0; i < G_N_ELEMENTS (falses); i++)
2439     {
2440       if (g_ascii_strcasecmp (string, falses[i]) == 0)
2441         {
2442           if (value != NULL)
2443             *value = FALSE;
2444
2445           return TRUE;
2446         }
2447     }
2448
2449   for (i = 0; i < G_N_ELEMENTS (trues); i++)
2450     {
2451       if (g_ascii_strcasecmp (string, trues[i]) == 0)
2452         {
2453           if (value != NULL)
2454             *value = TRUE;
2455
2456           return TRUE;
2457         }
2458     }
2459
2460   return FALSE;
2461 }
2462
2463 /**
2464  * GMarkupCollectType:
2465  * @G_MARKUP_COLLECT_INVALID: used to terminate the list of attributes
2466  *                            to collect.
2467  * @G_MARKUP_COLLECT_STRING: collect the string pointer directly from
2468  *                           the attribute_values[] array.  Expects a
2469  *                           parameter of type (const char **).  If
2470  *                           %G_MARKUP_COLLECT_OPTIONAL is specified
2471  *                           and the attribute isn't present then the
2472  *                           pointer will be set to %NULL.
2473  * @G_MARKUP_COLLECT_STRDUP: as with %G_MARKUP_COLLECT_STRING, but
2474  *                           expects a parameter of type (char **) and
2475  *                           g_strdup()s the returned pointer.  The
2476  *                           pointer must be freed with g_free().
2477  * @G_MARKUP_COLLECT_BOOLEAN: expects a parameter of type (gboolean *)
2478  *                            and parses the attribute value as a
2479  *                            boolean.  Sets %FALSE if the attribute
2480  *                            isn't present.  Valid boolean values
2481  *                            consist of (case insensitive) "false",
2482  *                            "f", "no", "n", "0" and "true", "t",
2483  *                            "yes", "y", "1".
2484  * @G_MARKUP_COLLECT_TRISTATE: as with %G_MARKUP_COLLECT_BOOLEAN, but
2485  *                             in the case of a missing attribute a
2486  *                             value is set that compares equal to
2487  *                             neither %FALSE nor %TRUE.
2488  *                             G_MARKUP_COLLECT_OPTIONAL is implied.
2489  * @G_MARKUP_COLLECT_OPTIONAL: can be bitwise ORed with the other
2490  *                             fields.  If present, allows the
2491  *                             attribute not to appear.  A default
2492  *                             value is set depending on what value
2493  *                             type is used.
2494  *
2495  * A mixed enumerated type and flags field.  You must specify one type
2496  * (string, strdup, boolean, tristate).  Additionally, you may
2497  * optionally bitwise OR the type with the flag
2498  * %G_MARKUP_COLLECT_OPTIONAL.
2499  *
2500  * It is likely that this enum will be extended in the future to
2501  * support other types.
2502  **/
2503
2504 /**
2505  * g_markup_collect_attributes:
2506  * @element_name: the current tag name
2507  * @attribute_names: the attribute names
2508  * @attribute_values: the attribute values
2509  * @error: a pointer to a #GError or %NULL
2510  * @first_type: the #GMarkupCollectType of the
2511  *              first attribute
2512  * @first_attr: the name of the first attribute
2513  * @...: a pointer to the storage location of the
2514  *       first attribute (or %NULL), followed by
2515  *       more types names and pointers, ending
2516  *       with %G_MARKUP_COLLECT_INVALID.
2517  * 
2518  * Collects the attributes of the element from the
2519  * data passed to the #GMarkupParser start_element
2520  * function, dealing with common error conditions
2521  * and supporting boolean values.
2522  *
2523  * This utility function is not required to write
2524  * a parser but can save a lot of typing.
2525  *
2526  * The @element_name, @attribute_names,
2527  * @attribute_values and @error parameters passed
2528  * to the start_element callback should be passed
2529  * unmodified to this function.
2530  *
2531  * Following these arguments is a list of
2532  * "supported" attributes to collect.  It is an
2533  * error to specify multiple attributes with the
2534  * same name.  If any attribute not in the list
2535  * appears in the @attribute_names array then an
2536  * unknown attribute error will result.
2537  *
2538  * The #GMarkupCollectType field allows specifying
2539  * the type of collection to perform and if a
2540  * given attribute must appear or is optional.
2541  *
2542  * The attribute name is simply the name of the
2543  * attribute to collect.
2544  *
2545  * The pointer should be of the appropriate type
2546  * (see the descriptions under
2547  * #GMarkupCollectType) and may be %NULL in case a
2548  * particular attribute is to be allowed but
2549  * ignored.
2550  *
2551  * This function deals with issuing errors for missing attributes 
2552  * (of type %G_MARKUP_ERROR_MISSING_ATTRIBUTE), unknown attributes 
2553  * (of type %G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE) and duplicate 
2554  * attributes (of type %G_MARKUP_ERROR_INVALID_CONTENT) as well 
2555  * as parse errors for boolean-valued attributes (again of type
2556  * %G_MARKUP_ERROR_INVALID_CONTENT). In all of these cases %FALSE 
2557  * will be returned and @error will be set as appropriate.
2558  *
2559  * Return value: %TRUE if successful
2560  *
2561  * Since: 2.16
2562  **/
2563 gboolean
2564 g_markup_collect_attributes (const gchar         *element_name,
2565                              const gchar        **attribute_names,
2566                              const gchar        **attribute_values,
2567                              GError             **error,
2568                              GMarkupCollectType   first_type,
2569                              const gchar         *first_attr,
2570                              ...)
2571 {
2572   GMarkupCollectType type;
2573   const gchar *attr;
2574   guint64 collected;
2575   int written;
2576   va_list ap;
2577   int i;
2578
2579   type = first_type;
2580   attr = first_attr;
2581   collected = 0;
2582   written = 0;
2583
2584   va_start (ap, first_attr);
2585   while (type != G_MARKUP_COLLECT_INVALID)
2586     {
2587       gboolean mandatory;
2588       const gchar *value;
2589
2590       mandatory = !(type & G_MARKUP_COLLECT_OPTIONAL);
2591       type &= (G_MARKUP_COLLECT_OPTIONAL - 1);
2592
2593       /* tristate records a value != TRUE and != FALSE
2594        * for the case where the attribute is missing
2595        */
2596       if (type == G_MARKUP_COLLECT_TRISTATE)
2597         mandatory = FALSE;
2598
2599       for (i = 0; attribute_names[i]; i++)
2600         if (i >= 40 || !(collected & (G_GUINT64_CONSTANT(1) << i)))
2601           if (!strcmp (attribute_names[i], attr))
2602             break;
2603
2604       /* ISO C99 only promises that the user can pass up to 127 arguments.
2605        * Subtracting the first 4 arguments plus the final NULL and dividing
2606        * by 3 arguments per collected attribute, we are left with a maximum
2607        * number of supported attributes of (127 - 5) / 3 = 40.
2608        *
2609        * In reality, nobody is ever going to call us with anywhere close to
2610        * 40 attributes to collect, so it is safe to assume that if i > 40
2611        * then the user has given some invalid or repeated arguments.  These
2612        * problems will be caught and reported at the end of the function.
2613        *
2614        * We know at this point that we have an error, but we don't know
2615        * what error it is, so just continue...
2616        */
2617       if (i < 40)
2618         collected |= (G_GUINT64_CONSTANT(1) << i);
2619
2620       value = attribute_values[i];
2621
2622       if (value == NULL && mandatory)
2623         {
2624           g_set_error (error, G_MARKUP_ERROR,
2625                        G_MARKUP_ERROR_MISSING_ATTRIBUTE,
2626                        "element '%s' requires attribute '%s'",
2627                        element_name, attr);
2628
2629           va_end (ap);
2630           goto failure;
2631         }
2632
2633       switch (type)
2634         {
2635         case G_MARKUP_COLLECT_STRING:
2636           {
2637             const char **str_ptr;
2638
2639             str_ptr = va_arg (ap, const char **);
2640
2641             if (str_ptr != NULL)
2642               *str_ptr = value;
2643           }
2644           break;
2645
2646         case G_MARKUP_COLLECT_STRDUP:
2647           {
2648             char **str_ptr;
2649
2650             str_ptr = va_arg (ap, char **);
2651
2652             if (str_ptr != NULL)
2653               *str_ptr = g_strdup (value);
2654           }
2655           break;
2656
2657         case G_MARKUP_COLLECT_BOOLEAN:
2658         case G_MARKUP_COLLECT_TRISTATE:
2659           if (value == NULL)
2660             {
2661               gboolean *bool_ptr;
2662
2663               bool_ptr = va_arg (ap, gboolean *);
2664
2665               if (bool_ptr != NULL)
2666                 {
2667                   if (type == G_MARKUP_COLLECT_TRISTATE)
2668                     /* constructivists rejoice!
2669                      * neither false nor true...
2670                      */
2671                     *bool_ptr = -1;
2672
2673                   else /* G_MARKUP_COLLECT_BOOLEAN */
2674                     *bool_ptr = FALSE;
2675                 }
2676             }
2677           else
2678             {
2679               if (!g_markup_parse_boolean (value, va_arg (ap, gboolean *)))
2680                 {
2681                   g_set_error (error, G_MARKUP_ERROR,
2682                                G_MARKUP_ERROR_INVALID_CONTENT,
2683                                "element '%s', attribute '%s', value '%s' "
2684                                "cannot be parsed as a boolean value",
2685                                element_name, attr, value);
2686
2687                   va_end (ap);
2688                   goto failure;
2689                 }
2690             }
2691
2692           break;
2693
2694         default:
2695           g_assert_not_reached ();
2696         }
2697
2698       type = va_arg (ap, GMarkupCollectType);
2699       attr = va_arg (ap, const char *);
2700       written++;
2701     }
2702   va_end (ap);
2703
2704   /* ensure we collected all the arguments */
2705   for (i = 0; attribute_names[i]; i++)
2706     if ((collected & (G_GUINT64_CONSTANT(1) << i)) == 0)
2707       {
2708         /* attribute not collected:  could be caused by two things.
2709          *
2710          * 1) it doesn't exist in our list of attributes
2711          * 2) it existed but was matched by a duplicate attribute earlier
2712          *
2713          * find out.
2714          */
2715         int j;
2716
2717         for (j = 0; j < i; j++)
2718           if (strcmp (attribute_names[i], attribute_names[j]) == 0)
2719             /* duplicate! */
2720             break;
2721
2722         /* j is now the first occurrence of attribute_names[i] */
2723         if (i == j)
2724           g_set_error (error, G_MARKUP_ERROR,
2725                        G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2726                        "attribute '%s' invalid for element '%s'",
2727                        attribute_names[i], element_name);
2728         else
2729           g_set_error (error, G_MARKUP_ERROR,
2730                        G_MARKUP_ERROR_INVALID_CONTENT,
2731                        "attribute '%s' given multiple times for element '%s'",
2732                        attribute_names[i], element_name);
2733
2734         goto failure;
2735       }
2736
2737   return TRUE;
2738
2739 failure:
2740   /* replay the above to free allocations */
2741   type = first_type;
2742   attr = first_attr;
2743
2744   va_start (ap, first_attr);
2745   while (type != G_MARKUP_COLLECT_INVALID)
2746     {
2747       gpointer ptr;
2748
2749       ptr = va_arg (ap, gpointer);
2750
2751       if (ptr == NULL)
2752         continue;
2753
2754       switch (type & (G_MARKUP_COLLECT_OPTIONAL - 1))
2755         {
2756         case G_MARKUP_COLLECT_STRDUP:
2757           if (written)
2758             g_free (*(char **) ptr);
2759
2760         case G_MARKUP_COLLECT_STRING:
2761           *(char **) ptr = NULL;
2762           break;
2763
2764         case G_MARKUP_COLLECT_BOOLEAN:
2765           *(gboolean *) ptr = FALSE;
2766           break;
2767
2768         case G_MARKUP_COLLECT_TRISTATE:
2769           *(gboolean *) ptr = -1;
2770           break;
2771         }
2772
2773       type = va_arg (ap, GMarkupCollectType);
2774       attr = va_arg (ap, const char *);
2775
2776       if (written)
2777         written--;
2778     }
2779   va_end (ap);
2780
2781   return FALSE;
2782 }
2783
2784 #define __G_MARKUP_C__
2785 #include "galiasdef.c"