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