Move short_month_names and long_month_names to bss.
[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                   context->balance--;
1629                   add_to_partial (context, context->start, context->iter);
1630                   context->start = context->iter;
1631                   if ((g_str_has_prefix (context->partial_chunk->str, "<?")
1632                        && g_str_has_suffix (context->partial_chunk->str, "?")) ||
1633                       (g_str_has_prefix (context->partial_chunk->str, "<!--")
1634                        && g_str_has_suffix (context->partial_chunk->str, "--")) ||
1635                       (g_str_has_prefix (context->partial_chunk->str, "<![CDATA[") 
1636                        && g_str_has_suffix (context->partial_chunk->str, "]]")) ||
1637                       (g_str_has_prefix (context->partial_chunk->str, "<!DOCTYPE")
1638                        && context->balance == 0)) 
1639                     break;
1640                 }
1641             }
1642           while (advance_char (context));
1643
1644           if (context->iter == context->current_text_end)
1645             {
1646               /* The passthrough hasn't necessarily ended. Merge with
1647                * partial chunk, leave state unchanged.
1648                */
1649               add_to_partial (context, context->start, context->iter);
1650             }
1651           else
1652             {
1653               /* The passthrough has ended at the close angle. Combine
1654                * it with the partial chunk if any. Call the passthrough
1655                * callback. Note that the open/close angles are
1656                * included in the text of the passthrough.
1657                */
1658               GError *tmp_error = NULL;
1659
1660               advance_char (context); /* advance past close angle */
1661               add_to_partial (context, context->start, context->iter);
1662
1663               if (context->parser->passthrough)
1664                 (*context->parser->passthrough) (context,
1665                                                  context->partial_chunk->str,
1666                                                  context->partial_chunk->len,
1667                                                  context->user_data,
1668                                                  &tmp_error);
1669                   
1670               truncate_partial (context);
1671
1672               if (tmp_error == NULL)
1673                 {
1674                   context->state = STATE_AFTER_CLOSE_ANGLE;
1675                   context->start = context->iter; /* could begin text */
1676                 }
1677               else
1678                 {
1679                   mark_error (context, tmp_error);
1680                   g_propagate_error (error, tmp_error);
1681                 }
1682             }
1683           break;
1684
1685         case STATE_ERROR:
1686           goto finished;
1687           break;
1688
1689         default:
1690           g_assert_not_reached ();
1691           break;
1692         }
1693     }
1694
1695  finished:
1696   context->parsing = FALSE;
1697
1698   return context->state != STATE_ERROR;
1699 }
1700
1701 /**
1702  * g_markup_parse_context_end_parse:
1703  * @context: a #GMarkupParseContext
1704  * @error: return location for a #GError
1705  * 
1706  * Signals to the #GMarkupParseContext that all data has been
1707  * fed into the parse context with g_markup_parse_context_parse().
1708  * This function reports an error if the document isn't complete,
1709  * for example if elements are still open.
1710  * 
1711  * Return value: %TRUE on success, %FALSE if an error was set
1712  **/
1713 gboolean
1714 g_markup_parse_context_end_parse (GMarkupParseContext *context,
1715                                   GError             **error)
1716 {
1717   g_return_val_if_fail (context != NULL, FALSE);
1718   g_return_val_if_fail (!context->parsing, FALSE);
1719   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
1720
1721   if (context->partial_chunk != NULL)
1722     {
1723       g_string_free (context->partial_chunk, TRUE);
1724       context->partial_chunk = NULL;
1725     }
1726
1727   if (context->document_empty)
1728     {
1729       set_error (context, error, G_MARKUP_ERROR_EMPTY,
1730                  _("Document was empty or contained only whitespace"));
1731       return FALSE;
1732     }
1733   
1734   context->parsing = TRUE;
1735   
1736   switch (context->state)
1737     {
1738     case STATE_START:
1739       /* Nothing to do */
1740       break;
1741
1742     case STATE_AFTER_OPEN_ANGLE:
1743       set_error (context, error, G_MARKUP_ERROR_PARSE,
1744                  _("Document ended unexpectedly just after an open angle bracket '<'"));
1745       break;
1746
1747     case STATE_AFTER_CLOSE_ANGLE:
1748       if (context->tag_stack != NULL)
1749         {
1750           /* Error message the same as for INSIDE_TEXT */
1751           set_error (context, error, G_MARKUP_ERROR_PARSE,
1752                      _("Document ended unexpectedly with elements still open - "
1753                        "'%s' was the last element opened"),
1754                      current_element (context));
1755         }
1756       break;
1757       
1758     case STATE_AFTER_ELISION_SLASH:
1759       set_error (context, error, G_MARKUP_ERROR_PARSE,
1760                  _("Document ended unexpectedly, expected to see a close angle "
1761                    "bracket ending the tag <%s/>"), current_element (context));
1762       break;
1763
1764     case STATE_INSIDE_OPEN_TAG_NAME:
1765       set_error (context, error, G_MARKUP_ERROR_PARSE,
1766                  _("Document ended unexpectedly inside an element name"));
1767       break;
1768
1769     case STATE_INSIDE_ATTRIBUTE_NAME:
1770       set_error (context, error, G_MARKUP_ERROR_PARSE,
1771                  _("Document ended unexpectedly inside an attribute name"));
1772       break;
1773
1774     case STATE_BETWEEN_ATTRIBUTES:
1775       set_error (context, error, G_MARKUP_ERROR_PARSE,
1776                  _("Document ended unexpectedly inside an element-opening "
1777                    "tag."));
1778       break;
1779
1780     case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1781       set_error (context, error, G_MARKUP_ERROR_PARSE,
1782                  _("Document ended unexpectedly after the equals sign "
1783                    "following an attribute name; no attribute value"));
1784       break;
1785
1786     case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1787     case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1788       set_error (context, error, G_MARKUP_ERROR_PARSE,
1789                  _("Document ended unexpectedly while inside an attribute "
1790                    "value"));
1791       break;
1792
1793     case STATE_INSIDE_TEXT:
1794       g_assert (context->tag_stack != NULL);
1795       set_error (context, error, G_MARKUP_ERROR_PARSE,
1796                  _("Document ended unexpectedly with elements still open - "
1797                    "'%s' was the last element opened"),
1798                  current_element (context));
1799       break;
1800
1801     case STATE_AFTER_CLOSE_TAG_SLASH:
1802     case STATE_INSIDE_CLOSE_TAG_NAME:
1803       set_error (context, error, G_MARKUP_ERROR_PARSE,
1804                  _("Document ended unexpectedly inside the close tag for "
1805                    "element '%s'"), current_element (context));
1806       break;
1807
1808     case STATE_INSIDE_PASSTHROUGH:
1809       set_error (context, error, G_MARKUP_ERROR_PARSE,
1810                  _("Document ended unexpectedly inside a comment or "
1811                    "processing instruction"));
1812       break;
1813
1814     case STATE_ERROR:
1815     default:
1816       g_assert_not_reached ();
1817       break;
1818     }
1819
1820   context->parsing = FALSE;
1821
1822   return context->state != STATE_ERROR;
1823 }
1824
1825 /**
1826  * g_markup_parse_context_get_element:
1827  * @context: a #GMarkupParseContext
1828  * @returns: the name of the currently open element, or %NULL
1829  *
1830  * Retrieves the name of the currently open element.
1831  *
1832  * Since: 2.2
1833  **/
1834 G_CONST_RETURN gchar *
1835 g_markup_parse_context_get_element (GMarkupParseContext *context)
1836 {
1837   g_return_val_if_fail (context != NULL, NULL);
1838
1839   if (context->tag_stack == NULL) 
1840     return NULL;
1841   else
1842     return current_element (context);
1843
1844
1845 /**
1846  * g_markup_parse_context_get_position:
1847  * @context: a #GMarkupParseContext
1848  * @line_number: return location for a line number, or %NULL
1849  * @char_number: return location for a char-on-line number, or %NULL
1850  *
1851  * Retrieves the current line number and the number of the character on
1852  * that line. Intended for use in error messages; there are no strict
1853  * semantics for what constitutes the "current" line number other than
1854  * "the best number we could come up with for error messages."
1855  * 
1856  **/
1857 void
1858 g_markup_parse_context_get_position (GMarkupParseContext *context,
1859                                      gint                *line_number,
1860                                      gint                *char_number)
1861 {
1862   g_return_if_fail (context != NULL);
1863
1864   if (line_number)
1865     *line_number = context->line_number;
1866
1867   if (char_number)
1868     *char_number = context->char_number;
1869 }
1870
1871 static void
1872 append_escaped_text (GString     *str,
1873                      const gchar *text,
1874                      gssize       length)    
1875 {
1876   const gchar *p;
1877   const gchar *end;
1878
1879   p = text;
1880   end = text + length;
1881
1882   while (p != end)
1883     {
1884       const gchar *next;
1885       next = g_utf8_next_char (p);
1886
1887       switch (*p)
1888         {
1889         case '&':
1890           g_string_append (str, "&amp;");
1891           break;
1892
1893         case '<':
1894           g_string_append (str, "&lt;");
1895           break;
1896
1897         case '>':
1898           g_string_append (str, "&gt;");
1899           break;
1900
1901         case '\'':
1902           g_string_append (str, "&apos;");
1903           break;
1904
1905         case '"':
1906           g_string_append (str, "&quot;");
1907           break;
1908
1909         default:
1910           g_string_append_len (str, p, next - p);
1911           break;
1912         }
1913
1914       p = next;
1915     }
1916 }
1917
1918 /**
1919  * g_markup_escape_text:
1920  * @text: some valid UTF-8 text
1921  * @length: length of @text in bytes, or -1 if the text is nul-terminated
1922  * 
1923  * Escapes text so that the markup parser will parse it verbatim.
1924  * Less than, greater than, ampersand, etc. are replaced with the
1925  * corresponding entities. This function would typically be used
1926  * when writing out a file to be parsed with the markup parser.
1927  * 
1928  * Note that this function doesn't protect whitespace and line endings
1929  * from being processed according to the XML rules for normalization
1930  * of line endings and attribute values.
1931  * 
1932  * Return value: a newly allocated string with the escaped text
1933  **/
1934 gchar*
1935 g_markup_escape_text (const gchar *text,
1936                       gssize       length)  
1937 {
1938   GString *str;
1939
1940   g_return_val_if_fail (text != NULL, NULL);
1941
1942   if (length < 0)
1943     length = strlen (text);
1944
1945   /* prealloc at least as long as original text */
1946   str = g_string_sized_new (length);
1947   append_escaped_text (str, text, length);
1948
1949   return g_string_free (str, FALSE);
1950 }
1951
1952 /**
1953  * find_conversion:
1954  * @format: a printf-style format string
1955  * @after: location to store a pointer to the character after
1956  *   the returned conversion. On a %NULL return, returns the
1957  *   pointer to the trailing NUL in the string
1958  * 
1959  * Find the next conversion in a printf-style format string.
1960  * Partially based on code from printf-parser.c,
1961  * Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
1962  * 
1963  * Return value: pointer to the next conversion in @format,
1964  *  or %NULL, if none.
1965  **/
1966 static const char *
1967 find_conversion (const char  *format,
1968                  const char **after)
1969 {
1970   const char *start = format;
1971   const char *cp;
1972   
1973   while (*start != '\0' && *start != '%')
1974     start++;
1975
1976   if (*start == '\0')
1977     {
1978       *after = start;
1979       return NULL;
1980     }
1981
1982   cp = start + 1;
1983
1984   if (*cp == '\0')
1985     {
1986       *after = cp;
1987       return NULL;
1988     }
1989   
1990   /* Test for positional argument.  */
1991   if (*cp >= '0' && *cp <= '9')
1992     {
1993       const char *np;
1994       
1995       for (np = cp; *np >= '0' && *np <= '9'; np++)
1996         ;
1997       if (*np == '$')
1998         cp = np + 1;
1999     }
2000
2001   /* Skip the flags.  */
2002   for (;;)
2003     {
2004       if (*cp == '\'' ||
2005           *cp == '-' ||
2006           *cp == '+' ||
2007           *cp == ' ' ||
2008           *cp == '#' ||
2009           *cp == '0')
2010         cp++;
2011       else
2012         break;
2013     }
2014
2015   /* Skip the field width.  */
2016   if (*cp == '*')
2017     {
2018       cp++;
2019
2020       /* Test for positional argument.  */
2021       if (*cp >= '0' && *cp <= '9')
2022         {
2023           const char *np;
2024
2025           for (np = cp; *np >= '0' && *np <= '9'; np++)
2026             ;
2027           if (*np == '$')
2028             cp = np + 1;
2029         }
2030     }
2031   else
2032     {
2033       for (; *cp >= '0' && *cp <= '9'; cp++)
2034         ;
2035     }
2036
2037   /* Skip the precision.  */
2038   if (*cp == '.')
2039     {
2040       cp++;
2041       if (*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
2061   /* Skip argument type/size specifiers.  */
2062   while (*cp == 'h' ||
2063          *cp == 'L' ||
2064          *cp == 'l' ||
2065          *cp == 'j' ||
2066          *cp == 'z' ||
2067          *cp == 'Z' ||
2068          *cp == 't')
2069     cp++;
2070           
2071   /* Skip the conversion character.  */
2072   cp++;
2073
2074   *after = cp;
2075   return start;
2076 }
2077
2078 /**
2079  * g_markup_vprintf_escaped:
2080  * @format: printf() style format string
2081  * @args: variable argument list, similar to vprintf()
2082  * 
2083  * Formats the data in @args according to @format, escaping
2084  * all string and character arguments in the fashion
2085  * of g_markup_escape_text(). See g_markup_printf_escaped().
2086  * 
2087  * Return value: newly allocated result from formatting
2088  *  operation. Free with g_free().
2089  *
2090  * Since: 2.4
2091  **/
2092 char *
2093 g_markup_vprintf_escaped (const char *format,
2094                           va_list     args)
2095 {
2096   GString *format1;
2097   GString *format2;
2098   GString *result = NULL;
2099   gchar *output1 = NULL;
2100   gchar *output2 = NULL;
2101   const char *p, *op1, *op2;
2102   va_list args2;
2103
2104   /* The technique here, is that we make two format strings that
2105    * have the identical conversions in the identical order to the
2106    * original strings, but differ in the text in-between. We
2107    * then use the normal g_strdup_vprintf() to format the arguments
2108    * with the two new format strings. By comparing the results,
2109    * we can figure out what segments of the output come from
2110    * the the original format string, and what from the arguments,
2111    * and thus know what portions of the string to escape.
2112    *
2113    * For instance, for:
2114    *
2115    *  g_markup_printf_escaped ("%s ate %d apples", "Susan & Fred", 5);
2116    *
2117    * We form the two format strings "%sX%dX" and %sY%sY". The results
2118    * of formatting with those two strings are
2119    *
2120    * "%sX%dX" => "Susan & FredX5X"
2121    * "%sY%dY" => "Susan & FredY5Y"
2122    *
2123    * To find the span of the first argument, we find the first position
2124    * where the two arguments differ, which tells us that the first
2125    * argument formatted to "Susan & Fred". We then escape that
2126    * to "Susan &amp; Fred" and join up with the intermediate portions
2127    * of the format string and the second argument to get
2128    * "Susan &amp; Fred ate 5 apples".
2129    */
2130
2131   /* Create the two modified format strings
2132    */
2133   format1 = g_string_new (NULL);
2134   format2 = g_string_new (NULL);
2135   p = format;
2136   while (TRUE)
2137     {
2138       const char *after;
2139       const char *conv = find_conversion (p, &after);
2140       if (!conv)
2141         break;
2142
2143       g_string_append_len (format1, conv, after - conv);
2144       g_string_append_c (format1, 'X');
2145       g_string_append_len (format2, conv, after - conv);
2146       g_string_append_c (format2, 'Y');
2147
2148       p = after;
2149     }
2150
2151   /* Use them to format the arguments
2152    */
2153   G_VA_COPY (args2, args);
2154   
2155   output1 = g_strdup_vprintf (format1->str, args);
2156   va_end (args);
2157   if (!output1)
2158     goto cleanup;
2159   
2160   output2 = g_strdup_vprintf (format2->str, args2);
2161   va_end (args2);
2162   if (!output2)
2163     goto cleanup;
2164
2165   result = g_string_new (NULL);
2166
2167   /* Iterate through the original format string again,
2168    * copying the non-conversion portions and the escaped
2169    * converted arguments to the output string.
2170    */
2171   op1 = output1;
2172   op2 = output2;
2173   p = format;
2174   while (TRUE)
2175     {
2176       const char *after;
2177       const char *output_start;
2178       const char *conv = find_conversion (p, &after);
2179       char *escaped;
2180       
2181       if (!conv)        /* The end, after points to the trailing \0 */
2182         {
2183           g_string_append_len (result, p, after - p);
2184           break;
2185         }
2186
2187       g_string_append_len (result, p, conv - p);
2188       output_start = op1;
2189       while (*op1 == *op2)
2190         {
2191           op1++;
2192           op2++;
2193         }
2194       
2195       escaped = g_markup_escape_text (output_start, op1 - output_start);
2196       g_string_append (result, escaped);
2197       g_free (escaped);
2198       
2199       p = after;
2200       op1++;
2201       op2++;
2202     }
2203
2204  cleanup:
2205   g_string_free (format1, TRUE);
2206   g_string_free (format2, TRUE);
2207   g_free (output1);
2208   g_free (output2);
2209
2210   if (result)
2211     return g_string_free (result, FALSE);
2212   else
2213     return NULL;
2214 }
2215
2216 /**
2217  * g_markup_printf_escaped:
2218  * @format: printf() style format string
2219  * @Varargs: the arguments to insert in the format string
2220  * 
2221  * Formats arguments according to @format, escaping
2222  * all string and character arguments in the fashion
2223  * of g_markup_escape_text(). This is useful when you
2224  * want to insert literal strings into XML-style markup
2225  * output, without having to worry that the strings
2226  * might themselves contain markup.
2227  *
2228  * <informalexample><programlisting>
2229  * const char *store = "Fortnum &amp; Mason";
2230  * const char *item = "Tea";
2231  * char *output;
2232  * &nbsp;
2233  * output = g_markup_printf_escaped ("&lt;purchase&gt;"
2234  *                                   "&lt;store&gt;&percnt;s&lt;/store&gt;"
2235  *                                   "&lt;item&gt;&percnt;s&lt;/item&gt;"
2236  *                                   "&lt;/purchase&gt;",
2237  *                                   store, item);
2238  * </programlisting></informalexample>
2239  * 
2240  * Return value: newly allocated result from formatting
2241  *  operation. Free with g_free().
2242  *
2243  * Since: 2.4
2244  **/
2245 char *
2246 g_markup_printf_escaped (const char *format, ...)
2247 {
2248   char *result;
2249   va_list args;
2250   
2251   va_start (args, format);
2252   result = g_markup_vprintf_escaped (format, args);
2253   va_end (args);
2254
2255   return result;
2256 }
2257
2258 #define __G_MARKUP_C__
2259 #include "galiasdef.c"