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