Define empty for gcc 2.95.
[platform/upstream/glib.git] / glib / gmarkup.c
1 /* gmarkup.c - Simple XML-like parser
2  *
3  *  Copyright 2000, 2003 Red Hat, Inc.
4  *
5  * GLib is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * GLib is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with GLib; see the file COPYING.LIB.  If not,
17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  *   Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28
29 #include "galias.h"
30 #include "glib.h"
31
32 #include "glibintl.h"
33
34 GQuark
35 g_markup_error_quark (void)
36 {
37   static GQuark error_quark = 0;
38
39   if (error_quark == 0)
40     error_quark = g_quark_from_static_string ("g-markup-error-quark");
41
42   return error_quark;
43 }
44
45 typedef enum
46 {
47   STATE_START,
48   STATE_AFTER_OPEN_ANGLE,
49   STATE_AFTER_CLOSE_ANGLE,
50   STATE_AFTER_ELISION_SLASH, /* the slash that obviates need for end element */
51   STATE_INSIDE_OPEN_TAG_NAME,
52   STATE_INSIDE_ATTRIBUTE_NAME,
53   STATE_AFTER_ATTRIBUTE_NAME,
54   STATE_BETWEEN_ATTRIBUTES,
55   STATE_AFTER_ATTRIBUTE_EQUALS_SIGN,
56   STATE_INSIDE_ATTRIBUTE_VALUE_SQ,
57   STATE_INSIDE_ATTRIBUTE_VALUE_DQ,
58   STATE_INSIDE_TEXT,
59   STATE_AFTER_CLOSE_TAG_SLASH,
60   STATE_INSIDE_CLOSE_TAG_NAME,
61   STATE_AFTER_CLOSE_TAG_NAME,
62   STATE_INSIDE_PASSTHROUGH,
63   STATE_ERROR
64 } GMarkupParseState;
65
66 struct _GMarkupParseContext
67 {
68   const GMarkupParser *parser;
69
70   GMarkupParseFlags flags;
71
72   gint line_number;
73   gint char_number;
74
75   gpointer user_data;
76   GDestroyNotify dnotify;
77
78   /* A piece of character data or an element that
79    * hasn't "ended" yet so we haven't yet called
80    * the callback for it.
81    */
82   GString *partial_chunk;
83
84   GMarkupParseState state;
85   GSList *tag_stack;
86   gchar **attr_names;
87   gchar **attr_values;
88   gint cur_attr;
89   gint alloc_attrs;
90
91   const gchar *current_text;
92   gssize       current_text_len;      
93   const gchar *current_text_end;
94
95   GString *leftover_char_portion;
96
97   /* used to save the start of the last interesting thingy */
98   const gchar *start;
99
100   const gchar *iter;
101
102   guint document_empty : 1;
103   guint parsing : 1;
104   gint balance;
105 };
106
107 /**
108  * g_markup_parse_context_new:
109  * @parser: a #GMarkupParser
110  * @flags: one or more #GMarkupParseFlags
111  * @user_data: user data to pass to #GMarkupParser functions
112  * @user_data_dnotify: user data destroy notifier called when the parse context is freed
113  * 
114  * Creates a new parse context. A parse context is used to parse
115  * marked-up documents. You can feed any number of documents into
116  * a context, as long as no errors occur; once an error occurs,
117  * the parse context can't continue to parse text (you have to free it
118  * and create a new parse context).
119  * 
120  * Return value: a new #GMarkupParseContext
121  **/
122 GMarkupParseContext *
123 g_markup_parse_context_new (const GMarkupParser *parser,
124                             GMarkupParseFlags    flags,
125                             gpointer             user_data,
126                             GDestroyNotify       user_data_dnotify)
127 {
128   GMarkupParseContext *context;
129
130   g_return_val_if_fail (parser != NULL, NULL);
131
132   context = g_new (GMarkupParseContext, 1);
133
134   context->parser = parser;
135   context->flags = flags;
136   context->user_data = user_data;
137   context->dnotify = user_data_dnotify;
138
139   context->line_number = 1;
140   context->char_number = 1;
141
142   context->partial_chunk = NULL;
143
144   context->state = STATE_START;
145   context->tag_stack = NULL;
146   context->attr_names = NULL;
147   context->attr_values = NULL;
148   context->cur_attr = -1;
149   context->alloc_attrs = 0;
150
151   context->current_text = NULL;
152   context->current_text_len = -1;
153   context->current_text_end = NULL;
154   context->leftover_char_portion = NULL;
155
156   context->start = NULL;
157   context->iter = NULL;
158
159   context->document_empty = TRUE;
160   context->parsing = FALSE;
161
162   context->balance = 0;
163
164   return context;
165 }
166
167 /**
168  * g_markup_parse_context_free:
169  * @context: a #GMarkupParseContext
170  * 
171  * Frees a #GMarkupParseContext. Can't be called from inside
172  * one of the #GMarkupParser functions.
173  * 
174  **/
175 void
176 g_markup_parse_context_free (GMarkupParseContext *context)
177 {
178   g_return_if_fail (context != NULL);
179   g_return_if_fail (!context->parsing);
180
181   if (context->dnotify)
182     (* context->dnotify) (context->user_data);
183
184   g_strfreev (context->attr_names);
185   g_strfreev (context->attr_values);
186
187   g_slist_foreach (context->tag_stack, (GFunc)g_free, NULL);
188   g_slist_free (context->tag_stack);
189
190   if (context->partial_chunk)
191     g_string_free (context->partial_chunk, TRUE);
192
193   if (context->leftover_char_portion)
194     g_string_free (context->leftover_char_portion, TRUE);
195
196   g_free (context);
197 }
198
199 static void
200 mark_error (GMarkupParseContext *context,
201             GError              *error)
202 {
203   context->state = STATE_ERROR;
204
205   if (context->parser->error)
206     (*context->parser->error) (context, error, context->user_data);
207 }
208
209 static void
210 set_error (GMarkupParseContext *context,
211            GError             **error,
212            GMarkupError         code,
213            const gchar         *format,
214            ...)
215 {
216   GError *tmp_error;
217   gchar *s;
218   va_list args;
219
220   va_start (args, format);
221   s = g_strdup_vprintf (format, args);
222   va_end (args);
223
224   tmp_error = g_error_new (G_MARKUP_ERROR,
225                            code,
226                            _("Error on line %d char %d: %s"),
227                            context->line_number,
228                            context->char_number,
229                            s);
230
231   g_free (s);
232
233   mark_error (context, tmp_error);
234
235   g_propagate_error (error, tmp_error);
236 }
237
238
239 /* To make these faster, we first use the ascii-only tests, then check
240  * for the usual non-alnum name-end chars, and only then call the
241  * expensive unicode stuff. Nobody uses non-ascii in XML tag/attribute
242  * names, so this is a reasonable hack that virtually always avoids
243  * the guniprop call.
244  */
245 #define IS_COMMON_NAME_END_CHAR(c) \
246   ((c) == '=' || (c) == '/' || (c) == '>' || (c) == ' ')
247
248 static gboolean
249 is_name_start_char (const gchar *p)
250 {
251   if (g_ascii_isalpha (*p) ||
252       (!IS_COMMON_NAME_END_CHAR (*p) &&
253        (*p == '_' || 
254         *p == ':' ||
255         g_unichar_isalpha (g_utf8_get_char (p)))))
256     return TRUE;
257   else
258     return FALSE;
259 }
260
261 static gboolean
262 is_name_char (const gchar *p)
263 {
264   if (g_ascii_isalnum (*p) ||
265       (!IS_COMMON_NAME_END_CHAR (*p) &&
266        (*p == '.' || 
267         *p == '-' ||
268         *p == '_' ||
269         *p == ':' ||
270         g_unichar_isalpha (g_utf8_get_char (p)))))
271     return TRUE;
272   else
273     return FALSE;
274 }
275
276
277 static gchar*
278 char_str (gunichar c,
279           gchar   *buf)
280 {
281   memset (buf, 0, 8);
282   g_unichar_to_utf8 (c, buf);
283   return buf;
284 }
285
286 static gchar*
287 utf8_str (const gchar *utf8,
288           gchar       *buf)
289 {
290   char_str (g_utf8_get_char (utf8), buf);
291   return buf;
292 }
293
294 static void
295 set_unescape_error (GMarkupParseContext *context,
296                     GError             **error,
297                     const gchar         *remaining_text,
298                     const gchar         *remaining_text_end,
299                     GMarkupError         code,
300                     const gchar         *format,
301                     ...)
302 {
303   GError *tmp_error;
304   gchar *s;
305   va_list args;
306   gint remaining_newlines;
307   const gchar *p;
308
309   remaining_newlines = 0;
310   p = remaining_text;
311   while (p != remaining_text_end)
312     {
313       if (*p == '\n')
314         ++remaining_newlines;
315       ++p;
316     }
317
318   va_start (args, format);
319   s = g_strdup_vprintf (format, args);
320   va_end (args);
321
322   tmp_error = g_error_new (G_MARKUP_ERROR,
323                            code,
324                            _("Error on line %d: %s"),
325                            context->line_number - remaining_newlines,
326                            s);
327
328   g_free (s);
329
330   mark_error (context, tmp_error);
331
332   g_propagate_error (error, tmp_error);
333 }
334
335 typedef enum
336 {
337   USTATE_INSIDE_TEXT,
338   USTATE_AFTER_AMPERSAND,
339   USTATE_INSIDE_ENTITY_NAME,
340   USTATE_AFTER_CHARREF_HASH
341 } UnescapeState;
342
343 typedef struct
344 {
345   GMarkupParseContext *context;
346   GString *str;
347   UnescapeState state;
348   const gchar *text;
349   const gchar *text_end;
350   const gchar *entity_start;
351 } UnescapeContext;
352
353 static const gchar*
354 unescape_text_state_inside_text (UnescapeContext *ucontext,
355                                  const gchar     *p,
356                                  GError         **error)
357 {
358   const gchar *start;
359   gboolean normalize_attribute;
360
361   if (ucontext->context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ ||
362       ucontext->context->state == STATE_INSIDE_ATTRIBUTE_VALUE_DQ)
363     normalize_attribute = TRUE;
364   else
365     normalize_attribute = FALSE;
366
367   start = p;
368   
369   while (p != ucontext->text_end)
370     {
371       if (*p == '&')
372         {
373           break;
374         }
375       else if (normalize_attribute && (*p == '\t' || *p == '\n'))
376         {
377           g_string_append_len (ucontext->str, start, p - start);
378           g_string_append_c (ucontext->str, ' ');
379           p = g_utf8_next_char (p);
380           start = p;
381         }
382       else if (*p == '\r')
383         {
384           g_string_append_len (ucontext->str, start, p - start);
385           g_string_append_c (ucontext->str, normalize_attribute ? ' ' : '\n');
386           p = g_utf8_next_char (p);
387           if (p != ucontext->text_end && *p == '\n')
388             p = g_utf8_next_char (p);
389           start = p;
390         }
391       else
392         p = g_utf8_next_char (p);
393     }
394   
395   if (p != start)
396     g_string_append_len (ucontext->str, start, p - start);
397   
398   if (p != ucontext->text_end && *p == '&')
399     {
400       p = g_utf8_next_char (p);
401       ucontext->state = USTATE_AFTER_AMPERSAND;
402     }
403
404   return p;
405 }
406
407 static const gchar*
408 unescape_text_state_after_ampersand (UnescapeContext *ucontext,
409                                      const gchar     *p,
410                                      GError         **error)
411 {
412   ucontext->entity_start = NULL;
413   
414   if (*p == '#')
415     {
416       p = g_utf8_next_char (p);
417
418       ucontext->entity_start = p;
419       ucontext->state = USTATE_AFTER_CHARREF_HASH;
420     }
421   else if (!is_name_start_char (p))
422     {
423       if (*p == ';')
424         {
425           set_unescape_error (ucontext->context, error,
426                               p, ucontext->text_end,
427                               G_MARKUP_ERROR_PARSE,
428                               _("Empty entity '&;' seen; valid "
429                                 "entities are: &amp; &quot; &lt; &gt; &apos;"));
430         }
431       else
432         {
433           gchar buf[8];
434
435           set_unescape_error (ucontext->context, error,
436                               p, ucontext->text_end,
437                               G_MARKUP_ERROR_PARSE,
438                               _("Character '%s' is not valid at "
439                                 "the start of an entity name; "
440                                 "the & character begins an entity; "
441                                 "if this ampersand isn't supposed "
442                                 "to be an entity, escape it as "
443                                 "&amp;"),
444                               utf8_str (p, buf));
445         }
446     }
447   else
448     {
449       ucontext->entity_start = p;
450       ucontext->state = USTATE_INSIDE_ENTITY_NAME;
451     }
452
453   return p;
454 }
455
456 static const gchar*
457 unescape_text_state_inside_entity_name (UnescapeContext *ucontext,
458                                         const gchar     *p,
459                                         GError         **error)
460 {
461 #define MAX_ENT_LEN 5
462   gchar buf[MAX_ENT_LEN+1] = {
463     '\0', '\0', '\0', '\0', '\0', '\0'
464   };
465   gchar *dest;
466
467   while (p != ucontext->text_end)
468     {
469       if (*p == ';')
470         break;
471       else if (!is_name_char (p))
472         {
473           gchar ubuf[8];
474
475           set_unescape_error (ucontext->context, error,
476                               p, ucontext->text_end,
477                               G_MARKUP_ERROR_PARSE,
478                               _("Character '%s' is not valid "
479                                 "inside an entity name"),
480                               utf8_str (p, ubuf));
481           break;
482         }
483
484       p = g_utf8_next_char (p);
485     }
486
487   if (ucontext->context->state != STATE_ERROR)
488     {
489       if (p != ucontext->text_end)
490         {
491           const gchar *src;
492                 
493           src = ucontext->entity_start;
494           dest = buf;
495           while (src != p)
496             {
497               *dest = *src;
498               ++dest;
499               ++src;
500             }
501
502           /* move to after semicolon */
503           p = g_utf8_next_char (p);
504           ucontext->state = USTATE_INSIDE_TEXT;
505
506           if (strcmp (buf, "lt") == 0)
507             g_string_append_c (ucontext->str, '<');
508           else if (strcmp (buf, "gt") == 0)
509             g_string_append_c (ucontext->str, '>');
510           else if (strcmp (buf, "amp") == 0)
511             g_string_append_c (ucontext->str, '&');
512           else if (strcmp (buf, "quot") == 0)
513             g_string_append_c (ucontext->str, '"');
514           else if (strcmp (buf, "apos") == 0)
515             g_string_append_c (ucontext->str, '\'');
516           else
517             {
518               set_unescape_error (ucontext->context, error,
519                                   p, ucontext->text_end,
520                                   G_MARKUP_ERROR_PARSE,
521                                   _("Entity name '%s' is not known"),
522                                   buf);
523             }
524         }
525       else
526         {
527           set_unescape_error (ucontext->context, error,
528                               /* give line number of the & */
529                               ucontext->entity_start, ucontext->text_end,
530                               G_MARKUP_ERROR_PARSE,
531                               _("Entity did not end with a semicolon; "
532                                 "most likely you used an ampersand "
533                                 "character without intending to start "
534                                 "an entity - escape ampersand as &amp;"));
535         }
536     }
537 #undef MAX_ENT_LEN
538
539   return p;
540 }
541
542 static const gchar*
543 unescape_text_state_after_charref_hash (UnescapeContext *ucontext,
544                                         const gchar     *p,
545                                         GError         **error)
546 {
547   gboolean is_hex = FALSE;
548   const char *start;
549
550   start = ucontext->entity_start;
551
552   if (*p == 'x')
553     {
554       is_hex = TRUE;
555       p = g_utf8_next_char (p);
556       start = p;
557     }
558
559   while (p != ucontext->text_end && *p != ';')
560     p = g_utf8_next_char (p);
561
562   if (p != ucontext->text_end)
563     {
564       g_assert (*p == ';');
565
566       /* digit is between start and p */
567
568       if (start != p)
569         {
570           gulong l;
571           gchar *end = NULL;
572                     
573           errno = 0;
574           if (is_hex)
575             l = strtoul (start, &end, 16);
576           else
577             l = strtoul (start, &end, 10);
578
579           if (end != p || errno != 0)
580             {
581               set_unescape_error (ucontext->context, error,
582                                   start, ucontext->text_end,
583                                   G_MARKUP_ERROR_PARSE,
584                                   _("Failed to parse '%-.*s', which "
585                                     "should have been a digit "
586                                     "inside a character reference "
587                                     "(&#234; for example) - perhaps "
588                                     "the digit is too large"),
589                                   p - start, start);
590             }
591           else
592             {
593               /* characters XML permits */
594               if (l == 0x9 ||
595                   l == 0xA ||
596                   l == 0xD ||
597                   (l >= 0x20 && l <= 0xD7FF) ||
598                   (l >= 0xE000 && l <= 0xFFFD) ||
599                   (l >= 0x10000 && l <= 0x10FFFF))
600                 {
601                   gchar buf[8];
602                   g_string_append (ucontext->str, char_str (l, buf));
603                 }
604               else
605                 {
606                   set_unescape_error (ucontext->context, error,
607                                       start, ucontext->text_end,
608                                       G_MARKUP_ERROR_PARSE,
609                                       _("Character reference '%-.*s' does not "
610                                         "encode a permitted character"),
611                                       p - start, start);
612                 }
613             }
614
615           /* Move to next state */
616           p = g_utf8_next_char (p); /* past semicolon */
617           ucontext->state = USTATE_INSIDE_TEXT;
618         }
619       else
620         {
621           set_unescape_error (ucontext->context, error,
622                               start, ucontext->text_end,
623                               G_MARKUP_ERROR_PARSE,
624                               _("Empty character reference; "
625                                 "should include a digit such as "
626                                 "&#454;"));
627         }
628     }
629   else
630     {
631       set_unescape_error (ucontext->context, error,
632                           start, ucontext->text_end,
633                           G_MARKUP_ERROR_PARSE,
634                           _("Character reference did not end with a "
635                             "semicolon; "
636                             "most likely you used an ampersand "
637                             "character without intending to start "
638                             "an entity - escape ampersand as &amp;"));
639     }
640
641   return p;
642 }
643
644 static gboolean
645 unescape_text (GMarkupParseContext *context,
646                const gchar         *text,
647                const gchar         *text_end,
648                GString            **unescaped,
649                GError             **error)
650 {
651   UnescapeContext ucontext;
652   const gchar *p;
653
654   ucontext.context = context;
655   ucontext.text = text;
656   ucontext.text_end = text_end;
657   ucontext.entity_start = NULL;
658   
659   ucontext.str = g_string_sized_new (text_end - text);
660
661   ucontext.state = USTATE_INSIDE_TEXT;
662   p = text;
663
664   while (p != text_end && context->state != STATE_ERROR)
665     {
666       g_assert (p < text_end);
667       
668       switch (ucontext.state)
669         {
670         case USTATE_INSIDE_TEXT:
671           {
672             p = unescape_text_state_inside_text (&ucontext,
673                                                  p,
674                                                  error);
675           }
676           break;
677
678         case USTATE_AFTER_AMPERSAND:
679           {
680             p = unescape_text_state_after_ampersand (&ucontext,
681                                                      p,
682                                                      error);
683           }
684           break;
685
686
687         case USTATE_INSIDE_ENTITY_NAME:
688           {
689             p = unescape_text_state_inside_entity_name (&ucontext,
690                                                         p,
691                                                         error);
692           }
693           break;
694
695         case USTATE_AFTER_CHARREF_HASH:
696           {
697             p = unescape_text_state_after_charref_hash (&ucontext,
698                                                         p,
699                                                         error);
700           }
701           break;
702
703         default:
704           g_assert_not_reached ();
705           break;
706         }
707     }
708
709   if (context->state != STATE_ERROR) 
710     {
711       switch (ucontext.state) 
712         {
713         case USTATE_INSIDE_TEXT:
714           break;
715         case USTATE_AFTER_AMPERSAND:
716         case USTATE_INSIDE_ENTITY_NAME:
717           set_unescape_error (context, error,
718                               NULL, NULL,
719                               G_MARKUP_ERROR_PARSE,
720                               _("Unfinished entity reference"));
721           break;
722         case USTATE_AFTER_CHARREF_HASH:
723           set_unescape_error (context, error,
724                               NULL, NULL,
725                               G_MARKUP_ERROR_PARSE,
726                               _("Unfinished character reference"));
727           break;
728         }
729     }
730
731   if (context->state == STATE_ERROR)
732     {
733       g_string_free (ucontext.str, TRUE);
734       *unescaped = NULL;
735       return FALSE;
736     }
737   else
738     {
739       *unescaped = ucontext.str;
740       return TRUE;
741     }
742 }
743
744 static inline gboolean
745 advance_char (GMarkupParseContext *context)
746 {  
747   context->iter = g_utf8_next_char (context->iter);
748   context->char_number += 1;
749
750   if (context->iter == context->current_text_end)
751     {
752       return FALSE;
753     }
754   else if (*context->iter == '\n')
755     {
756       context->line_number += 1;
757       context->char_number = 1;
758     }
759   
760   return TRUE;
761 }
762
763 static inline gboolean
764 xml_isspace (char c)
765 {
766   return c == ' ' || c == '\t' || c == '\n' || c == '\r';
767 }
768
769 static void
770 skip_spaces (GMarkupParseContext *context)
771 {
772   do
773     {
774       if (!xml_isspace (*context->iter))
775         return;
776     }
777   while (advance_char (context));
778 }
779
780 static void
781 advance_to_name_end (GMarkupParseContext *context)
782 {
783   do
784     {
785       if (!is_name_char (context->iter))
786         return;
787     }
788   while (advance_char (context));
789 }
790
791 static void
792 add_to_partial (GMarkupParseContext *context,
793                 const gchar         *text_start,
794                 const gchar         *text_end)
795 {
796   if (context->partial_chunk == NULL)
797     context->partial_chunk = g_string_sized_new (text_end - text_start);
798
799   if (text_start != text_end)
800     g_string_append_len (context->partial_chunk, text_start,
801                          text_end - text_start);
802
803   /* Invariant here that partial_chunk exists */
804 }
805
806 static void
807 truncate_partial (GMarkupParseContext *context)
808 {
809   if (context->partial_chunk != NULL)
810     {
811       context->partial_chunk = g_string_truncate (context->partial_chunk, 0);
812     }
813 }
814
815 static const gchar*
816 current_element (GMarkupParseContext *context)
817 {
818   return context->tag_stack->data;
819 }
820
821 static const gchar*
822 current_attribute (GMarkupParseContext *context)
823 {
824   g_assert (context->cur_attr >= 0);
825   return context->attr_names[context->cur_attr];
826 }
827
828 static void
829 find_current_text_end (GMarkupParseContext *context)
830 {
831   /* This function must be safe (non-segfaulting) on invalid UTF8.
832    * It assumes the string starts with a character start
833    */
834   const gchar *end = context->current_text + context->current_text_len;
835   const gchar *p;
836   const gchar *next;
837
838   g_assert (context->current_text_len > 0);
839
840   p = g_utf8_find_prev_char (context->current_text, end);
841
842   g_assert (p != NULL); /* since current_text was a char start */
843
844   /* p is now the start of the last character or character portion. */
845   g_assert (p != end);
846   next = g_utf8_next_char (p); /* this only touches *p, nothing beyond */
847
848   if (next == end)
849     {
850       /* whole character */
851       context->current_text_end = end;
852     }
853   else
854     {
855       /* portion */
856       context->leftover_char_portion = g_string_new_len (p, end - p);
857       context->current_text_len -= (end - p);
858       context->current_text_end = p;
859     }
860 }
861
862
863 static void
864 add_attribute (GMarkupParseContext *context, char *name)
865 {
866   if (context->cur_attr + 2 >= context->alloc_attrs)
867     {
868       context->alloc_attrs += 5; /* silly magic number */
869       context->attr_names = g_realloc (context->attr_names, sizeof(char*)*context->alloc_attrs);
870       context->attr_values = g_realloc (context->attr_values, sizeof(char*)*context->alloc_attrs);
871     }
872   context->cur_attr++;
873   context->attr_names[context->cur_attr] = name;
874   context->attr_values[context->cur_attr] = NULL;
875   context->attr_names[context->cur_attr+1] = NULL;
876   context->attr_values[context->cur_attr+1] = NULL;
877 }
878
879 /**
880  * g_markup_parse_context_parse:
881  * @context: a #GMarkupParseContext
882  * @text: chunk of text to parse
883  * @text_len: length of @text in bytes
884  * @error: return location for a #GError
885  * 
886  * Feed some data to the #GMarkupParseContext. The data need not
887  * be valid UTF-8; an error will be signaled if it's invalid.
888  * The data need not be an entire document; you can feed a document
889  * into the parser incrementally, via multiple calls to this function.
890  * Typically, as you receive data from a network connection or file,
891  * you feed each received chunk of data into this function, aborting
892  * the process if an error occurs. Once an error is reported, no further
893  * data may be fed to the #GMarkupParseContext; all errors are fatal.
894  * 
895  * Return value: %FALSE if an error occurred, %TRUE on success
896  **/
897 gboolean
898 g_markup_parse_context_parse (GMarkupParseContext *context,
899                               const gchar         *text,
900                               gssize               text_len,
901                               GError             **error)
902 {
903   const gchar *first_invalid;
904   
905   g_return_val_if_fail (context != NULL, FALSE);
906   g_return_val_if_fail (text != NULL, FALSE);
907   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
908   g_return_val_if_fail (!context->parsing, FALSE);
909   
910   if (text_len < 0)
911     text_len = strlen (text);
912
913   if (text_len == 0)
914     return TRUE;
915   
916   context->parsing = TRUE;
917   
918   if (context->leftover_char_portion)
919     {
920       const gchar *first_char;
921
922       if ((*text & 0xc0) != 0x80)
923         first_char = text;
924       else
925         first_char = g_utf8_find_next_char (text, text + text_len);
926
927       if (first_char)
928         {
929           /* leftover_char_portion was completed. Parse it. */
930           GString *portion = context->leftover_char_portion;
931           
932           g_string_append_len (context->leftover_char_portion,
933                                text, first_char - text);
934
935           /* hacks to allow recursion */
936           context->parsing = FALSE;
937           context->leftover_char_portion = NULL;
938           
939           if (!g_markup_parse_context_parse (context,
940                                              portion->str, portion->len,
941                                              error))
942             {
943               g_assert (context->state == STATE_ERROR);
944             }
945           
946           g_string_free (portion, TRUE);
947           context->parsing = TRUE;
948
949           /* Skip the fraction of char that was in this text */
950           text_len -= (first_char - text);
951           text = first_char;
952         }
953       else
954         {
955           /* another little chunk of the leftover char; geez
956            * someone is inefficient.
957            */
958           g_string_append_len (context->leftover_char_portion,
959                                text, text_len);
960
961           if (context->leftover_char_portion->len > 7)
962             {
963               /* The leftover char portion is too big to be
964                * a UTF-8 character
965                */
966               set_error (context,
967                          error,
968                          G_MARKUP_ERROR_BAD_UTF8,
969                          _("Invalid UTF-8 encoded text"));
970             }
971           
972           goto finished;
973         }
974     }
975
976   context->current_text = text;
977   context->current_text_len = text_len;
978   context->iter = context->current_text;
979   context->start = context->iter;
980
981   /* Nothing left after finishing the leftover char, or nothing
982    * passed in to begin with.
983    */
984   if (context->current_text_len == 0)
985     goto finished;
986
987   /* find_current_text_end () assumes the string starts at
988    * a character start, so we need to validate at least
989    * that much. It doesn't assume any following bytes
990    * are valid.
991    */
992   if ((*context->current_text & 0xc0) == 0x80) /* not a char start */
993     {
994       set_error (context,
995                  error,
996                  G_MARKUP_ERROR_BAD_UTF8,
997                  _("Invalid UTF-8 encoded text"));
998       goto finished;
999     }
1000
1001   /* Initialize context->current_text_end, possibly adjusting
1002    * current_text_len, and add any leftover char portion
1003    */
1004   find_current_text_end (context);
1005
1006   /* Validate UTF8 (must be done after we find the end, since
1007    * we could have a trailing incomplete char)
1008    */
1009   if (!g_utf8_validate (context->current_text,
1010                         context->current_text_len,
1011                         &first_invalid))
1012     {
1013       gint newlines = 0;
1014       const gchar *p;
1015       p = context->current_text;
1016       while (p != context->current_text_end)
1017         {
1018           if (*p == '\n')
1019             ++newlines;
1020           ++p;
1021         }
1022
1023       context->line_number += newlines;
1024
1025       set_error (context,
1026                  error,
1027                  G_MARKUP_ERROR_BAD_UTF8,
1028                  _("Invalid UTF-8 encoded text"));
1029       goto finished;
1030     }
1031
1032   while (context->iter != context->current_text_end)
1033     {
1034       switch (context->state)
1035         {
1036         case STATE_START:
1037           /* Possible next state: AFTER_OPEN_ANGLE */
1038
1039           g_assert (context->tag_stack == NULL);
1040
1041           /* whitespace is ignored outside of any elements */
1042           skip_spaces (context);
1043
1044           if (context->iter != context->current_text_end)
1045             {
1046               if (*context->iter == '<')
1047                 {
1048                   /* Move after the open angle */
1049                   advance_char (context);
1050
1051                   context->state = STATE_AFTER_OPEN_ANGLE;
1052
1053                   /* this could start a passthrough */
1054                   context->start = context->iter;
1055
1056                   /* document is now non-empty */
1057                   context->document_empty = FALSE;
1058                 }
1059               else
1060                 {
1061                   set_error (context,
1062                              error,
1063                              G_MARKUP_ERROR_PARSE,
1064                              _("Document must begin with an element (e.g. <book>)"));
1065                 }
1066             }
1067           break;
1068
1069         case STATE_AFTER_OPEN_ANGLE:
1070           /* Possible next states: INSIDE_OPEN_TAG_NAME,
1071            *  AFTER_CLOSE_TAG_SLASH, INSIDE_PASSTHROUGH
1072            */
1073           if (*context->iter == '?' ||
1074               *context->iter == '!')
1075             {
1076               /* include < in the passthrough */
1077               const gchar *openangle = "<";
1078               add_to_partial (context, openangle, openangle + 1);
1079               context->start = context->iter;
1080               context->balance = 1;
1081               context->state = STATE_INSIDE_PASSTHROUGH;
1082             }
1083           else if (*context->iter == '/')
1084             {
1085               /* move after it */
1086               advance_char (context);
1087
1088               context->state = STATE_AFTER_CLOSE_TAG_SLASH;
1089             }
1090           else if (is_name_start_char (context->iter))
1091             {
1092               context->state = STATE_INSIDE_OPEN_TAG_NAME;
1093
1094               /* start of tag name */
1095               context->start = context->iter;
1096             }
1097           else
1098             {
1099               gchar buf[8];
1100
1101               set_error (context,
1102                          error,
1103                          G_MARKUP_ERROR_PARSE,
1104                          _("'%s' is not a valid character following "
1105                            "a '<' character; it may not begin an "
1106                            "element name"),
1107                          utf8_str (context->iter, buf));
1108             }
1109           break;
1110
1111           /* The AFTER_CLOSE_ANGLE state is actually sort of
1112            * broken, because it doesn't correspond to a range
1113            * of characters in the input stream as the others do,
1114            * and thus makes things harder to conceptualize
1115            */
1116         case STATE_AFTER_CLOSE_ANGLE:
1117           /* Possible next states: INSIDE_TEXT, STATE_START */
1118           if (context->tag_stack == NULL)
1119             {
1120               context->start = NULL;
1121               context->state = STATE_START;
1122             }
1123           else
1124             {
1125               context->start = context->iter;
1126               context->state = STATE_INSIDE_TEXT;
1127             }
1128           break;
1129
1130         case STATE_AFTER_ELISION_SLASH:
1131           /* Possible next state: AFTER_CLOSE_ANGLE */
1132
1133           {
1134             /* We need to pop the tag stack and call the end_element
1135              * function, since this is the close tag
1136              */
1137             GError *tmp_error = NULL;
1138           
1139             g_assert (context->tag_stack != NULL);
1140
1141             tmp_error = NULL;
1142             if (context->parser->end_element)
1143               (* context->parser->end_element) (context,
1144                                                 context->tag_stack->data,
1145                                                 context->user_data,
1146                                                 &tmp_error);
1147           
1148             if (tmp_error)
1149               {
1150                 mark_error (context, tmp_error);
1151                 g_propagate_error (error, tmp_error);
1152               }          
1153             else
1154               {
1155                 if (*context->iter == '>')
1156                   {
1157                     /* move after the close angle */
1158                     advance_char (context);
1159                     context->state = STATE_AFTER_CLOSE_ANGLE;
1160                   }
1161                 else
1162                   {
1163                     gchar buf[8];
1164
1165                     set_error (context,
1166                                error,
1167                                G_MARKUP_ERROR_PARSE,
1168                                _("Odd character '%s', expected a '>' character "
1169                                  "to end the start tag of element '%s'"),
1170                                utf8_str (context->iter, buf),
1171                                current_element (context));
1172                   }
1173               }
1174
1175             g_free (context->tag_stack->data);
1176             context->tag_stack = g_slist_delete_link (context->tag_stack,
1177                                                       context->tag_stack);
1178           }
1179           break;
1180
1181         case STATE_INSIDE_OPEN_TAG_NAME:
1182           /* Possible next states: BETWEEN_ATTRIBUTES */
1183
1184           /* if there's a partial chunk then it's the first part of the
1185            * tag name. If there's a context->start then it's the start
1186            * of the tag name in current_text, the partial chunk goes
1187            * before that start though.
1188            */
1189           advance_to_name_end (context);
1190
1191           if (context->iter == context->current_text_end)
1192             {
1193               /* The name hasn't necessarily ended. Merge with
1194                * partial chunk, leave state unchanged.
1195                */
1196               add_to_partial (context, context->start, context->iter);
1197             }
1198           else
1199             {
1200               /* The name has ended. Combine it with the partial chunk
1201                * if any; push it on the stack; enter next state.
1202                */
1203               add_to_partial (context, context->start, context->iter);
1204               context->tag_stack =
1205                 g_slist_prepend (context->tag_stack,
1206                                  g_string_free (context->partial_chunk,
1207                                                 FALSE));
1208
1209               context->partial_chunk = NULL;
1210
1211               context->state = STATE_BETWEEN_ATTRIBUTES;
1212               context->start = NULL;
1213             }
1214           break;
1215
1216         case STATE_INSIDE_ATTRIBUTE_NAME:
1217           /* Possible next states: AFTER_ATTRIBUTE_NAME */
1218
1219           advance_to_name_end (context);
1220           add_to_partial (context, context->start, context->iter);
1221
1222           /* read the full name, if we enter the equals sign state
1223            * then add the attribute to the list (without the value),
1224            * otherwise store a partial chunk to be prepended later.
1225            */
1226           if (context->iter != context->current_text_end)
1227             context->state = STATE_AFTER_ATTRIBUTE_NAME;
1228           break;
1229
1230         case STATE_AFTER_ATTRIBUTE_NAME:
1231           /* Possible next states: AFTER_ATTRIBUTE_EQUALS_SIGN */
1232
1233           skip_spaces (context);
1234
1235           if (context->iter != context->current_text_end)
1236             {
1237               /* The name has ended. Combine it with the partial chunk
1238                * if any; push it on the stack; enter next state.
1239                */
1240               add_attribute (context, g_string_free (context->partial_chunk, FALSE));
1241               
1242               context->partial_chunk = NULL;
1243               context->start = NULL;
1244               
1245               if (*context->iter == '=')
1246                 {
1247                   advance_char (context);
1248                   context->state = STATE_AFTER_ATTRIBUTE_EQUALS_SIGN;
1249                 }
1250               else
1251                 {
1252                   gchar buf[8];
1253
1254                   set_error (context,
1255                              error,
1256                              G_MARKUP_ERROR_PARSE,
1257                              _("Odd character '%s', expected a '=' after "
1258                                "attribute name '%s' of element '%s'"),
1259                              utf8_str (context->iter, buf),
1260                              current_attribute (context),
1261                              current_element (context));
1262                   
1263                 }
1264             }
1265           break;
1266
1267         case STATE_BETWEEN_ATTRIBUTES:
1268           /* Possible next states: AFTER_CLOSE_ANGLE,
1269            * AFTER_ELISION_SLASH, INSIDE_ATTRIBUTE_NAME
1270            */
1271           skip_spaces (context);
1272
1273           if (context->iter != context->current_text_end)
1274             {
1275               if (*context->iter == '/')
1276                 {
1277                   advance_char (context);
1278                   context->state = STATE_AFTER_ELISION_SLASH;
1279                 }
1280               else if (*context->iter == '>')
1281                 {
1282
1283                   advance_char (context);
1284                   context->state = STATE_AFTER_CLOSE_ANGLE;
1285                 }
1286               else if (is_name_start_char (context->iter))
1287                 {
1288                   context->state = STATE_INSIDE_ATTRIBUTE_NAME;
1289                   /* start of attribute name */
1290                   context->start = context->iter;
1291                 }
1292               else
1293                 {
1294                   gchar buf[8];
1295
1296                   set_error (context,
1297                              error,
1298                              G_MARKUP_ERROR_PARSE,
1299                              _("Odd character '%s', expected a '>' or '/' "
1300                                "character to end the start tag of "
1301                                "element '%s', or optionally an attribute; "
1302                                "perhaps you used an invalid character in "
1303                                "an attribute name"),
1304                              utf8_str (context->iter, buf),
1305                              current_element (context));
1306                 }
1307
1308               /* If we're done with attributes, invoke
1309                * the start_element callback
1310                */
1311               if (context->state == STATE_AFTER_ELISION_SLASH ||
1312                   context->state == STATE_AFTER_CLOSE_ANGLE)
1313                 {
1314                   const gchar *start_name;
1315                   /* Ugly, but the current code expects an empty array instead of NULL */
1316                   const gchar *empty = NULL;
1317                   const gchar **attr_names =  &empty;
1318                   const gchar **attr_values = &empty;
1319                   GError *tmp_error;
1320
1321                   /* Call user callback for element start */
1322                   start_name = current_element (context);
1323
1324                   if (context->cur_attr >= 0)
1325                     {
1326                       attr_names = (const gchar**)context->attr_names;
1327                       attr_values = (const gchar**)context->attr_values;
1328                     }
1329
1330                   tmp_error = NULL;
1331                   if (context->parser->start_element)
1332                     (* context->parser->start_element) (context,
1333                                                         start_name,
1334                                                         (const gchar **)attr_names,
1335                                                         (const gchar **)attr_values,
1336                                                         context->user_data,
1337                                                         &tmp_error);
1338
1339                   /* Go ahead and free the attributes. */
1340                   for (; context->cur_attr >= 0; context->cur_attr--)
1341                     {
1342                       int pos = context->cur_attr;
1343                       g_free (context->attr_names[pos]);
1344                       g_free (context->attr_values[pos]);
1345                       context->attr_names[pos] = context->attr_values[pos] = NULL;
1346                     }
1347                   g_assert (context->cur_attr == -1);
1348                   g_assert (context->attr_names == NULL ||
1349                             context->attr_names[0] == NULL);
1350                   g_assert (context->attr_values == NULL ||
1351                             context->attr_values[0] == NULL);
1352                   
1353                   if (tmp_error != NULL)
1354                     {
1355                       mark_error (context, tmp_error);
1356                       g_propagate_error (error, tmp_error);
1357                     }
1358                 }
1359             }
1360           break;
1361
1362         case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1363           /* Possible next state: INSIDE_ATTRIBUTE_VALUE_[SQ/DQ] */
1364
1365           skip_spaces (context);
1366
1367           if (context->iter != context->current_text_end)
1368             {
1369               if (*context->iter == '"')
1370                 {
1371                   advance_char (context);
1372                   context->state = STATE_INSIDE_ATTRIBUTE_VALUE_DQ;
1373                   context->start = context->iter;
1374                 }
1375               else if (*context->iter == '\'')
1376                 {
1377                   advance_char (context);
1378                   context->state = STATE_INSIDE_ATTRIBUTE_VALUE_SQ;
1379                   context->start = context->iter;
1380                 }
1381               else
1382                 {
1383                   gchar buf[8];
1384                   
1385                   set_error (context,
1386                              error,
1387                              G_MARKUP_ERROR_PARSE,
1388                              _("Odd character '%s', expected an open quote mark "
1389                                "after the equals sign when giving value for "
1390                                "attribute '%s' of element '%s'"),
1391                              utf8_str (context->iter, buf),
1392                              current_attribute (context),
1393                              current_element (context));
1394                 }
1395             }
1396           break;
1397
1398         case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1399         case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1400           /* Possible next states: BETWEEN_ATTRIBUTES */
1401           {
1402             gchar delim;
1403
1404             if (context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ) 
1405               {
1406                 delim = '\'';
1407               }
1408             else 
1409               {
1410                 delim = '"';
1411               }
1412
1413             do
1414               {
1415                 if (*context->iter == delim)
1416                   break;
1417               }
1418             while (advance_char (context));
1419           }
1420           if (context->iter == context->current_text_end)
1421             {
1422               /* The value hasn't necessarily ended. Merge with
1423                * partial chunk, leave state unchanged.
1424                */
1425               add_to_partial (context, context->start, context->iter);
1426             }
1427           else
1428             {
1429               /* The value has ended at the quote mark. Combine it
1430                * with the partial chunk if any; set it for the current
1431                * attribute.
1432                */
1433               GString *unescaped;
1434               
1435               add_to_partial (context, context->start, context->iter);
1436
1437               g_assert (context->cur_attr >= 0);
1438               
1439               if (unescape_text (context,
1440                                  context->partial_chunk->str,
1441                                  context->partial_chunk->str +
1442                                  context->partial_chunk->len,
1443                                  &unescaped,
1444                                  error))
1445                 {
1446                   /* success, advance past quote and set state. */
1447                   context->attr_values[context->cur_attr] = g_string_free (unescaped, FALSE);
1448                   advance_char (context);
1449                   context->state = STATE_BETWEEN_ATTRIBUTES;
1450                   context->start = NULL;
1451                 }
1452               
1453               truncate_partial (context);
1454             }
1455           break;
1456
1457         case STATE_INSIDE_TEXT:
1458           /* Possible next states: AFTER_OPEN_ANGLE */
1459           do
1460             {
1461               if (*context->iter == '<')
1462                 break;
1463             }
1464           while (advance_char (context));
1465
1466           /* The text hasn't necessarily ended. Merge with
1467            * partial chunk, leave state unchanged.
1468            */
1469
1470           add_to_partial (context, context->start, context->iter);
1471
1472           if (context->iter != context->current_text_end)
1473             {
1474               GString *unescaped = NULL;
1475
1476               /* The text has ended at the open angle. Call the text
1477                * callback.
1478                */
1479               
1480               if (unescape_text (context,
1481                                  context->partial_chunk->str,
1482                                  context->partial_chunk->str +
1483                                  context->partial_chunk->len,
1484                                  &unescaped,
1485                                  error))
1486                 {
1487                   GError *tmp_error = NULL;
1488
1489                   if (context->parser->text)
1490                     (*context->parser->text) (context,
1491                                               unescaped->str,
1492                                               unescaped->len,
1493                                               context->user_data,
1494                                               &tmp_error);
1495                   
1496                   g_string_free (unescaped, TRUE);
1497
1498                   if (tmp_error == NULL)
1499                     {
1500                       /* advance past open angle and set state. */
1501                       advance_char (context);
1502                       context->state = STATE_AFTER_OPEN_ANGLE;
1503                       /* could begin a passthrough */
1504                       context->start = context->iter;
1505                     }
1506                   else
1507                     {
1508                       mark_error (context, tmp_error);
1509                       g_propagate_error (error, tmp_error);
1510                     }
1511                 }
1512
1513               truncate_partial (context);
1514             }
1515           break;
1516
1517         case STATE_AFTER_CLOSE_TAG_SLASH:
1518           /* Possible next state: INSIDE_CLOSE_TAG_NAME */
1519           if (is_name_start_char (context->iter))
1520             {
1521               context->state = STATE_INSIDE_CLOSE_TAG_NAME;
1522
1523               /* start of tag name */
1524               context->start = context->iter;
1525             }
1526           else
1527             {
1528               gchar buf[8];
1529
1530               set_error (context,
1531                          error,
1532                          G_MARKUP_ERROR_PARSE,
1533                          _("'%s' is not a valid character following "
1534                            "the characters '</'; '%s' may not begin an "
1535                            "element name"),
1536                          utf8_str (context->iter, buf),
1537                          utf8_str (context->iter, buf));
1538             }
1539           break;
1540
1541         case STATE_INSIDE_CLOSE_TAG_NAME:
1542           /* Possible next state: AFTER_CLOSE_TAG_NAME */
1543           advance_to_name_end (context);
1544           add_to_partial (context, context->start, context->iter);
1545
1546           if (context->iter != context->current_text_end)
1547             context->state = STATE_AFTER_CLOSE_TAG_NAME;
1548           break;
1549
1550         case STATE_AFTER_CLOSE_TAG_NAME:
1551           /* Possible next state: AFTER_CLOSE_TAG_SLASH */
1552
1553           skip_spaces (context);
1554           
1555           if (context->iter != context->current_text_end)
1556             {
1557               gchar *close_name;
1558
1559               /* The name has ended. Combine it with the partial chunk
1560                * if any; check that it matches stack top and pop
1561                * stack; invoke proper callback; enter next state.
1562                */
1563               close_name = g_string_free (context->partial_chunk, FALSE);
1564               context->partial_chunk = NULL;
1565               
1566               if (*context->iter != '>')
1567                 {
1568                   gchar buf[8];
1569
1570                   set_error (context,
1571                              error,
1572                              G_MARKUP_ERROR_PARSE,
1573                              _("'%s' is not a valid character following "
1574                                "the close element name '%s'; the allowed "
1575                                "character is '>'"),
1576                              utf8_str (context->iter, buf),
1577                              close_name);
1578                 }
1579               else if (context->tag_stack == NULL)
1580                 {
1581                   set_error (context,
1582                              error,
1583                              G_MARKUP_ERROR_PARSE,
1584                              _("Element '%s' was closed, no element "
1585                                "is currently open"),
1586                              close_name);
1587                 }
1588               else if (strcmp (close_name, current_element (context)) != 0)
1589                 {
1590                   set_error (context,
1591                              error,
1592                              G_MARKUP_ERROR_PARSE,
1593                              _("Element '%s' was closed, but the currently "
1594                                "open element is '%s'"),
1595                              close_name,
1596                              current_element (context));
1597                 }
1598               else
1599                 {
1600                   GError *tmp_error;
1601                   advance_char (context);
1602                   context->state = STATE_AFTER_CLOSE_ANGLE;
1603                   context->start = NULL;
1604                   
1605                   /* call the end_element callback */
1606                   tmp_error = NULL;
1607                   if (context->parser->end_element)
1608                     (* context->parser->end_element) (context,
1609                                                       close_name,
1610                                                       context->user_data,
1611                                                       &tmp_error);
1612                   
1613                   
1614                   /* Pop the tag stack */
1615                   g_free (context->tag_stack->data);
1616                   context->tag_stack = g_slist_delete_link (context->tag_stack,
1617                                                             context->tag_stack);
1618                   
1619                   if (tmp_error)
1620                     {
1621                       mark_error (context, tmp_error);
1622                       g_propagate_error (error, tmp_error);
1623                     }
1624                 }
1625               
1626               g_free (close_name);
1627             }
1628           break;
1629           
1630         case STATE_INSIDE_PASSTHROUGH:
1631           /* Possible next state: AFTER_CLOSE_ANGLE */
1632           do
1633             {
1634               if (*context->iter == '<') 
1635                 context->balance++;
1636               if (*context->iter == '>') 
1637                 {
1638                   context->balance--;
1639                   add_to_partial (context, context->start, context->iter);
1640                   context->start = context->iter;
1641                   if ((g_str_has_prefix (context->partial_chunk->str, "<?")
1642                        && g_str_has_suffix (context->partial_chunk->str, "?")) ||
1643                       (g_str_has_prefix (context->partial_chunk->str, "<!--")
1644                        && g_str_has_suffix (context->partial_chunk->str, "--")) ||
1645                       (g_str_has_prefix (context->partial_chunk->str, "<![CDATA[") 
1646                        && g_str_has_suffix (context->partial_chunk->str, "]]")) ||
1647                       (g_str_has_prefix (context->partial_chunk->str, "<!DOCTYPE")
1648                        && context->balance == 0)) 
1649                     break;
1650                 }
1651             }
1652           while (advance_char (context));
1653
1654           if (context->iter == context->current_text_end)
1655             {
1656               /* The passthrough hasn't necessarily ended. Merge with
1657                * partial chunk, leave state unchanged.
1658                */
1659               add_to_partial (context, context->start, context->iter);
1660             }
1661           else
1662             {
1663               /* The passthrough has ended at the close angle. Combine
1664                * it with the partial chunk if any. Call the passthrough
1665                * callback. Note that the open/close angles are
1666                * included in the text of the passthrough.
1667                */
1668               GError *tmp_error = NULL;
1669
1670               advance_char (context); /* advance past close angle */
1671               add_to_partial (context, context->start, context->iter);
1672
1673               if (context->parser->passthrough)
1674                 (*context->parser->passthrough) (context,
1675                                                  context->partial_chunk->str,
1676                                                  context->partial_chunk->len,
1677                                                  context->user_data,
1678                                                  &tmp_error);
1679                   
1680               truncate_partial (context);
1681
1682               if (tmp_error == NULL)
1683                 {
1684                   context->state = STATE_AFTER_CLOSE_ANGLE;
1685                   context->start = context->iter; /* could begin text */
1686                 }
1687               else
1688                 {
1689                   mark_error (context, tmp_error);
1690                   g_propagate_error (error, tmp_error);
1691                 }
1692             }
1693           break;
1694
1695         case STATE_ERROR:
1696           goto finished;
1697           break;
1698
1699         default:
1700           g_assert_not_reached ();
1701           break;
1702         }
1703     }
1704
1705  finished:
1706   context->parsing = FALSE;
1707
1708   return context->state != STATE_ERROR;
1709 }
1710
1711 /**
1712  * g_markup_parse_context_end_parse:
1713  * @context: a #GMarkupParseContext
1714  * @error: return location for a #GError
1715  * 
1716  * Signals to the #GMarkupParseContext that all data has been
1717  * fed into the parse context with g_markup_parse_context_parse().
1718  * This function reports an error if the document isn't complete,
1719  * for example if elements are still open.
1720  * 
1721  * Return value: %TRUE on success, %FALSE if an error was set
1722  **/
1723 gboolean
1724 g_markup_parse_context_end_parse (GMarkupParseContext *context,
1725                                   GError             **error)
1726 {
1727   g_return_val_if_fail (context != NULL, FALSE);
1728   g_return_val_if_fail (!context->parsing, FALSE);
1729   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
1730
1731   if (context->partial_chunk != NULL)
1732     {
1733       g_string_free (context->partial_chunk, TRUE);
1734       context->partial_chunk = NULL;
1735     }
1736
1737   if (context->document_empty)
1738     {
1739       set_error (context, error, G_MARKUP_ERROR_EMPTY,
1740                  _("Document was empty or contained only whitespace"));
1741       return FALSE;
1742     }
1743   
1744   context->parsing = TRUE;
1745   
1746   switch (context->state)
1747     {
1748     case STATE_START:
1749       /* Nothing to do */
1750       break;
1751
1752     case STATE_AFTER_OPEN_ANGLE:
1753       set_error (context, error, G_MARKUP_ERROR_PARSE,
1754                  _("Document ended unexpectedly just after an open angle bracket '<'"));
1755       break;
1756
1757     case STATE_AFTER_CLOSE_ANGLE:
1758       if (context->tag_stack != NULL)
1759         {
1760           /* Error message the same as for INSIDE_TEXT */
1761           set_error (context, error, G_MARKUP_ERROR_PARSE,
1762                      _("Document ended unexpectedly with elements still open - "
1763                        "'%s' was the last element opened"),
1764                      current_element (context));
1765         }
1766       break;
1767       
1768     case STATE_AFTER_ELISION_SLASH:
1769       set_error (context, error, G_MARKUP_ERROR_PARSE,
1770                  _("Document ended unexpectedly, expected to see a close angle "
1771                    "bracket ending the tag <%s/>"), current_element (context));
1772       break;
1773
1774     case STATE_INSIDE_OPEN_TAG_NAME:
1775       set_error (context, error, G_MARKUP_ERROR_PARSE,
1776                  _("Document ended unexpectedly inside an element name"));
1777       break;
1778
1779     case STATE_INSIDE_ATTRIBUTE_NAME:
1780       set_error (context, error, G_MARKUP_ERROR_PARSE,
1781                  _("Document ended unexpectedly inside an attribute name"));
1782       break;
1783
1784     case STATE_BETWEEN_ATTRIBUTES:
1785       set_error (context, error, G_MARKUP_ERROR_PARSE,
1786                  _("Document ended unexpectedly inside an element-opening "
1787                    "tag."));
1788       break;
1789
1790     case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1791       set_error (context, error, G_MARKUP_ERROR_PARSE,
1792                  _("Document ended unexpectedly after the equals sign "
1793                    "following an attribute name; no attribute value"));
1794       break;
1795
1796     case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1797     case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1798       set_error (context, error, G_MARKUP_ERROR_PARSE,
1799                  _("Document ended unexpectedly while inside an attribute "
1800                    "value"));
1801       break;
1802
1803     case STATE_INSIDE_TEXT:
1804       g_assert (context->tag_stack != NULL);
1805       set_error (context, error, G_MARKUP_ERROR_PARSE,
1806                  _("Document ended unexpectedly with elements still open - "
1807                    "'%s' was the last element opened"),
1808                  current_element (context));
1809       break;
1810
1811     case STATE_AFTER_CLOSE_TAG_SLASH:
1812     case STATE_INSIDE_CLOSE_TAG_NAME:
1813       set_error (context, error, G_MARKUP_ERROR_PARSE,
1814                  _("Document ended unexpectedly inside the close tag for "
1815                    "element '%s'"), current_element);
1816       break;
1817
1818     case STATE_INSIDE_PASSTHROUGH:
1819       set_error (context, error, G_MARKUP_ERROR_PARSE,
1820                  _("Document ended unexpectedly inside a comment or "
1821                    "processing instruction"));
1822       break;
1823
1824     case STATE_ERROR:
1825     default:
1826       g_assert_not_reached ();
1827       break;
1828     }
1829
1830   context->parsing = FALSE;
1831
1832   return context->state != STATE_ERROR;
1833 }
1834
1835 /**
1836  * g_markup_parse_context_get_element:
1837  * @context: a #GMarkupParseContext
1838  * @returns: the name of the currently open element, or %NULL
1839  *
1840  * Retrieves the name of the currently open element.
1841  *
1842  * Since: 2.2
1843  **/
1844 G_CONST_RETURN gchar *
1845 g_markup_parse_context_get_element (GMarkupParseContext *context)
1846 {
1847   g_return_val_if_fail (context != NULL, NULL);
1848
1849   if (context->tag_stack == NULL) 
1850     return NULL;
1851   else
1852     return current_element (context);
1853
1854
1855 /**
1856  * g_markup_parse_context_get_position:
1857  * @context: a #GMarkupParseContext
1858  * @line_number: return location for a line number, or %NULL
1859  * @char_number: return location for a char-on-line number, or %NULL
1860  *
1861  * Retrieves the current line number and the number of the character on
1862  * that line. Intended for use in error messages; there are no strict
1863  * semantics for what constitutes the "current" line number other than
1864  * "the best number we could come up with for error messages."
1865  * 
1866  **/
1867 void
1868 g_markup_parse_context_get_position (GMarkupParseContext *context,
1869                                      gint                *line_number,
1870                                      gint                *char_number)
1871 {
1872   g_return_if_fail (context != NULL);
1873
1874   if (line_number)
1875     *line_number = context->line_number;
1876
1877   if (char_number)
1878     *char_number = context->char_number;
1879 }
1880
1881 static void
1882 append_escaped_text (GString     *str,
1883                      const gchar *text,
1884                      gssize       length)    
1885 {
1886   const gchar *p;
1887   const gchar *end;
1888
1889   p = text;
1890   end = text + length;
1891
1892   while (p != end)
1893     {
1894       const gchar *next;
1895       next = g_utf8_next_char (p);
1896
1897       switch (*p)
1898         {
1899         case '&':
1900           g_string_append (str, "&amp;");
1901           break;
1902
1903         case '<':
1904           g_string_append (str, "&lt;");
1905           break;
1906
1907         case '>':
1908           g_string_append (str, "&gt;");
1909           break;
1910
1911         case '\'':
1912           g_string_append (str, "&apos;");
1913           break;
1914
1915         case '"':
1916           g_string_append (str, "&quot;");
1917           break;
1918
1919         default:
1920           g_string_append_len (str, p, next - p);
1921           break;
1922         }
1923
1924       p = next;
1925     }
1926 }
1927
1928 /**
1929  * g_markup_escape_text:
1930  * @text: some valid UTF-8 text
1931  * @length: length of @text in bytes
1932  * 
1933  * Escapes text so that the markup parser will parse it verbatim.
1934  * Less than, greater than, ampersand, etc. are replaced with the
1935  * corresponding entities. This function would typically be used
1936  * when writing out a file to be parsed with the markup parser.
1937  * 
1938  * Note that this function doesn't protect whitespace and line endings
1939  * from being processed according to the XML rules for normalization
1940  * of line endings and attribute values.
1941  * 
1942  * Return value: escaped text
1943  **/
1944 gchar*
1945 g_markup_escape_text (const gchar *text,
1946                       gssize       length)  
1947 {
1948   GString *str;
1949
1950   g_return_val_if_fail (text != NULL, NULL);
1951
1952   if (length < 0)
1953     length = strlen (text);
1954
1955   /* prealloc at least as long as original text */
1956   str = g_string_sized_new (length);
1957   append_escaped_text (str, text, length);
1958
1959   return g_string_free (str, FALSE);
1960 }
1961
1962 /**
1963  * find_conversion:
1964  * @format: a printf-style format string
1965  * @after: location to store a pointer to the character after
1966  *   the returned conversion. On a %NULL return, returns the
1967  *   pointer to the trailing NUL in the string
1968  * 
1969  * Find the next conversion in a printf-style format string.
1970  * Partially based on code from printf-parser.c,
1971  * Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
1972  * 
1973  * Return value: pointer to the next conversion in @format,
1974  *  or %NULL, if none.
1975  **/
1976 static const char *
1977 find_conversion (const char  *format,
1978                  const char **after)
1979 {
1980   const char *start = format;
1981   const char *cp;
1982   
1983   while (*start != '\0' && *start != '%')
1984     start++;
1985
1986   if (*start == '\0')
1987     {
1988       *after = start;
1989       return NULL;
1990     }
1991
1992   cp = start + 1;
1993
1994   if (*cp == '\0')
1995     {
1996       *after = cp;
1997       return NULL;
1998     }
1999   
2000   /* Test for positional argument.  */
2001   if (*cp >= '0' && *cp <= '9')
2002     {
2003       const char *np;
2004       
2005       for (np = cp; *np >= '0' && *np <= '9'; np++)
2006         ;
2007       if (*np == '$')
2008         cp = np + 1;
2009     }
2010
2011   /* Skip the flags.  */
2012   for (;;)
2013     {
2014       if (*cp == '\'' ||
2015           *cp == '-' ||
2016           *cp == '+' ||
2017           *cp == ' ' ||
2018           *cp == '#' ||
2019           *cp == '0')
2020         cp++;
2021       else
2022         break;
2023     }
2024
2025   /* Skip the field width.  */
2026   if (*cp == '*')
2027     {
2028       cp++;
2029
2030       /* Test for positional argument.  */
2031       if (*cp >= '0' && *cp <= '9')
2032         {
2033           const char *np;
2034
2035           for (np = cp; *np >= '0' && *np <= '9'; np++)
2036             ;
2037           if (*np == '$')
2038             cp = np + 1;
2039         }
2040     }
2041   else
2042     {
2043       for (; *cp >= '0' && *cp <= '9'; cp++)
2044         ;
2045     }
2046
2047   /* Skip the precision.  */
2048   if (*cp == '.')
2049     {
2050       cp++;
2051       if (*cp == '*')
2052         {
2053           /* Test for positional argument.  */
2054           if (*cp >= '0' && *cp <= '9')
2055             {
2056               const char *np;
2057
2058               for (np = cp; *np >= '0' && *np <= '9'; np++)
2059                 ;
2060               if (*np == '$')
2061                 cp = np + 1;
2062             }
2063         }
2064       else
2065         {
2066           for (; *cp >= '0' && *cp <= '9'; cp++)
2067             ;
2068         }
2069     }
2070
2071   /* Skip argument type/size specifiers.  */
2072   while (*cp == 'h' ||
2073          *cp == 'L' ||
2074          *cp == 'l' ||
2075          *cp == 'j' ||
2076          *cp == 'z' ||
2077          *cp == 'Z' ||
2078          *cp == 't')
2079     cp++;
2080           
2081   /* Skip the conversion character.  */
2082   cp++;
2083
2084   *after = cp;
2085   return start;
2086 }
2087
2088 /**
2089  * g_markup_vprintf_escaped:
2090  * @format: printf() style format string
2091  * @args: variable argument list, similar to vprintf()
2092  * 
2093  * Formats the data in @args according to @format, escaping
2094  * all string and character arguments in the fashion
2095  * of g_markup_escape_text(). See g_markup_printf_escaped().
2096  * 
2097  * Return value: newly allocated result from formatting
2098  *  operation. Free with g_free().
2099  *
2100  * Since: 2.4
2101  **/
2102 char *
2103 g_markup_vprintf_escaped (const char *format,
2104                           va_list     args)
2105 {
2106   GString *format1;
2107   GString *format2;
2108   GString *result = NULL;
2109   gchar *output1 = NULL;
2110   gchar *output2 = NULL;
2111   const char *p, *op1, *op2;
2112   va_list args2;
2113
2114   /* The technique here, is that we make two format strings that
2115    * have the identical conversions in the identical order to the
2116    * original strings, but differ in the text in-between. We
2117    * then use the normal g_strdup_vprintf() to format the arguments
2118    * with the two new format strings. By comparing the results,
2119    * we can figure out what segments of the output come from
2120    * the the original format string, and what from the arguments,
2121    * and thus know what portions of the string to escape.
2122    *
2123    * For instance, for:
2124    *
2125    *  g_markup_printf_escaped ("%s ate %d apples", "Susan & Fred", 5);
2126    *
2127    * We form the two format strings "%sX%dX" and %sY%sY". The results
2128    * of formatting with those two strings are
2129    *
2130    * "%sX%dX" => "Susan & FredX5X"
2131    * "%sY%dY" => "Susan & FredY5Y"
2132    *
2133    * To find the span of the first argument, we find the first position
2134    * where the two arguments differ, which tells us that the first
2135    * argument formatted to "Susan & Fred". We then escape that
2136    * to "Susan &amp; Fred" and join up with the intermediate portions
2137    * of the format string and the second argument to get
2138    * "Susan &amp; Fred ate 5 apples".
2139    */
2140
2141   /* Create the two modified format strings
2142    */
2143   format1 = g_string_new (NULL);
2144   format2 = g_string_new (NULL);
2145   p = format;
2146   while (TRUE)
2147     {
2148       const char *after;
2149       const char *conv = find_conversion (p, &after);
2150       if (!conv)
2151         break;
2152
2153       g_string_append_len (format1, conv, after - conv);
2154       g_string_append_c (format1, 'X');
2155       g_string_append_len (format2, conv, after - conv);
2156       g_string_append_c (format2, 'Y');
2157
2158       p = after;
2159     }
2160
2161   /* Use them to format the arguments
2162    */
2163   G_VA_COPY (args2, args);
2164   
2165   output1 = g_strdup_vprintf (format1->str, args);
2166   va_end (args);
2167   if (!output1)
2168     goto cleanup;
2169   
2170   output2 = g_strdup_vprintf (format2->str, args2);
2171   va_end (args2);
2172   if (!output2)
2173     goto cleanup;
2174
2175   result = g_string_new (NULL);
2176
2177   /* Iterate through the original format string again,
2178    * copying the non-conversion portions and the escaped
2179    * converted arguments to the output string.
2180    */
2181   op1 = output1;
2182   op2 = output2;
2183   p = format;
2184   while (TRUE)
2185     {
2186       const char *after;
2187       const char *output_start;
2188       const char *conv = find_conversion (p, &after);
2189       char *escaped;
2190       
2191       if (!conv)        /* The end, after points to the trailing \0 */
2192         {
2193           g_string_append_len (result, p, after - p);
2194           break;
2195         }
2196
2197       g_string_append_len (result, p, conv - p);
2198       output_start = op1;
2199       while (*op1 == *op2)
2200         {
2201           op1++;
2202           op2++;
2203         }
2204       
2205       escaped = g_markup_escape_text (output_start, op1 - output_start);
2206       g_string_append (result, escaped);
2207       g_free (escaped);
2208       
2209       p = after;
2210       op1++;
2211       op2++;
2212     }
2213
2214  cleanup:
2215   g_string_free (format1, TRUE);
2216   g_string_free (format2, TRUE);
2217   g_free (output1);
2218   g_free (output2);
2219
2220   if (result)
2221     return g_string_free (result, FALSE);
2222   else
2223     return NULL;
2224 }
2225
2226 /**
2227  * g_markup_printf_escaped:
2228  * @format: printf() style format string
2229  * @Varargs: the arguments to insert in the format string
2230  * 
2231  * Formats arguments according to @format, escaping
2232  * all string and character arguments in the fashion
2233  * of g_markup_escape_text(). This is useful when you
2234  * want to insert literal strings into XML-style markup
2235  * output, without having to worry that the strings
2236  * might themselves contain markup.
2237  *
2238  * <informalexample><programlisting>
2239  * const char *store = "Fortnum &amp; Mason";
2240  * const char *item = "Tea";
2241  * char *output;
2242  * &nbsp;
2243  * output = g_markup_printf_escaped ("&lt;purchase&gt;"
2244  *                                   "&lt;store&gt;&percnt;s&lt;/store&gt;"
2245  *                                   "&lt;item&gt;&percnt;s&lt;/item&gt;"
2246  *                                   "&lt;/purchase&gt;",
2247  *                                   store, item);
2248  * </programlisting></informalexample>
2249  * 
2250  * Return value: newly allocated result from formatting
2251  *  operation. Free with g_free().
2252  *
2253  * Since: 2.4
2254  **/
2255 char *
2256 g_markup_printf_escaped (const char *format, ...)
2257 {
2258   char *result;
2259   va_list args;
2260   
2261   va_start (args, format);
2262   result = g_markup_vprintf_escaped (format, args);
2263   va_end (args);
2264
2265   return result;
2266 }