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