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