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