Bug 556921 – gpoll.h breaks hal compilation
[platform/upstream/glib.git] / glib / gmarkup.c
1 /* gmarkup.c - Simple XML-like parser
2  *
3  *  Copyright 2000, 2003 Red Hat, Inc.
4  *  Copyright 2007, 2008 Ryan Lortie <desrt@desrt.ca>
5  *
6  * GLib is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * GLib is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GLib; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  *   Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29
30 #include "glib.h"
31 #include "glibintl.h"
32 #include "galias.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 typedef struct
62 {
63   const char *prev_element;
64   const GMarkupParser *prev_parser;
65   gpointer prev_user_data;
66 } GMarkupRecursionTracker;
67
68 struct _GMarkupParseContext
69 {
70   const GMarkupParser *parser;
71
72   GMarkupParseFlags flags;
73
74   gint line_number;
75   gint char_number;
76
77   gpointer user_data;
78   GDestroyNotify dnotify;
79
80   /* A piece of character data or an element that
81    * hasn't "ended" yet so we haven't yet called
82    * the callback for it.
83    */
84   GString *partial_chunk;
85
86   GMarkupParseState state;
87   GSList *tag_stack;
88   gchar **attr_names;
89   gchar **attr_values;
90   gint cur_attr;
91   gint alloc_attrs;
92
93   const gchar *current_text;
94   gssize       current_text_len;      
95   const gchar *current_text_end;
96
97   GString *leftover_char_portion;
98
99   /* used to save the start of the last interesting thingy */
100   const gchar *start;
101
102   const gchar *iter;
103
104   guint document_empty : 1;
105   guint parsing : 1;
106   guint awaiting_pop : 1;
107   gint balance;
108
109   /* subparser support */
110   GSList *subparser_stack; /* (GMarkupRecursionTracker *) */
111   const char *subparser_element;
112   gpointer held_user_data;
113 };
114
115 /**
116  * g_markup_parse_context_new:
117  * @parser: a #GMarkupParser
118  * @flags: one or more #GMarkupParseFlags
119  * @user_data: user data to pass to #GMarkupParser functions
120  * @user_data_dnotify: user data destroy notifier called when the parse context is freed
121  * 
122  * Creates a new parse context. A parse context is used to parse
123  * marked-up documents. You can feed any number of documents into
124  * a context, as long as no errors occur; once an error occurs,
125  * the parse context can't continue to parse text (you have to free it
126  * and create a new parse context).
127  * 
128  * Return value: a new #GMarkupParseContext
129  **/
130 GMarkupParseContext *
131 g_markup_parse_context_new (const GMarkupParser *parser,
132                             GMarkupParseFlags    flags,
133                             gpointer             user_data,
134                             GDestroyNotify       user_data_dnotify)
135 {
136   GMarkupParseContext *context;
137
138   g_return_val_if_fail (parser != NULL, NULL);
139
140   context = g_new (GMarkupParseContext, 1);
141
142   context->parser = parser;
143   context->flags = flags;
144   context->user_data = user_data;
145   context->dnotify = user_data_dnotify;
146
147   context->line_number = 1;
148   context->char_number = 1;
149
150   context->partial_chunk = NULL;
151
152   context->state = STATE_START;
153   context->tag_stack = NULL;
154   context->attr_names = NULL;
155   context->attr_values = NULL;
156   context->cur_attr = -1;
157   context->alloc_attrs = 0;
158
159   context->current_text = NULL;
160   context->current_text_len = -1;
161   context->current_text_end = NULL;
162   context->leftover_char_portion = NULL;
163
164   context->start = NULL;
165   context->iter = NULL;
166
167   context->document_empty = TRUE;
168   context->parsing = FALSE;
169
170   context->awaiting_pop = FALSE;
171   context->subparser_stack = NULL;
172   context->subparser_element = NULL;
173
174   /* this is only looked at if awaiting_pop = TRUE.  initialise anyway. */
175   context->held_user_data = NULL;
176
177   context->balance = 0;
178
179   return context;
180 }
181
182 /**
183  * g_markup_parse_context_free:
184  * @context: a #GMarkupParseContext
185  * 
186  * Frees a #GMarkupParseContext. Can't be called from inside
187  * one of the #GMarkupParser functions. Can't be called while
188  * a subparser is pushed.
189  **/
190 void
191 g_markup_parse_context_free (GMarkupParseContext *context)
192 {
193   g_return_if_fail (context != NULL);
194   g_return_if_fail (!context->parsing);
195   g_return_if_fail (!context->subparser_stack);
196   g_return_if_fail (!context->awaiting_pop);
197
198   if (context->dnotify)
199     (* context->dnotify) (context->user_data);
200
201   g_strfreev (context->attr_names);
202   g_strfreev (context->attr_values);
203
204   g_slist_foreach (context->tag_stack, (GFunc)g_free, NULL);
205   g_slist_free (context->tag_stack);
206
207   if (context->partial_chunk)
208     g_string_free (context->partial_chunk, TRUE);
209
210   if (context->leftover_char_portion)
211     g_string_free (context->leftover_char_portion, TRUE);
212
213   g_free (context);
214 }
215
216 static void pop_subparser_stack (GMarkupParseContext *context);
217
218 static void
219 mark_error (GMarkupParseContext *context,
220             GError              *error)
221 {
222   context->state = STATE_ERROR;
223
224   if (context->parser->error)
225     (*context->parser->error) (context, error, context->user_data);
226
227   /* report the error all the way up to free all the user-data */
228   while (context->subparser_stack)
229     {
230       pop_subparser_stack (context);
231       context->awaiting_pop = FALSE; /* already been freed */
232
233       if (context->parser->error)
234         (*context->parser->error) (context, error, context->user_data);
235     }
236 }
237
238 static void set_error (GMarkupParseContext *context,
239                        GError             **error,
240                        GMarkupError         code,
241                        const gchar         *format,
242                        ...) G_GNUC_PRINTF (4, 5);
243
244 static void
245 set_error_literal (GMarkupParseContext *context,
246                    GError             **error,
247                    GMarkupError         code,
248                    const gchar         *message)
249 {
250   GError *tmp_error;
251
252   tmp_error = g_error_new_literal (G_MARKUP_ERROR, code, message);
253
254   g_prefix_error (&tmp_error,
255                   _("Error on line %d char %d: "),
256                   context->line_number,
257                   context->char_number);
258
259   mark_error (context, tmp_error);
260
261   g_propagate_error (error, tmp_error);
262 }
263
264 static void
265 set_error (GMarkupParseContext *context,
266            GError             **error,
267            GMarkupError         code,
268            const gchar         *format,
269            ...)
270 {
271   gchar *s;
272   gchar *s_valid;
273   va_list args;
274
275   va_start (args, format);
276   s = g_strdup_vprintf (format, args);
277   va_end (args);
278
279   /* Make sure that the GError message is valid UTF-8 even if it is
280    * complaining about invalid UTF-8 in the markup: */
281   s_valid = _g_utf8_make_valid (s);
282   set_error_literal (context, error, code, s);
283
284   g_free (s);
285   g_free (s_valid);
286 }
287
288 static void
289 propagate_error (GMarkupParseContext  *context,
290                  GError              **dest,
291                  GError               *src)
292 {
293   if (context->flags & G_MARKUP_PREFIX_ERROR_POSITION)
294     g_prefix_error (&src,
295                     _("Error on line %d char %d: "),
296                     context->line_number,
297                     context->char_number);
298
299   mark_error (context, src);
300
301   g_propagate_error (dest, src);
302 }
303
304 /* To make these faster, we first use the ascii-only tests, then check
305  * for the usual non-alnum name-end chars, and only then call the
306  * expensive unicode stuff. Nobody uses non-ascii in XML tag/attribute
307  * names, so this is a reasonable hack that virtually always avoids
308  * the guniprop call.
309  */
310 #define IS_COMMON_NAME_END_CHAR(c) \
311   ((c) == '=' || (c) == '/' || (c) == '>' || (c) == ' ')
312
313 static gboolean
314 is_name_start_char (const gchar *p)
315 {
316   if (g_ascii_isalpha (*p) ||
317       (!IS_COMMON_NAME_END_CHAR (*p) &&
318        (*p == '_' || 
319         *p == ':' ||
320         g_unichar_isalpha (g_utf8_get_char (p)))))
321     return TRUE;
322   else
323     return FALSE;
324 }
325
326 static gboolean
327 is_name_char (const gchar *p)
328 {
329   if (g_ascii_isalnum (*p) ||
330       (!IS_COMMON_NAME_END_CHAR (*p) &&
331        (*p == '.' || 
332         *p == '-' ||
333         *p == '_' ||
334         *p == ':' ||
335         g_unichar_isalpha (g_utf8_get_char (p)))))
336     return TRUE;
337   else
338     return FALSE;
339 }
340
341
342 static gchar*
343 char_str (gunichar c,
344           gchar   *buf)
345 {
346   memset (buf, 0, 8);
347   g_unichar_to_utf8 (c, buf);
348   return buf;
349 }
350
351 static gchar*
352 utf8_str (const gchar *utf8,
353           gchar       *buf)
354 {
355   char_str (g_utf8_get_char (utf8), buf);
356   return buf;
357 }
358
359 static void
360 set_unescape_error (GMarkupParseContext *context,
361                     GError             **error,
362                     const gchar         *remaining_text,
363                     const gchar         *remaining_text_end,
364                     GMarkupError         code,
365                     const gchar         *format,
366                     ...)
367 {
368   GError *tmp_error;
369   gchar *s;
370   va_list args;
371   gint remaining_newlines;
372   const gchar *p;
373
374   remaining_newlines = 0;
375   p = remaining_text;
376   while (p != remaining_text_end)
377     {
378       if (*p == '\n')
379         ++remaining_newlines;
380       ++p;
381     }
382
383   va_start (args, format);
384   s = g_strdup_vprintf (format, args);
385   va_end (args);
386
387   tmp_error = g_error_new (G_MARKUP_ERROR,
388                            code,
389                            _("Error on line %d: %s"),
390                            context->line_number - remaining_newlines,
391                            s);
392
393   g_free (s);
394
395   mark_error (context, tmp_error);
396
397   g_propagate_error (error, tmp_error);
398 }
399
400 typedef enum
401 {
402   USTATE_INSIDE_TEXT,
403   USTATE_AFTER_AMPERSAND,
404   USTATE_INSIDE_ENTITY_NAME,
405   USTATE_AFTER_CHARREF_HASH
406 } UnescapeState;
407
408 typedef struct
409 {
410   GMarkupParseContext *context;
411   GString *str;
412   UnescapeState state;
413   const gchar *text;
414   const gchar *text_end;
415   const gchar *entity_start;
416 } UnescapeContext;
417
418 static const gchar*
419 unescape_text_state_inside_text (UnescapeContext *ucontext,
420                                  const gchar     *p,
421                                  GError         **error)
422 {
423   const gchar *start;
424   gboolean normalize_attribute;
425
426   if (ucontext->context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ ||
427       ucontext->context->state == STATE_INSIDE_ATTRIBUTE_VALUE_DQ)
428     normalize_attribute = TRUE;
429   else
430     normalize_attribute = FALSE;
431
432   start = p;
433   
434   while (p != ucontext->text_end)
435     {
436       if (*p == '&')
437         {
438           break;
439         }
440       else if (normalize_attribute && (*p == '\t' || *p == '\n'))
441         {
442           g_string_append_len (ucontext->str, start, p - start);
443           g_string_append_c (ucontext->str, ' ');
444           p = g_utf8_next_char (p);
445           start = p;
446         }
447       else if (*p == '\r')
448         {
449           g_string_append_len (ucontext->str, start, p - start);
450           g_string_append_c (ucontext->str, normalize_attribute ? ' ' : '\n');
451           p = g_utf8_next_char (p);
452           if (p != ucontext->text_end && *p == '\n')
453             p = g_utf8_next_char (p);
454           start = p;
455         }
456       else
457         p = g_utf8_next_char (p);
458     }
459   
460   if (p != start)
461     g_string_append_len (ucontext->str, start, p - start);
462   
463   if (p != ucontext->text_end && *p == '&')
464     {
465       p = g_utf8_next_char (p);
466       ucontext->state = USTATE_AFTER_AMPERSAND;
467     }
468
469   return p;
470 }
471
472 static const gchar*
473 unescape_text_state_after_ampersand (UnescapeContext *ucontext,
474                                      const gchar     *p,
475                                      GError         **error)
476 {
477   ucontext->entity_start = NULL;
478   
479   if (*p == '#')
480     {
481       p = g_utf8_next_char (p);
482
483       ucontext->entity_start = p;
484       ucontext->state = USTATE_AFTER_CHARREF_HASH;
485     }
486   else if (!is_name_start_char (p))
487     {
488       if (*p == ';')
489         {
490           set_unescape_error (ucontext->context, error,
491                               p, ucontext->text_end,
492                               G_MARKUP_ERROR_PARSE,
493                               _("Empty entity '&;' seen; valid "
494                                 "entities are: &amp; &quot; &lt; &gt; &apos;"));
495         }
496       else
497         {
498           gchar buf[8];
499
500           set_unescape_error (ucontext->context, error,
501                               p, ucontext->text_end,
502                               G_MARKUP_ERROR_PARSE,
503                               _("Character '%s' is not valid at "
504                                 "the start of an entity name; "
505                                 "the & character begins an entity; "
506                                 "if this ampersand isn't supposed "
507                                 "to be an entity, escape it as "
508                                 "&amp;"),
509                               utf8_str (p, buf));
510         }
511     }
512   else
513     {
514       ucontext->entity_start = p;
515       ucontext->state = USTATE_INSIDE_ENTITY_NAME;
516     }
517
518   return p;
519 }
520
521 static const gchar*
522 unescape_text_state_inside_entity_name (UnescapeContext *ucontext,
523                                         const gchar     *p,
524                                         GError         **error)
525 {
526   while (p != ucontext->text_end)
527     {
528       if (*p == ';')
529         break;
530       else if (!is_name_char (p))
531         {
532           gchar ubuf[8];
533
534           set_unescape_error (ucontext->context, error,
535                               p, ucontext->text_end,
536                               G_MARKUP_ERROR_PARSE,
537                               _("Character '%s' is not valid "
538                                 "inside an entity name"),
539                               utf8_str (p, ubuf));
540           break;
541         }
542
543       p = g_utf8_next_char (p);
544     }
545
546   if (ucontext->context->state != STATE_ERROR)
547     {
548       if (p != ucontext->text_end)
549         {
550           gint len = p - ucontext->entity_start;
551
552           /* move to after semicolon */
553           p = g_utf8_next_char (p);
554           ucontext->state = USTATE_INSIDE_TEXT;
555
556           if (strncmp (ucontext->entity_start, "lt", len) == 0)
557             g_string_append_c (ucontext->str, '<');
558           else if (strncmp (ucontext->entity_start, "gt", len) == 0)
559             g_string_append_c (ucontext->str, '>');
560           else if (strncmp (ucontext->entity_start, "amp", len) == 0)
561             g_string_append_c (ucontext->str, '&');
562           else if (strncmp (ucontext->entity_start, "quot", len) == 0)
563             g_string_append_c (ucontext->str, '"');
564           else if (strncmp (ucontext->entity_start, "apos", len) == 0)
565             g_string_append_c (ucontext->str, '\'');
566           else
567             {
568               gchar *name;
569
570               name = g_strndup (ucontext->entity_start, len);
571               set_unescape_error (ucontext->context, error,
572                                   p, ucontext->text_end,
573                                   G_MARKUP_ERROR_PARSE,
574                                   _("Entity name '%s' is not known"),
575                                   name);
576               g_free (name);
577             }
578         }
579       else
580         {
581           set_unescape_error (ucontext->context, error,
582                               /* give line number of the & */
583                               ucontext->entity_start, ucontext->text_end,
584                               G_MARKUP_ERROR_PARSE,
585                               _("Entity did not end with a semicolon; "
586                                 "most likely you used an ampersand "
587                                 "character without intending to start "
588                                 "an entity - escape ampersand as &amp;"));
589         }
590     }
591 #undef MAX_ENT_LEN
592
593   return p;
594 }
595
596 static const gchar*
597 unescape_text_state_after_charref_hash (UnescapeContext *ucontext,
598                                         const gchar     *p,
599                                         GError         **error)
600 {
601   gboolean is_hex = FALSE;
602   const char *start;
603
604   start = ucontext->entity_start;
605
606   if (*p == 'x')
607     {
608       is_hex = TRUE;
609       p = g_utf8_next_char (p);
610       start = p;
611     }
612
613   while (p != ucontext->text_end && *p != ';')
614     p = g_utf8_next_char (p);
615
616   if (p != ucontext->text_end)
617     {
618       g_assert (*p == ';');
619
620       /* digit is between start and p */
621
622       if (start != p)
623         {
624           gulong l;
625           gchar *end = NULL;
626                     
627           errno = 0;
628           if (is_hex)
629             l = strtoul (start, &end, 16);
630           else
631             l = strtoul (start, &end, 10);
632
633           if (end != p || errno != 0)
634             {
635               set_unescape_error (ucontext->context, error,
636                                   start, ucontext->text_end,
637                                   G_MARKUP_ERROR_PARSE,
638                                   _("Failed to parse '%-.*s', which "
639                                     "should have been a digit "
640                                     "inside a character reference "
641                                     "(&#234; for example) - perhaps "
642                                     "the digit is too large"),
643                                   p - start, start);
644             }
645           else
646             {
647               /* characters XML 1.1 permits */
648               if ((0 < l && l <= 0xD7FF) ||
649                   (0xE000 <= l && l <= 0xFFFD) ||
650                   (0x10000 <= l && l <= 0x10FFFF))
651                 {
652                   gchar buf[8];
653                   g_string_append (ucontext->str, char_str (l, buf));
654                 }
655               else
656                 {
657                   set_unescape_error (ucontext->context, error,
658                                       start, ucontext->text_end,
659                                       G_MARKUP_ERROR_PARSE,
660                                       _("Character reference '%-.*s' does not "
661                                         "encode a permitted character"),
662                                       p - start, start);
663                 }
664             }
665
666           /* Move to next state */
667           p = g_utf8_next_char (p); /* past semicolon */
668           ucontext->state = USTATE_INSIDE_TEXT;
669         }
670       else
671         {
672           set_unescape_error (ucontext->context, error,
673                               start, ucontext->text_end,
674                               G_MARKUP_ERROR_PARSE,
675                               _("Empty character reference; "
676                                 "should include a digit such as "
677                                 "&#454;"));
678         }
679     }
680   else
681     {
682       set_unescape_error (ucontext->context, error,
683                           start, ucontext->text_end,
684                           G_MARKUP_ERROR_PARSE,
685                           _("Character reference did not end with a "
686                             "semicolon; "
687                             "most likely you used an ampersand "
688                             "character without intending to start "
689                             "an entity - escape ampersand as &amp;"));
690     }
691
692   return p;
693 }
694
695 static gboolean
696 unescape_text (GMarkupParseContext *context,
697                const gchar         *text,
698                const gchar         *text_end,
699                GString            **unescaped,
700                GError             **error)
701 {
702   UnescapeContext ucontext;
703   const gchar *p;
704
705   ucontext.context = context;
706   ucontext.text = text;
707   ucontext.text_end = text_end;
708   ucontext.entity_start = NULL;
709   
710   ucontext.str = g_string_sized_new (text_end - text);
711
712   ucontext.state = USTATE_INSIDE_TEXT;
713   p = text;
714
715   while (p != text_end && context->state != STATE_ERROR)
716     {
717       g_assert (p < text_end);
718       
719       switch (ucontext.state)
720         {
721         case USTATE_INSIDE_TEXT:
722           {
723             p = unescape_text_state_inside_text (&ucontext,
724                                                  p,
725                                                  error);
726           }
727           break;
728
729         case USTATE_AFTER_AMPERSAND:
730           {
731             p = unescape_text_state_after_ampersand (&ucontext,
732                                                      p,
733                                                      error);
734           }
735           break;
736
737
738         case USTATE_INSIDE_ENTITY_NAME:
739           {
740             p = unescape_text_state_inside_entity_name (&ucontext,
741                                                         p,
742                                                         error);
743           }
744           break;
745
746         case USTATE_AFTER_CHARREF_HASH:
747           {
748             p = unescape_text_state_after_charref_hash (&ucontext,
749                                                         p,
750                                                         error);
751           }
752           break;
753
754         default:
755           g_assert_not_reached ();
756           break;
757         }
758     }
759
760   if (context->state != STATE_ERROR) 
761     {
762       switch (ucontext.state) 
763         {
764         case USTATE_INSIDE_TEXT:
765           break;
766         case USTATE_AFTER_AMPERSAND:
767         case USTATE_INSIDE_ENTITY_NAME:
768           set_unescape_error (context, error,
769                               NULL, NULL,
770                               G_MARKUP_ERROR_PARSE,
771                               _("Unfinished entity reference"));
772           break;
773         case USTATE_AFTER_CHARREF_HASH:
774           set_unescape_error (context, error,
775                               NULL, NULL,
776                               G_MARKUP_ERROR_PARSE,
777                               _("Unfinished character reference"));
778           break;
779         }
780     }
781
782   if (context->state == STATE_ERROR)
783     {
784       g_string_free (ucontext.str, TRUE);
785       *unescaped = NULL;
786       return FALSE;
787     }
788   else
789     {
790       *unescaped = ucontext.str;
791       return TRUE;
792     }
793 }
794
795 static inline gboolean
796 advance_char (GMarkupParseContext *context)
797 {  
798   context->iter = g_utf8_next_char (context->iter);
799   context->char_number += 1;
800
801   if (context->iter == context->current_text_end)
802     {
803       return FALSE;
804     }
805   else if (*context->iter == '\n')
806     {
807       context->line_number += 1;
808       context->char_number = 1;
809     }
810   
811   return TRUE;
812 }
813
814 static inline gboolean
815 xml_isspace (char c)
816 {
817   return c == ' ' || c == '\t' || c == '\n' || c == '\r';
818 }
819
820 static void
821 skip_spaces (GMarkupParseContext *context)
822 {
823   do
824     {
825       if (!xml_isspace (*context->iter))
826         return;
827     }
828   while (advance_char (context));
829 }
830
831 static void
832 advance_to_name_end (GMarkupParseContext *context)
833 {
834   do
835     {
836       if (!is_name_char (context->iter))
837         return;
838     }
839   while (advance_char (context));
840 }
841
842 static void
843 add_to_partial (GMarkupParseContext *context,
844                 const gchar         *text_start,
845                 const gchar         *text_end)
846 {
847   if (context->partial_chunk == NULL)
848     context->partial_chunk = g_string_sized_new (text_end - text_start);
849
850   if (text_start != text_end)
851     g_string_append_len (context->partial_chunk, text_start,
852                          text_end - text_start);
853
854   /* Invariant here that partial_chunk exists */
855 }
856
857 static void
858 truncate_partial (GMarkupParseContext *context)
859 {
860   if (context->partial_chunk != NULL)
861     {
862       context->partial_chunk = g_string_truncate (context->partial_chunk, 0);
863     }
864 }
865
866 static const gchar*
867 current_element (GMarkupParseContext *context)
868 {
869   return context->tag_stack->data;
870 }
871
872 static void
873 pop_subparser_stack (GMarkupParseContext *context)
874 {
875   GMarkupRecursionTracker *tracker;
876
877   g_assert (context->subparser_stack);
878
879   tracker = context->subparser_stack->data;
880
881   context->awaiting_pop = TRUE;
882   context->held_user_data = context->user_data;
883
884   context->user_data = tracker->prev_user_data;
885   context->parser = tracker->prev_parser;
886   context->subparser_element = tracker->prev_element;
887   g_slice_free (GMarkupRecursionTracker, tracker);
888
889   context->subparser_stack = g_slist_delete_link (context->subparser_stack,
890                                                   context->subparser_stack);
891 }
892
893 static void
894 possibly_finish_subparser (GMarkupParseContext *context)
895 {
896   if (current_element (context) == context->subparser_element)
897     pop_subparser_stack (context);
898 }
899
900 static void
901 ensure_no_outstanding_subparser (GMarkupParseContext *context)
902 {
903   if (context->awaiting_pop)
904     g_critical ("During the first end_element call after invoking a "
905                 "subparser you must pop the subparser stack and handle "
906                 "the freeing of the subparser user_data.  This can be "
907                 "done by calling the end function of the subparser.  "
908                 "Very probably, your program just leaked memory.");
909
910   /* let valgrind watch the pointer disappear... */
911   context->held_user_data = NULL;
912   context->awaiting_pop = FALSE;
913 }
914
915 static const gchar*
916 current_attribute (GMarkupParseContext *context)
917 {
918   g_assert (context->cur_attr >= 0);
919   return context->attr_names[context->cur_attr];
920 }
921
922 static void
923 find_current_text_end (GMarkupParseContext *context)
924 {
925   /* This function must be safe (non-segfaulting) on invalid UTF8.
926    * It assumes the string starts with a character start
927    */
928   const gchar *end = context->current_text + context->current_text_len;
929   const gchar *p;
930   const gchar *next;
931
932   g_assert (context->current_text_len > 0);
933
934   p = g_utf8_find_prev_char (context->current_text, end);
935
936   g_assert (p != NULL); /* since current_text was a char start */
937
938   /* p is now the start of the last character or character portion. */
939   g_assert (p != end);
940   next = g_utf8_next_char (p); /* this only touches *p, nothing beyond */
941
942   if (next == end)
943     {
944       /* whole character */
945       context->current_text_end = end;
946     }
947   else
948     {
949       /* portion */
950       context->leftover_char_portion = g_string_new_len (p, end - p);
951       context->current_text_len -= (end - p);
952       context->current_text_end = p;
953     }
954 }
955
956
957 static void
958 add_attribute (GMarkupParseContext *context, char *name)
959 {
960   if (context->cur_attr + 2 >= context->alloc_attrs)
961     {
962       context->alloc_attrs += 5; /* silly magic number */
963       context->attr_names = g_realloc (context->attr_names, sizeof(char*)*context->alloc_attrs);
964       context->attr_values = g_realloc (context->attr_values, sizeof(char*)*context->alloc_attrs);
965     }
966   context->cur_attr++;
967   context->attr_names[context->cur_attr] = name;
968   context->attr_values[context->cur_attr] = NULL;
969   context->attr_names[context->cur_attr+1] = NULL;
970   context->attr_values[context->cur_attr+1] = NULL;
971 }
972
973 /**
974  * g_markup_parse_context_parse:
975  * @context: a #GMarkupParseContext
976  * @text: chunk of text to parse
977  * @text_len: length of @text in bytes
978  * @error: return location for a #GError
979  * 
980  * Feed some data to the #GMarkupParseContext. The data need not
981  * be valid UTF-8; an error will be signaled if it's invalid.
982  * The data need not be an entire document; you can feed a document
983  * into the parser incrementally, via multiple calls to this function.
984  * Typically, as you receive data from a network connection or file,
985  * you feed each received chunk of data into this function, aborting
986  * the process if an error occurs. Once an error is reported, no further
987  * data may be fed to the #GMarkupParseContext; all errors are fatal.
988  * 
989  * Return value: %FALSE if an error occurred, %TRUE on success
990  **/
991 gboolean
992 g_markup_parse_context_parse (GMarkupParseContext *context,
993                               const gchar         *text,
994                               gssize               text_len,
995                               GError             **error)
996 {
997   const gchar *first_invalid;
998   
999   g_return_val_if_fail (context != NULL, FALSE);
1000   g_return_val_if_fail (text != NULL, FALSE);
1001   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
1002   g_return_val_if_fail (!context->parsing, FALSE);
1003   
1004   if (text_len < 0)
1005     text_len = strlen (text);
1006
1007   if (text_len == 0)
1008     return TRUE;
1009   
1010   context->parsing = TRUE;
1011   
1012   if (context->leftover_char_portion)
1013     {
1014       const gchar *first_char;
1015
1016       if ((*text & 0xc0) != 0x80)
1017         first_char = text;
1018       else
1019         first_char = g_utf8_find_next_char (text, text + text_len);
1020
1021       if (first_char)
1022         {
1023           /* leftover_char_portion was completed. Parse it. */
1024           GString *portion = context->leftover_char_portion;
1025           
1026           g_string_append_len (context->leftover_char_portion,
1027                                text, first_char - text);
1028
1029           /* hacks to allow recursion */
1030           context->parsing = FALSE;
1031           context->leftover_char_portion = NULL;
1032           
1033           if (!g_markup_parse_context_parse (context,
1034                                              portion->str, portion->len,
1035                                              error))
1036             {
1037               g_assert (context->state == STATE_ERROR);
1038             }
1039           
1040           g_string_free (portion, TRUE);
1041           context->parsing = TRUE;
1042
1043           /* Skip the fraction of char that was in this text */
1044           text_len -= (first_char - text);
1045           text = first_char;
1046         }
1047       else
1048         {
1049           /* another little chunk of the leftover char; geez
1050            * someone is inefficient.
1051            */
1052           g_string_append_len (context->leftover_char_portion,
1053                                text, text_len);
1054
1055           if (context->leftover_char_portion->len > 7)
1056             {
1057               /* The leftover char portion is too big to be
1058                * a UTF-8 character
1059                */
1060               set_error_literal (context,
1061                                  error,
1062                                  G_MARKUP_ERROR_BAD_UTF8,
1063                                  _("Invalid UTF-8 encoded text - overlong sequence"));
1064             }
1065           
1066           goto finished;
1067         }
1068     }
1069
1070   context->current_text = text;
1071   context->current_text_len = text_len;
1072   context->iter = context->current_text;
1073   context->start = context->iter;
1074
1075   /* Nothing left after finishing the leftover char, or nothing
1076    * passed in to begin with.
1077    */
1078   if (context->current_text_len == 0)
1079     goto finished;
1080
1081   /* find_current_text_end () assumes the string starts at
1082    * a character start, so we need to validate at least
1083    * that much. It doesn't assume any following bytes
1084    * are valid.
1085    */
1086   if ((*context->current_text & 0xc0) == 0x80) /* not a char start */
1087     {
1088       set_error_literal (context,
1089                          error,
1090                          G_MARKUP_ERROR_BAD_UTF8,
1091                          _("Invalid UTF-8 encoded text - not a start char"));
1092       goto finished;
1093     }
1094
1095   /* Initialize context->current_text_end, possibly adjusting
1096    * current_text_len, and add any leftover char portion
1097    */
1098   find_current_text_end (context);
1099
1100   /* Validate UTF8 (must be done after we find the end, since
1101    * we could have a trailing incomplete char)
1102    */
1103   if (!g_utf8_validate (context->current_text,
1104                         context->current_text_len,
1105                         &first_invalid))
1106     {
1107       gint newlines = 0;
1108       const gchar *p, *q;
1109       gchar *current_text_dup;
1110
1111       q = p = context->current_text;
1112       while (p != first_invalid)
1113         {
1114           if (*p == '\n')
1115             {
1116               ++newlines;
1117               q = p + 1;
1118               context->char_number = 1;
1119             }
1120           ++p;
1121         }
1122
1123       context->line_number += newlines;
1124       context->char_number += g_utf8_strlen (q, first_invalid - q);
1125
1126       current_text_dup = g_strndup (context->current_text, context->current_text_len);
1127       set_error (context,
1128                  error,
1129                  G_MARKUP_ERROR_BAD_UTF8,
1130                  _("Invalid UTF-8 encoded text - not valid '%s'"),
1131                  current_text_dup);
1132       g_free (current_text_dup);
1133       goto finished;
1134     }
1135
1136   while (context->iter != context->current_text_end)
1137     {
1138       switch (context->state)
1139         {
1140         case STATE_START:
1141           /* Possible next state: AFTER_OPEN_ANGLE */
1142
1143           g_assert (context->tag_stack == NULL);
1144
1145           /* whitespace is ignored outside of any elements */
1146           skip_spaces (context);
1147
1148           if (context->iter != context->current_text_end)
1149             {
1150               if (*context->iter == '<')
1151                 {
1152                   /* Move after the open angle */
1153                   advance_char (context);
1154
1155                   context->state = STATE_AFTER_OPEN_ANGLE;
1156
1157                   /* this could start a passthrough */
1158                   context->start = context->iter;
1159
1160                   /* document is now non-empty */
1161                   context->document_empty = FALSE;
1162                 }
1163               else
1164                 {
1165                   set_error_literal (context,
1166                                      error,
1167                                      G_MARKUP_ERROR_PARSE,
1168                                      _("Document must begin with an element (e.g. <book>)"));
1169                 }
1170             }
1171           break;
1172
1173         case STATE_AFTER_OPEN_ANGLE:
1174           /* Possible next states: INSIDE_OPEN_TAG_NAME,
1175            *  AFTER_CLOSE_TAG_SLASH, INSIDE_PASSTHROUGH
1176            */
1177           if (*context->iter == '?' ||
1178               *context->iter == '!')
1179             {
1180               /* include < in the passthrough */
1181               const gchar *openangle = "<";
1182               add_to_partial (context, openangle, openangle + 1);
1183               context->start = context->iter;
1184               context->balance = 1;
1185               context->state = STATE_INSIDE_PASSTHROUGH;
1186             }
1187           else if (*context->iter == '/')
1188             {
1189               /* move after it */
1190               advance_char (context);
1191
1192               context->state = STATE_AFTER_CLOSE_TAG_SLASH;
1193             }
1194           else if (is_name_start_char (context->iter))
1195             {
1196               context->state = STATE_INSIDE_OPEN_TAG_NAME;
1197
1198               /* start of tag name */
1199               context->start = context->iter;
1200             }
1201           else
1202             {
1203               gchar buf[8];
1204
1205               set_error (context,
1206                          error,
1207                          G_MARKUP_ERROR_PARSE,
1208                          _("'%s' is not a valid character following "
1209                            "a '<' character; it may not begin an "
1210                            "element name"),
1211                          utf8_str (context->iter, buf));
1212             }
1213           break;
1214
1215           /* The AFTER_CLOSE_ANGLE state is actually sort of
1216            * broken, because it doesn't correspond to a range
1217            * of characters in the input stream as the others do,
1218            * and thus makes things harder to conceptualize
1219            */
1220         case STATE_AFTER_CLOSE_ANGLE:
1221           /* Possible next states: INSIDE_TEXT, STATE_START */
1222           if (context->tag_stack == NULL)
1223             {
1224               context->start = NULL;
1225               context->state = STATE_START;
1226             }
1227           else
1228             {
1229               context->start = context->iter;
1230               context->state = STATE_INSIDE_TEXT;
1231             }
1232           break;
1233
1234         case STATE_AFTER_ELISION_SLASH:
1235           /* Possible next state: AFTER_CLOSE_ANGLE */
1236
1237           {
1238             /* We need to pop the tag stack and call the end_element
1239              * function, since this is the close tag
1240              */
1241             GError *tmp_error = NULL;
1242           
1243             g_assert (context->tag_stack != NULL);
1244
1245             possibly_finish_subparser (context);
1246
1247             tmp_error = NULL;
1248             if (context->parser->end_element)
1249               (* context->parser->end_element) (context,
1250                                                 context->tag_stack->data,
1251                                                 context->user_data,
1252                                                 &tmp_error);
1253
1254             ensure_no_outstanding_subparser (context);
1255           
1256             if (tmp_error)
1257               {
1258                 mark_error (context, tmp_error);
1259                 g_propagate_error (error, tmp_error);
1260               }          
1261             else
1262               {
1263                 if (*context->iter == '>')
1264                   {
1265                     /* move after the close angle */
1266                     advance_char (context);
1267                     context->state = STATE_AFTER_CLOSE_ANGLE;
1268                   }
1269                 else
1270                   {
1271                     gchar buf[8];
1272
1273                     set_error (context,
1274                                error,
1275                                G_MARKUP_ERROR_PARSE,
1276                                _("Odd character '%s', expected a '>' character "
1277                                  "to end the empty-element tag '%s'"),
1278                                utf8_str (context->iter, buf),
1279                                current_element (context));
1280                   }
1281               }
1282
1283             g_free (context->tag_stack->data);
1284             context->tag_stack = g_slist_delete_link (context->tag_stack,
1285                                                       context->tag_stack);
1286           }
1287           break;
1288
1289         case STATE_INSIDE_OPEN_TAG_NAME:
1290           /* Possible next states: BETWEEN_ATTRIBUTES */
1291
1292           /* if there's a partial chunk then it's the first part of the
1293            * tag name. If there's a context->start then it's the start
1294            * of the tag name in current_text, the partial chunk goes
1295            * before that start though.
1296            */
1297           advance_to_name_end (context);
1298
1299           if (context->iter == context->current_text_end)
1300             {
1301               /* The name hasn't necessarily ended. Merge with
1302                * partial chunk, leave state unchanged.
1303                */
1304               add_to_partial (context, context->start, context->iter);
1305             }
1306           else
1307             {
1308               /* The name has ended. Combine it with the partial chunk
1309                * if any; push it on the stack; enter next state.
1310                */
1311               add_to_partial (context, context->start, context->iter);
1312               context->tag_stack =
1313                 g_slist_prepend (context->tag_stack,
1314                                  g_string_free (context->partial_chunk,
1315                                                 FALSE));
1316
1317               context->partial_chunk = NULL;
1318
1319               context->state = STATE_BETWEEN_ATTRIBUTES;
1320               context->start = NULL;
1321             }
1322           break;
1323
1324         case STATE_INSIDE_ATTRIBUTE_NAME:
1325           /* Possible next states: AFTER_ATTRIBUTE_NAME */
1326
1327           advance_to_name_end (context);
1328           add_to_partial (context, context->start, context->iter);
1329
1330           /* read the full name, if we enter the equals sign state
1331            * then add the attribute to the list (without the value),
1332            * otherwise store a partial chunk to be prepended later.
1333            */
1334           if (context->iter != context->current_text_end)
1335             context->state = STATE_AFTER_ATTRIBUTE_NAME;
1336           break;
1337
1338         case STATE_AFTER_ATTRIBUTE_NAME:
1339           /* Possible next states: AFTER_ATTRIBUTE_EQUALS_SIGN */
1340
1341           skip_spaces (context);
1342
1343           if (context->iter != context->current_text_end)
1344             {
1345               /* The name has ended. Combine it with the partial chunk
1346                * if any; push it on the stack; enter next state.
1347                */
1348               add_attribute (context, g_string_free (context->partial_chunk, FALSE));
1349               
1350               context->partial_chunk = NULL;
1351               context->start = NULL;
1352               
1353               if (*context->iter == '=')
1354                 {
1355                   advance_char (context);
1356                   context->state = STATE_AFTER_ATTRIBUTE_EQUALS_SIGN;
1357                 }
1358               else
1359                 {
1360                   gchar buf[8];
1361
1362                   set_error (context,
1363                              error,
1364                              G_MARKUP_ERROR_PARSE,
1365                              _("Odd character '%s', expected a '=' after "
1366                                "attribute name '%s' of element '%s'"),
1367                              utf8_str (context->iter, buf),
1368                              current_attribute (context),
1369                              current_element (context));
1370                   
1371                 }
1372             }
1373           break;
1374
1375         case STATE_BETWEEN_ATTRIBUTES:
1376           /* Possible next states: AFTER_CLOSE_ANGLE,
1377            * AFTER_ELISION_SLASH, INSIDE_ATTRIBUTE_NAME
1378            */
1379           skip_spaces (context);
1380
1381           if (context->iter != context->current_text_end)
1382             {
1383               if (*context->iter == '/')
1384                 {
1385                   advance_char (context);
1386                   context->state = STATE_AFTER_ELISION_SLASH;
1387                 }
1388               else if (*context->iter == '>')
1389                 {
1390
1391                   advance_char (context);
1392                   context->state = STATE_AFTER_CLOSE_ANGLE;
1393                 }
1394               else if (is_name_start_char (context->iter))
1395                 {
1396                   context->state = STATE_INSIDE_ATTRIBUTE_NAME;
1397                   /* start of attribute name */
1398                   context->start = context->iter;
1399                 }
1400               else
1401                 {
1402                   gchar buf[8];
1403
1404                   set_error (context,
1405                              error,
1406                              G_MARKUP_ERROR_PARSE,
1407                              _("Odd character '%s', expected a '>' or '/' "
1408                                "character to end the start tag of "
1409                                "element '%s', or optionally an attribute; "
1410                                "perhaps you used an invalid character in "
1411                                "an attribute name"),
1412                              utf8_str (context->iter, buf),
1413                              current_element (context));
1414                 }
1415
1416               /* If we're done with attributes, invoke
1417                * the start_element callback
1418                */
1419               if (context->state == STATE_AFTER_ELISION_SLASH ||
1420                   context->state == STATE_AFTER_CLOSE_ANGLE)
1421                 {
1422                   const gchar *start_name;
1423                   /* Ugly, but the current code expects an empty array instead of NULL */
1424                   const gchar *empty = NULL;
1425                   const gchar **attr_names =  &empty;
1426                   const gchar **attr_values = &empty;
1427                   GError *tmp_error;
1428
1429                   /* Call user callback for element start */
1430                   start_name = current_element (context);
1431
1432                   if (context->cur_attr >= 0)
1433                     {
1434                       attr_names = (const gchar**)context->attr_names;
1435                       attr_values = (const gchar**)context->attr_values;
1436                     }
1437
1438                   tmp_error = NULL;
1439                   if (context->parser->start_element)
1440                     (* context->parser->start_element) (context,
1441                                                         start_name,
1442                                                         (const gchar **)attr_names,
1443                                                         (const gchar **)attr_values,
1444                                                         context->user_data,
1445                                                         &tmp_error);
1446
1447                   /* Go ahead and free the attributes. */
1448                   for (; context->cur_attr >= 0; context->cur_attr--)
1449                     {
1450                       int pos = context->cur_attr;
1451                       g_free (context->attr_names[pos]);
1452                       g_free (context->attr_values[pos]);
1453                       context->attr_names[pos] = context->attr_values[pos] = NULL;
1454                     }
1455                   g_assert (context->cur_attr == -1);
1456                   g_assert (context->attr_names == NULL ||
1457                             context->attr_names[0] == NULL);
1458                   g_assert (context->attr_values == NULL ||
1459                             context->attr_values[0] == NULL);
1460                   
1461                   if (tmp_error != NULL)
1462                     propagate_error (context, error, tmp_error);
1463                 }
1464             }
1465           break;
1466
1467         case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1468           /* Possible next state: INSIDE_ATTRIBUTE_VALUE_[SQ/DQ] */
1469
1470           skip_spaces (context);
1471
1472           if (context->iter != context->current_text_end)
1473             {
1474               if (*context->iter == '"')
1475                 {
1476                   advance_char (context);
1477                   context->state = STATE_INSIDE_ATTRIBUTE_VALUE_DQ;
1478                   context->start = context->iter;
1479                 }
1480               else if (*context->iter == '\'')
1481                 {
1482                   advance_char (context);
1483                   context->state = STATE_INSIDE_ATTRIBUTE_VALUE_SQ;
1484                   context->start = context->iter;
1485                 }
1486               else
1487                 {
1488                   gchar buf[8];
1489                   
1490                   set_error (context,
1491                              error,
1492                              G_MARKUP_ERROR_PARSE,
1493                              _("Odd character '%s', expected an open quote mark "
1494                                "after the equals sign when giving value for "
1495                                "attribute '%s' of element '%s'"),
1496                              utf8_str (context->iter, buf),
1497                              current_attribute (context),
1498                              current_element (context));
1499                 }
1500             }
1501           break;
1502
1503         case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1504         case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1505           /* Possible next states: BETWEEN_ATTRIBUTES */
1506           {
1507             gchar delim;
1508
1509             if (context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ) 
1510               {
1511                 delim = '\'';
1512               }
1513             else 
1514               {
1515                 delim = '"';
1516               }
1517
1518             do
1519               {
1520                 if (*context->iter == delim)
1521                   break;
1522               }
1523             while (advance_char (context));
1524           }
1525           if (context->iter == context->current_text_end)
1526             {
1527               /* The value hasn't necessarily ended. Merge with
1528                * partial chunk, leave state unchanged.
1529                */
1530               add_to_partial (context, context->start, context->iter);
1531             }
1532           else
1533             {
1534               /* The value has ended at the quote mark. Combine it
1535                * with the partial chunk if any; set it for the current
1536                * attribute.
1537                */
1538               GString *unescaped;
1539               
1540               add_to_partial (context, context->start, context->iter);
1541
1542               g_assert (context->cur_attr >= 0);
1543               
1544               if (unescape_text (context,
1545                                  context->partial_chunk->str,
1546                                  context->partial_chunk->str +
1547                                  context->partial_chunk->len,
1548                                  &unescaped,
1549                                  error))
1550                 {
1551                   /* success, advance past quote and set state. */
1552                   context->attr_values[context->cur_attr] = g_string_free (unescaped, FALSE);
1553                   advance_char (context);
1554                   context->state = STATE_BETWEEN_ATTRIBUTES;
1555                   context->start = NULL;
1556                 }
1557               
1558               truncate_partial (context);
1559             }
1560           break;
1561
1562         case STATE_INSIDE_TEXT:
1563           /* Possible next states: AFTER_OPEN_ANGLE */
1564           do
1565             {
1566               if (*context->iter == '<')
1567                 break;
1568             }
1569           while (advance_char (context));
1570
1571           /* The text hasn't necessarily ended. Merge with
1572            * partial chunk, leave state unchanged.
1573            */
1574
1575           add_to_partial (context, context->start, context->iter);
1576
1577           if (context->iter != context->current_text_end)
1578             {
1579               GString *unescaped = NULL;
1580
1581               /* The text has ended at the open angle. Call the text
1582                * callback.
1583                */
1584               
1585               if (unescape_text (context,
1586                                  context->partial_chunk->str,
1587                                  context->partial_chunk->str +
1588                                  context->partial_chunk->len,
1589                                  &unescaped,
1590                                  error))
1591                 {
1592                   GError *tmp_error = NULL;
1593
1594                   if (context->parser->text)
1595                     (*context->parser->text) (context,
1596                                               unescaped->str,
1597                                               unescaped->len,
1598                                               context->user_data,
1599                                               &tmp_error);
1600                   
1601                   g_string_free (unescaped, TRUE);
1602
1603                   if (tmp_error == NULL)
1604                     {
1605                       /* advance past open angle and set state. */
1606                       advance_char (context);
1607                       context->state = STATE_AFTER_OPEN_ANGLE;
1608                       /* could begin a passthrough */
1609                       context->start = context->iter;
1610                     }
1611                   else
1612                     propagate_error (context, error, tmp_error);
1613                 }
1614
1615               truncate_partial (context);
1616             }
1617           break;
1618
1619         case STATE_AFTER_CLOSE_TAG_SLASH:
1620           /* Possible next state: INSIDE_CLOSE_TAG_NAME */
1621           if (is_name_start_char (context->iter))
1622             {
1623               context->state = STATE_INSIDE_CLOSE_TAG_NAME;
1624
1625               /* start of tag name */
1626               context->start = context->iter;
1627             }
1628           else
1629             {
1630               gchar buf[8];
1631
1632               set_error (context,
1633                          error,
1634                          G_MARKUP_ERROR_PARSE,
1635                          _("'%s' is not a valid character following "
1636                            "the characters '</'; '%s' may not begin an "
1637                            "element name"),
1638                          utf8_str (context->iter, buf),
1639                          utf8_str (context->iter, buf));
1640             }
1641           break;
1642
1643         case STATE_INSIDE_CLOSE_TAG_NAME:
1644           /* Possible next state: AFTER_CLOSE_TAG_NAME */
1645           advance_to_name_end (context);
1646           add_to_partial (context, context->start, context->iter);
1647
1648           if (context->iter != context->current_text_end)
1649             context->state = STATE_AFTER_CLOSE_TAG_NAME;
1650           break;
1651
1652         case STATE_AFTER_CLOSE_TAG_NAME:
1653           /* Possible next state: AFTER_CLOSE_TAG_SLASH */
1654
1655           skip_spaces (context);
1656           
1657           if (context->iter != context->current_text_end)
1658             {
1659               gchar *close_name;
1660
1661               /* The name has ended. Combine it with the partial chunk
1662                * if any; check that it matches stack top and pop
1663                * stack; invoke proper callback; enter next state.
1664                */
1665               close_name = g_string_free (context->partial_chunk, FALSE);
1666               context->partial_chunk = NULL;
1667               
1668               if (*context->iter != '>')
1669                 {
1670                   gchar buf[8];
1671
1672                   set_error (context,
1673                              error,
1674                              G_MARKUP_ERROR_PARSE,
1675                              _("'%s' is not a valid character following "
1676                                "the close element name '%s'; the allowed "
1677                                "character is '>'"),
1678                              utf8_str (context->iter, buf),
1679                              close_name);
1680                 }
1681               else if (context->tag_stack == NULL)
1682                 {
1683                   set_error (context,
1684                              error,
1685                              G_MARKUP_ERROR_PARSE,
1686                              _("Element '%s' was closed, no element "
1687                                "is currently open"),
1688                              close_name);
1689                 }
1690               else if (strcmp (close_name, current_element (context)) != 0)
1691                 {
1692                   set_error (context,
1693                              error,
1694                              G_MARKUP_ERROR_PARSE,
1695                              _("Element '%s' was closed, but the currently "
1696                                "open element is '%s'"),
1697                              close_name,
1698                              current_element (context));
1699                 }
1700               else
1701                 {
1702                   GError *tmp_error;
1703                   advance_char (context);
1704                   context->state = STATE_AFTER_CLOSE_ANGLE;
1705                   context->start = NULL;
1706                   
1707                   possibly_finish_subparser (context);
1708
1709                   /* call the end_element callback */
1710                   tmp_error = NULL;
1711                   if (context->parser->end_element)
1712                     (* context->parser->end_element) (context,
1713                                                       close_name,
1714                                                       context->user_data,
1715                                                       &tmp_error);
1716                   
1717                   ensure_no_outstanding_subparser (context);
1718                   
1719                   /* Pop the tag stack */
1720                   g_free (context->tag_stack->data);
1721                   context->tag_stack = g_slist_delete_link (context->tag_stack,
1722                                                             context->tag_stack);
1723                   
1724                   if (tmp_error)
1725                     propagate_error (context, error, tmp_error);
1726                 }
1727               
1728               g_free (close_name);
1729             }
1730           break;
1731           
1732         case STATE_INSIDE_PASSTHROUGH:
1733           /* Possible next state: AFTER_CLOSE_ANGLE */
1734           do
1735             {
1736               if (*context->iter == '<') 
1737                 context->balance++;
1738               if (*context->iter == '>') 
1739                 {                               
1740                   gchar *str;
1741                   gsize len;
1742
1743                   context->balance--;
1744                   add_to_partial (context, context->start, context->iter);
1745                   context->start = context->iter;
1746
1747                   str = context->partial_chunk->str;
1748                   len = context->partial_chunk->len;
1749
1750                   if (str[1] == '?' && str[len - 1] == '?')
1751                     break;
1752                   if (strncmp (str, "<!--", 4) == 0 && 
1753                       strcmp (str + len - 2, "--") == 0)
1754                     break;
1755                   if (strncmp (str, "<![CDATA[", 9) == 0 && 
1756                       strcmp (str + len - 2, "]]") == 0)
1757                     break;
1758                   if (strncmp (str, "<!DOCTYPE", 9) == 0 &&
1759                       context->balance == 0)
1760                     break;
1761                 }
1762             }
1763           while (advance_char (context));
1764
1765           if (context->iter == context->current_text_end)
1766             {
1767               /* The passthrough hasn't necessarily ended. Merge with
1768                * partial chunk, leave state unchanged.
1769                */
1770                add_to_partial (context, context->start, context->iter);
1771             }
1772           else
1773             {
1774               /* The passthrough has ended at the close angle. Combine
1775                * it with the partial chunk if any. Call the passthrough
1776                * callback. Note that the open/close angles are
1777                * included in the text of the passthrough.
1778                */
1779               GError *tmp_error = NULL;
1780
1781               advance_char (context); /* advance past close angle */
1782               add_to_partial (context, context->start, context->iter);
1783
1784               if (context->flags & G_MARKUP_TREAT_CDATA_AS_TEXT &&
1785                   strncmp (context->partial_chunk->str, "<![CDATA[", 9) == 0)
1786                 {
1787                   if (context->parser->text)
1788                     (*context->parser->text) (context,
1789                                               context->partial_chunk->str + 9,
1790                                               context->partial_chunk->len - 12,
1791                                               context->user_data,
1792                                               &tmp_error);
1793                 }
1794               else if (context->parser->passthrough)
1795                 (*context->parser->passthrough) (context,
1796                                                  context->partial_chunk->str,
1797                                                  context->partial_chunk->len,
1798                                                  context->user_data,
1799                                                  &tmp_error);
1800                   
1801               truncate_partial (context);
1802
1803               if (tmp_error == NULL)
1804                 {
1805                   context->state = STATE_AFTER_CLOSE_ANGLE;
1806                   context->start = context->iter; /* could begin text */
1807                 }
1808               else
1809                 propagate_error (context, error, tmp_error);
1810             }
1811           break;
1812
1813         case STATE_ERROR:
1814           goto finished;
1815           break;
1816
1817         default:
1818           g_assert_not_reached ();
1819           break;
1820         }
1821     }
1822
1823  finished:
1824   context->parsing = FALSE;
1825
1826   return context->state != STATE_ERROR;
1827 }
1828
1829 /**
1830  * g_markup_parse_context_end_parse:
1831  * @context: a #GMarkupParseContext
1832  * @error: return location for a #GError
1833  * 
1834  * Signals to the #GMarkupParseContext that all data has been
1835  * fed into the parse context with g_markup_parse_context_parse().
1836  * This function reports an error if the document isn't complete,
1837  * for example if elements are still open.
1838  * 
1839  * Return value: %TRUE on success, %FALSE if an error was set
1840  **/
1841 gboolean
1842 g_markup_parse_context_end_parse (GMarkupParseContext *context,
1843                                   GError             **error)
1844 {
1845   g_return_val_if_fail (context != NULL, FALSE);
1846   g_return_val_if_fail (!context->parsing, FALSE);
1847   g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
1848
1849   if (context->partial_chunk != NULL)
1850     {
1851       g_string_free (context->partial_chunk, TRUE);
1852       context->partial_chunk = NULL;
1853     }
1854
1855   if (context->document_empty)
1856     {
1857       set_error_literal (context, error, G_MARKUP_ERROR_EMPTY,
1858                          _("Document was empty or contained only whitespace"));
1859       return FALSE;
1860     }
1861   
1862   context->parsing = TRUE;
1863   
1864   switch (context->state)
1865     {
1866     case STATE_START:
1867       /* Nothing to do */
1868       break;
1869
1870     case STATE_AFTER_OPEN_ANGLE:
1871       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1872                          _("Document ended unexpectedly just after an open angle bracket '<'"));
1873       break;
1874
1875     case STATE_AFTER_CLOSE_ANGLE:
1876       if (context->tag_stack != NULL)
1877         {
1878           /* Error message the same as for INSIDE_TEXT */
1879           set_error (context, error, G_MARKUP_ERROR_PARSE,
1880                      _("Document ended unexpectedly with elements still open - "
1881                        "'%s' was the last element opened"),
1882                      current_element (context));
1883         }
1884       break;
1885       
1886     case STATE_AFTER_ELISION_SLASH:
1887       set_error (context, error, G_MARKUP_ERROR_PARSE,
1888                  _("Document ended unexpectedly, expected to see a close angle "
1889                    "bracket ending the tag <%s/>"), current_element (context));
1890       break;
1891
1892     case STATE_INSIDE_OPEN_TAG_NAME:
1893       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1894                          _("Document ended unexpectedly inside an element name"));
1895       break;
1896
1897     case STATE_INSIDE_ATTRIBUTE_NAME:
1898     case STATE_AFTER_ATTRIBUTE_NAME:
1899       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1900                          _("Document ended unexpectedly inside an attribute name"));
1901       break;
1902
1903     case STATE_BETWEEN_ATTRIBUTES:
1904       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1905                          _("Document ended unexpectedly inside an element-opening "
1906                            "tag."));
1907       break;
1908
1909     case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
1910       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1911                          _("Document ended unexpectedly after the equals sign "
1912                            "following an attribute name; no attribute value"));
1913       break;
1914
1915     case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
1916     case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
1917       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1918                          _("Document ended unexpectedly while inside an attribute "
1919                            "value"));
1920       break;
1921
1922     case STATE_INSIDE_TEXT:
1923       g_assert (context->tag_stack != NULL);
1924       set_error (context, error, G_MARKUP_ERROR_PARSE,
1925                  _("Document ended unexpectedly with elements still open - "
1926                    "'%s' was the last element opened"),
1927                  current_element (context));
1928       break;
1929
1930     case STATE_AFTER_CLOSE_TAG_SLASH:
1931     case STATE_INSIDE_CLOSE_TAG_NAME:
1932     case STATE_AFTER_CLOSE_TAG_NAME:
1933       set_error (context, error, G_MARKUP_ERROR_PARSE,
1934                  _("Document ended unexpectedly inside the close tag for "
1935                    "element '%s'"), current_element (context));
1936       break;
1937
1938     case STATE_INSIDE_PASSTHROUGH:
1939       set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
1940                          _("Document ended unexpectedly inside a comment or "
1941                            "processing instruction"));
1942       break;
1943
1944     case STATE_ERROR:
1945     default:
1946       g_assert_not_reached ();
1947       break;
1948     }
1949
1950   context->parsing = FALSE;
1951
1952   return context->state != STATE_ERROR;
1953 }
1954
1955 /**
1956  * g_markup_parse_context_get_element:
1957  * @context: a #GMarkupParseContext
1958  * @returns: the name of the currently open element, or %NULL
1959  *
1960  * Retrieves the name of the currently open element.
1961  *
1962  * If called from the start_element or end_element handlers this will
1963  * give the element_name as passed to those functions. For the parent
1964  * elements, see g_markup_parse_context_get_element_stack().
1965  *
1966  * Since: 2.2
1967  **/
1968 G_CONST_RETURN gchar *
1969 g_markup_parse_context_get_element (GMarkupParseContext *context)
1970 {
1971   g_return_val_if_fail (context != NULL, NULL);
1972
1973   if (context->tag_stack == NULL) 
1974     return NULL;
1975   else
1976     return current_element (context);
1977
1978
1979 /**
1980  * g_markup_parse_context_get_element_stack:
1981  * @context: a #GMarkupParseContext
1982  *
1983  * Retrieves the element stack from the internal state of the parser.
1984  * The returned #GSList is a list of strings where the first item is
1985  * the currently open tag (as would be returned by
1986  * g_markup_parse_context_get_element()) and the next item is its
1987  * immediate parent.
1988  *
1989  * This function is intended to be used in the start_element and
1990  * end_element handlers where g_markup_parse_context_get_element()
1991  * would merely return the name of the element that is being
1992  * processed.
1993  *
1994  * Returns: the element stack, which must not be modified
1995  *
1996  * Since: 2.16
1997  **/
1998 G_CONST_RETURN GSList *
1999 g_markup_parse_context_get_element_stack (GMarkupParseContext *context)
2000 {
2001   g_return_val_if_fail (context != NULL, NULL);
2002
2003   return context->tag_stack;
2004 }
2005
2006 /**
2007  * g_markup_parse_context_get_position:
2008  * @context: a #GMarkupParseContext
2009  * @line_number: return location for a line number, or %NULL
2010  * @char_number: return location for a char-on-line number, or %NULL
2011  *
2012  * Retrieves the current line number and the number of the character on
2013  * that line. Intended for use in error messages; there are no strict
2014  * semantics for what constitutes the "current" line number other than
2015  * "the best number we could come up with for error messages."
2016  * 
2017  **/
2018 void
2019 g_markup_parse_context_get_position (GMarkupParseContext *context,
2020                                      gint                *line_number,
2021                                      gint                *char_number)
2022 {
2023   g_return_if_fail (context != NULL);
2024
2025   if (line_number)
2026     *line_number = context->line_number;
2027
2028   if (char_number)
2029     *char_number = context->char_number;
2030 }
2031
2032 /**
2033  * g_markup_parse_context_get_user_data:
2034  * @context: a #GMarkupParseContext
2035  *
2036  * Returns the user_data associated with @context.  This will either
2037  * be the user_data that was provided to g_markup_parse_context_new()
2038  * or to the most recent call of g_markup_parse_context_push().
2039  *
2040  * Returns: the provided user_data. The returned data belongs to
2041  *     the markup context and will be freed when g_markup_context_free()
2042  *     is called.
2043  *
2044  * Since: 2.18
2045  **/
2046 gpointer
2047 g_markup_parse_context_get_user_data (GMarkupParseContext *context)
2048 {
2049   return context->user_data;
2050 }
2051
2052 /**
2053  * g_markup_parse_context_push:
2054  * @context: a #GMarkupParseContext
2055  * @parser: a #GMarkupParser
2056  * @user_data: user data to pass to #GMarkupParser functions
2057  *
2058  * Temporarily redirects markup data to a sub-parser.
2059  *
2060  * This function may only be called from the start_element handler of
2061  * a #GMarkupParser.  It must be matched with a corresponding call to
2062  * g_markup_parse_context_pop() in the matching end_element handler
2063  * (except in the case that the parser aborts due to an error).
2064  *
2065  * All tags, text and other data between the matching tags is
2066  * redirected to the subparser given by @parser.  @user_data is used
2067  * as the user_data for that parser.  @user_data is also passed to the
2068  * error callback in the event that an error occurs.  This includes
2069  * errors that occur in subparsers of the subparser.
2070  *
2071  * The end tag matching the start tag for which this call was made is
2072  * handled by the previous parser (which is given its own user_data)
2073  * which is why g_markup_parse_context_pop() is provided to allow "one
2074  * last access" to the @user_data provided to this function.  In the
2075  * case of error, the @user_data provided here is passed directly to
2076  * the error callback of the subparser and g_markup_parse_context()
2077  * should not be called.  In either case, if @user_data was allocated
2078  * then it ought to be freed from both of these locations.
2079  *
2080  * This function is not intended to be directly called by users
2081  * interested in invoking subparsers.  Instead, it is intended to be
2082  * used by the subparsers themselves to implement a higher-level
2083  * interface.
2084  *
2085  * As an example, see the following implementation of a simple
2086  * parser that counts the number of tags encountered.
2087  *
2088  * |[
2089  * typedef struct
2090  * {
2091  *   gint tag_count;
2092  * } CounterData;
2093  * 
2094  * static void
2095  * counter_start_element (GMarkupParseContext  *context,
2096  *                        const gchar          *element_name,
2097  *                        const gchar         **attribute_names,
2098  *                        const gchar         **attribute_values,
2099  *                        gpointer              user_data,
2100  *                        GError              **error)
2101  * {
2102  *   CounterData *data = user_data;
2103  * 
2104  *   data->tag_count++;
2105  * }
2106  * 
2107  * static void
2108  * counter_error (GMarkupParseContext *context,
2109  *                GError              *error,
2110  *                gpointer             user_data)
2111  * {
2112  *   CounterData *data = user_data;
2113  * 
2114  *   g_slice_free (CounterData, data);
2115  * }
2116  * 
2117  * static GMarkupParser counter_subparser =
2118  * {
2119  *   counter_start_element,
2120  *   NULL,
2121  *   NULL,
2122  *   NULL,
2123  *   counter_error
2124  * };
2125  * ]|
2126  *
2127  * In order to allow this parser to be easily used as a subparser, the
2128  * following interface is provided:
2129  *
2130  * |[
2131  * void
2132  * start_counting (GMarkupParseContext *context)
2133  * {
2134  *   CounterData *data = g_slice_new (CounterData);
2135  * 
2136  *   data->tag_count = 0;
2137  *   g_markup_parse_context_push (context, &counter_subparser, data);
2138  * }
2139  * 
2140  * gint
2141  * end_counting (GMarkupParseContext *context)
2142  * {
2143  *   CounterData *data = g_markup_parse_context_pop (context);
2144  *   int result;
2145  * 
2146  *   result = data->tag_count;
2147  *   g_slice_free (CounterData, data);
2148  * 
2149  *   return result;
2150  * }
2151  * ]|
2152  *
2153  * The subparser would then be used as follows:
2154  *
2155  * |[
2156  * static void start_element (context, element_name, ...)
2157  * {
2158  *   if (strcmp (element_name, "count-these") == 0)
2159  *     start_counting (context);
2160  * 
2161  *   /&ast; else, handle other tags... &ast;/
2162  * }
2163  * 
2164  * static void end_element (context, element_name, ...)
2165  * {
2166  *   if (strcmp (element_name, "count-these") == 0)
2167  *     g_print ("Counted %d tags\n", end_counting (context));
2168  * 
2169  *   /&ast; else, handle other tags... &ast;/
2170  * }
2171  * ]|
2172  *
2173  * Since: 2.18
2174  **/
2175 void
2176 g_markup_parse_context_push (GMarkupParseContext *context,
2177                              GMarkupParser       *parser,
2178                              gpointer             user_data)
2179 {
2180   GMarkupRecursionTracker *tracker;
2181
2182   tracker = g_slice_new (GMarkupRecursionTracker);
2183   tracker->prev_element = context->subparser_element;
2184   tracker->prev_parser = context->parser;
2185   tracker->prev_user_data = context->user_data;
2186
2187   context->subparser_element = current_element (context);
2188   context->parser = parser;
2189   context->user_data = user_data;
2190
2191   context->subparser_stack = g_slist_prepend (context->subparser_stack,
2192                                               tracker);
2193 }
2194
2195 /**
2196  * g_markup_parse_context_pop:
2197  * @context: a #GMarkupParseContext
2198  *
2199  * Completes the process of a temporary sub-parser redirection.
2200  *
2201  * This function exists to collect the user_data allocated by a
2202  * matching call to g_markup_parse_context_push().  It must be called
2203  * in the end_element handler corresponding to the start_element
2204  * handler during which g_markup_parse_context_push() was called.  You
2205  * must not call this function from the error callback -- the
2206  * @user_data is provided directly to the callback in that case.
2207  *
2208  * This function is not intended to be directly called by users
2209  * interested in invoking subparsers.  Instead, it is intended to be
2210  * used by the subparsers themselves to implement a higher-level
2211  * interface.
2212  *
2213  * Returns: the user_data passed to g_markup_parse_context_push().
2214  *
2215  * Since: 2.18
2216  **/
2217 gpointer
2218 g_markup_parse_context_pop (GMarkupParseContext *context)
2219 {
2220   gpointer user_data;
2221
2222   if (!context->awaiting_pop)
2223     possibly_finish_subparser (context);
2224
2225   g_assert (context->awaiting_pop);
2226
2227   context->awaiting_pop = FALSE;
2228   
2229   /* valgrind friendliness */
2230   user_data = context->held_user_data;
2231   context->held_user_data = NULL;
2232
2233   return user_data;
2234 }
2235
2236 static void
2237 append_escaped_text (GString     *str,
2238                      const gchar *text,
2239                      gssize       length)    
2240 {
2241   const gchar *p;
2242   const gchar *end;
2243   gunichar c;
2244
2245   p = text;
2246   end = text + length;
2247
2248   while (p != end)
2249     {
2250       const gchar *next;
2251       next = g_utf8_next_char (p);
2252
2253       switch (*p)
2254         {
2255         case '&':
2256           g_string_append (str, "&amp;");
2257           break;
2258
2259         case '<':
2260           g_string_append (str, "&lt;");
2261           break;
2262
2263         case '>':
2264           g_string_append (str, "&gt;");
2265           break;
2266
2267         case '\'':
2268           g_string_append (str, "&apos;");
2269           break;
2270
2271         case '"':
2272           g_string_append (str, "&quot;");
2273           break;
2274
2275         default:
2276           c = g_utf8_get_char (p);
2277           if ((0x1 <= c && c <= 0x8) ||
2278               (0xb <= c && c  <= 0xc) ||
2279               (0xe <= c && c <= 0x1f) ||
2280               (0x7f <= c && c <= 0x84) ||
2281               (0x86 <= c && c <= 0x9f))
2282             g_string_append_printf (str, "&#x%x;", c);
2283           else
2284             g_string_append_len (str, p, next - p);
2285           break;
2286         }
2287
2288       p = next;
2289     }
2290 }
2291
2292 /**
2293  * g_markup_escape_text:
2294  * @text: some valid UTF-8 text
2295  * @length: length of @text in bytes, or -1 if the text is nul-terminated
2296  * 
2297  * Escapes text so that the markup parser will parse it verbatim.
2298  * Less than, greater than, ampersand, etc. are replaced with the
2299  * corresponding entities. This function would typically be used
2300  * when writing out a file to be parsed with the markup parser.
2301  * 
2302  * Note that this function doesn't protect whitespace and line endings
2303  * from being processed according to the XML rules for normalization
2304  * of line endings and attribute values.
2305  *
2306  * Note also that if given a string containing them, this function
2307  * will produce character references in the range of &amp;#x1; ..
2308  * &amp;#x1f; for all control sequences except for tabstop, newline
2309  * and carriage return.  The character references in this range are
2310  * not valid XML 1.0, but they are valid XML 1.1 and will be accepted
2311  * by the GMarkup parser.
2312  * 
2313  * Return value: a newly allocated string with the escaped text
2314  **/
2315 gchar*
2316 g_markup_escape_text (const gchar *text,
2317                       gssize       length)  
2318 {
2319   GString *str;
2320
2321   g_return_val_if_fail (text != NULL, NULL);
2322
2323   if (length < 0)
2324     length = strlen (text);
2325
2326   /* prealloc at least as long as original text */
2327   str = g_string_sized_new (length);
2328   append_escaped_text (str, text, length);
2329
2330   return g_string_free (str, FALSE);
2331 }
2332
2333 /**
2334  * find_conversion:
2335  * @format: a printf-style format string
2336  * @after: location to store a pointer to the character after
2337  *   the returned conversion. On a %NULL return, returns the
2338  *   pointer to the trailing NUL in the string
2339  * 
2340  * Find the next conversion in a printf-style format string.
2341  * Partially based on code from printf-parser.c,
2342  * Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
2343  * 
2344  * Return value: pointer to the next conversion in @format,
2345  *  or %NULL, if none.
2346  **/
2347 static const char *
2348 find_conversion (const char  *format,
2349                  const char **after)
2350 {
2351   const char *start = format;
2352   const char *cp;
2353   
2354   while (*start != '\0' && *start != '%')
2355     start++;
2356
2357   if (*start == '\0')
2358     {
2359       *after = start;
2360       return NULL;
2361     }
2362
2363   cp = start + 1;
2364
2365   if (*cp == '\0')
2366     {
2367       *after = cp;
2368       return NULL;
2369     }
2370   
2371   /* Test for positional argument.  */
2372   if (*cp >= '0' && *cp <= '9')
2373     {
2374       const char *np;
2375       
2376       for (np = cp; *np >= '0' && *np <= '9'; np++)
2377         ;
2378       if (*np == '$')
2379         cp = np + 1;
2380     }
2381
2382   /* Skip the flags.  */
2383   for (;;)
2384     {
2385       if (*cp == '\'' ||
2386           *cp == '-' ||
2387           *cp == '+' ||
2388           *cp == ' ' ||
2389           *cp == '#' ||
2390           *cp == '0')
2391         cp++;
2392       else
2393         break;
2394     }
2395
2396   /* Skip the field width.  */
2397   if (*cp == '*')
2398     {
2399       cp++;
2400
2401       /* Test for positional argument.  */
2402       if (*cp >= '0' && *cp <= '9')
2403         {
2404           const char *np;
2405
2406           for (np = cp; *np >= '0' && *np <= '9'; np++)
2407             ;
2408           if (*np == '$')
2409             cp = np + 1;
2410         }
2411     }
2412   else
2413     {
2414       for (; *cp >= '0' && *cp <= '9'; cp++)
2415         ;
2416     }
2417
2418   /* Skip the precision.  */
2419   if (*cp == '.')
2420     {
2421       cp++;
2422       if (*cp == '*')
2423         {
2424           /* Test for positional argument.  */
2425           if (*cp >= '0' && *cp <= '9')
2426             {
2427               const char *np;
2428
2429               for (np = cp; *np >= '0' && *np <= '9'; np++)
2430                 ;
2431               if (*np == '$')
2432                 cp = np + 1;
2433             }
2434         }
2435       else
2436         {
2437           for (; *cp >= '0' && *cp <= '9'; cp++)
2438             ;
2439         }
2440     }
2441
2442   /* Skip argument type/size specifiers.  */
2443   while (*cp == 'h' ||
2444          *cp == 'L' ||
2445          *cp == 'l' ||
2446          *cp == 'j' ||
2447          *cp == 'z' ||
2448          *cp == 'Z' ||
2449          *cp == 't')
2450     cp++;
2451           
2452   /* Skip the conversion character.  */
2453   cp++;
2454
2455   *after = cp;
2456   return start;
2457 }
2458
2459 /**
2460  * g_markup_vprintf_escaped:
2461  * @format: printf() style format string
2462  * @args: variable argument list, similar to vprintf()
2463  * 
2464  * Formats the data in @args according to @format, escaping
2465  * all string and character arguments in the fashion
2466  * of g_markup_escape_text(). See g_markup_printf_escaped().
2467  * 
2468  * Return value: newly allocated result from formatting
2469  *  operation. Free with g_free().
2470  *
2471  * Since: 2.4
2472  **/
2473 char *
2474 g_markup_vprintf_escaped (const char *format,
2475                           va_list     args)
2476 {
2477   GString *format1;
2478   GString *format2;
2479   GString *result = NULL;
2480   gchar *output1 = NULL;
2481   gchar *output2 = NULL;
2482   const char *p, *op1, *op2;
2483   va_list args2;
2484
2485   /* The technique here, is that we make two format strings that
2486    * have the identical conversions in the identical order to the
2487    * original strings, but differ in the text in-between. We
2488    * then use the normal g_strdup_vprintf() to format the arguments
2489    * with the two new format strings. By comparing the results,
2490    * we can figure out what segments of the output come from
2491    * the the original format string, and what from the arguments,
2492    * and thus know what portions of the string to escape.
2493    *
2494    * For instance, for:
2495    *
2496    *  g_markup_printf_escaped ("%s ate %d apples", "Susan & Fred", 5);
2497    *
2498    * We form the two format strings "%sX%dX" and %sY%sY". The results
2499    * of formatting with those two strings are
2500    *
2501    * "%sX%dX" => "Susan & FredX5X"
2502    * "%sY%dY" => "Susan & FredY5Y"
2503    *
2504    * To find the span of the first argument, we find the first position
2505    * where the two arguments differ, which tells us that the first
2506    * argument formatted to "Susan & Fred". We then escape that
2507    * to "Susan &amp; Fred" and join up with the intermediate portions
2508    * of the format string and the second argument to get
2509    * "Susan &amp; Fred ate 5 apples".
2510    */
2511
2512   /* Create the two modified format strings
2513    */
2514   format1 = g_string_new (NULL);
2515   format2 = g_string_new (NULL);
2516   p = format;
2517   while (TRUE)
2518     {
2519       const char *after;
2520       const char *conv = find_conversion (p, &after);
2521       if (!conv)
2522         break;
2523
2524       g_string_append_len (format1, conv, after - conv);
2525       g_string_append_c (format1, 'X');
2526       g_string_append_len (format2, conv, after - conv);
2527       g_string_append_c (format2, 'Y');
2528
2529       p = after;
2530     }
2531
2532   /* Use them to format the arguments
2533    */
2534   G_VA_COPY (args2, args);
2535   
2536   output1 = g_strdup_vprintf (format1->str, args);
2537   if (!output1)
2538     {
2539       va_end (args2);
2540       goto cleanup;
2541     }
2542   
2543   output2 = g_strdup_vprintf (format2->str, args2);
2544   va_end (args2);
2545   if (!output2)
2546     goto cleanup;
2547
2548   result = g_string_new (NULL);
2549
2550   /* Iterate through the original format string again,
2551    * copying the non-conversion portions and the escaped
2552    * converted arguments to the output string.
2553    */
2554   op1 = output1;
2555   op2 = output2;
2556   p = format;
2557   while (TRUE)
2558     {
2559       const char *after;
2560       const char *output_start;
2561       const char *conv = find_conversion (p, &after);
2562       char *escaped;
2563       
2564       if (!conv)        /* The end, after points to the trailing \0 */
2565         {
2566           g_string_append_len (result, p, after - p);
2567           break;
2568         }
2569
2570       g_string_append_len (result, p, conv - p);
2571       output_start = op1;
2572       while (*op1 == *op2)
2573         {
2574           op1++;
2575           op2++;
2576         }
2577       
2578       escaped = g_markup_escape_text (output_start, op1 - output_start);
2579       g_string_append (result, escaped);
2580       g_free (escaped);
2581       
2582       p = after;
2583       op1++;
2584       op2++;
2585     }
2586
2587  cleanup:
2588   g_string_free (format1, TRUE);
2589   g_string_free (format2, TRUE);
2590   g_free (output1);
2591   g_free (output2);
2592
2593   if (result)
2594     return g_string_free (result, FALSE);
2595   else
2596     return NULL;
2597 }
2598
2599 /**
2600  * g_markup_printf_escaped:
2601  * @format: printf() style format string
2602  * @Varargs: the arguments to insert in the format string
2603  * 
2604  * Formats arguments according to @format, escaping
2605  * all string and character arguments in the fashion
2606  * of g_markup_escape_text(). This is useful when you
2607  * want to insert literal strings into XML-style markup
2608  * output, without having to worry that the strings
2609  * might themselves contain markup.
2610  *
2611  * |[
2612  * const char *store = "Fortnum &amp; Mason";
2613  * const char *item = "Tea";
2614  * char *output;
2615  * &nbsp;
2616  * output = g_markup_printf_escaped ("&lt;purchase&gt;"
2617  *                                   "&lt;store&gt;&percnt;s&lt;/store&gt;"
2618  *                                   "&lt;item&gt;&percnt;s&lt;/item&gt;"
2619  *                                   "&lt;/purchase&gt;",
2620  *                                   store, item);
2621  * ]|
2622  * 
2623  * Return value: newly allocated result from formatting
2624  *  operation. Free with g_free().
2625  *
2626  * Since: 2.4
2627  **/
2628 char *
2629 g_markup_printf_escaped (const char *format, ...)
2630 {
2631   char *result;
2632   va_list args;
2633   
2634   va_start (args, format);
2635   result = g_markup_vprintf_escaped (format, args);
2636   va_end (args);
2637
2638   return result;
2639 }
2640
2641 static gboolean
2642 g_markup_parse_boolean (const char  *string,
2643                         gboolean    *value)
2644 {
2645   char const * const falses[] = { "false", "f", "no", "n", "0" };
2646   char const * const trues[] = { "true", "t", "yes", "y", "1" };
2647   int i;
2648
2649   for (i = 0; i < G_N_ELEMENTS (falses); i++)
2650     {
2651       if (g_ascii_strcasecmp (string, falses[i]) == 0)
2652         {
2653           if (value != NULL)
2654             *value = FALSE;
2655
2656           return TRUE;
2657         }
2658     }
2659
2660   for (i = 0; i < G_N_ELEMENTS (trues); i++)
2661     {
2662       if (g_ascii_strcasecmp (string, trues[i]) == 0)
2663         {
2664           if (value != NULL)
2665             *value = TRUE;
2666
2667           return TRUE;
2668         }
2669     }
2670
2671   return FALSE;
2672 }
2673
2674 /**
2675  * GMarkupCollectType:
2676  * @G_MARKUP_COLLECT_INVALID: used to terminate the list of attributes
2677  *                            to collect.
2678  * @G_MARKUP_COLLECT_STRING: collect the string pointer directly from
2679  *                           the attribute_values[] array.  Expects a
2680  *                           parameter of type (const char **).  If
2681  *                           %G_MARKUP_COLLECT_OPTIONAL is specified
2682  *                           and the attribute isn't present then the
2683  *                           pointer will be set to %NULL.
2684  * @G_MARKUP_COLLECT_STRDUP: as with %G_MARKUP_COLLECT_STRING, but
2685  *                           expects a paramter of type (char **) and
2686  *                           g_strdup()s the returned pointer.  The
2687  *                           pointer must be freed with g_free().
2688  * @G_MARKUP_COLLECT_BOOLEAN: expects a parameter of type (gboolean *)
2689  *                            and parses the attribute value as a
2690  *                            boolean.  Sets %FALSE if the attribute
2691  *                            isn't present.  Valid boolean values
2692  *                            consist of (case insensitive) "false",
2693  *                            "f", "no", "n", "0" and "true", "t",
2694  *                            "yes", "y", "1".
2695  * @G_MARKUP_COLLECT_TRISTATE: as with %G_MARKUP_COLLECT_BOOLEAN, but
2696  *                             in the case of a missing attribute a
2697  *                             value is set that compares equal to
2698  *                             neither %FALSE nor %TRUE.
2699  *                             G_MARKUP_COLLECT_OPTIONAL is implied.
2700  * @G_MARKUP_COLLECT_OPTIONAL: can be bitwise ORed with the other
2701  *                             fields.  If present, allows the
2702  *                             attribute not to appear.  A default
2703  *                             value is set depending on what value
2704  *                             type is used.
2705  *
2706  * A mixed enumerated type and flags field.  You must specify one type
2707  * (string, strdup, boolean, tristate).  Additionally, you may
2708  * optionally bitwise OR the type with the flag
2709  * %G_MARKUP_COLLECT_OPTIONAL.
2710  *
2711  * It is likely that this enum will be extended in the future to
2712  * support other types.
2713  **/
2714
2715 /**
2716  * g_markup_collect_attributes:
2717  * @element_name: the current tag name
2718  * @attribute_names: the attribute names
2719  * @attribute_values: the attribute values
2720  * @error: a pointer to a #GError or %NULL
2721  * @first_type: the #GMarkupCollectType of the
2722  *              first attribute
2723  * @first_attr: the name of the first attribute
2724  * @...: a pointer to the storage location of the
2725  *       first attribute (or %NULL), followed by
2726  *       more types names and pointers, ending
2727  *       with %G_MARKUP_COLLECT_INVALID.
2728  * 
2729  * Collects the attributes of the element from the
2730  * data passed to the #GMarkupParser start_element
2731  * function, dealing with common error conditions
2732  * and supporting boolean values.
2733  *
2734  * This utility function is not required to write
2735  * a parser but can save a lot of typing.
2736  *
2737  * The @element_name, @attribute_names,
2738  * @attribute_values and @error parameters passed
2739  * to the start_element callback should be passed
2740  * unmodified to this function.
2741  *
2742  * Following these arguments is a list of
2743  * "supported" attributes to collect.  It is an
2744  * error to specify multiple attributes with the
2745  * same name.  If any attribute not in the list
2746  * appears in the @attribute_names array then an
2747  * unknown attribute error will result.
2748  *
2749  * The #GMarkupCollectType field allows specifying
2750  * the type of collection to perform and if a
2751  * given attribute must appear or is optional.
2752  *
2753  * The attribute name is simply the name of the
2754  * attribute to collect.
2755  *
2756  * The pointer should be of the appropriate type
2757  * (see the descriptions under
2758  * #GMarkupCollectType) and may be %NULL in case a
2759  * particular attribute is to be allowed but
2760  * ignored.
2761  *
2762  * This function deals with issuing errors for missing attributes 
2763  * (of type %G_MARKUP_ERROR_MISSING_ATTRIBUTE), unknown attributes 
2764  * (of type %G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE) and duplicate 
2765  * attributes (of type %G_MARKUP_ERROR_INVALID_CONTENT) as well 
2766  * as parse errors for boolean-valued attributes (again of type
2767  * %G_MARKUP_ERROR_INVALID_CONTENT). In all of these cases %FALSE 
2768  * will be returned and @error will be set as appropriate.
2769  *
2770  * Return value: %TRUE if successful
2771  *
2772  * Since: 2.16
2773  **/
2774 gboolean
2775 g_markup_collect_attributes (const gchar         *element_name,
2776                              const gchar        **attribute_names,
2777                              const gchar        **attribute_values,
2778                              GError             **error,
2779                              GMarkupCollectType   first_type,
2780                              const gchar         *first_attr,
2781                              ...)
2782 {
2783   GMarkupCollectType type;
2784   const gchar *attr;
2785   guint64 collected;
2786   int written;
2787   va_list ap;
2788   int i;
2789
2790   type = first_type;
2791   attr = first_attr;
2792   collected = 0;
2793   written = 0;
2794
2795   va_start (ap, first_attr);
2796   while (type != G_MARKUP_COLLECT_INVALID)
2797     {
2798       gboolean mandatory;
2799       const gchar *value;
2800
2801       mandatory = !(type & G_MARKUP_COLLECT_OPTIONAL);
2802       type &= (G_MARKUP_COLLECT_OPTIONAL - 1);
2803
2804       /* tristate records a value != TRUE and != FALSE
2805        * for the case where the attribute is missing
2806        */
2807       if (type == G_MARKUP_COLLECT_TRISTATE)
2808         mandatory = FALSE;
2809
2810       for (i = 0; attribute_names[i]; i++)
2811         if (i >= 40 || !(collected & (G_GUINT64_CONSTANT(1) << i)))
2812           if (!strcmp (attribute_names[i], attr))
2813             break;
2814
2815       /* ISO C99 only promises that the user can pass up to 127 arguments.
2816        * Subtracting the first 4 arguments plus the final NULL and dividing
2817        * by 3 arguments per collected attribute, we are left with a maximum
2818        * number of supported attributes of (127 - 5) / 3 = 40.
2819        *
2820        * In reality, nobody is ever going to call us with anywhere close to
2821        * 40 attributes to collect, so it is safe to assume that if i > 40
2822        * then the user has given some invalid or repeated arguments.  These
2823        * problems will be caught and reported at the end of the function.
2824        *
2825        * We know at this point that we have an error, but we don't know
2826        * what error it is, so just continue...
2827        */
2828       if (i < 40)
2829         collected |= (G_GUINT64_CONSTANT(1) << i);
2830
2831       value = attribute_values[i];
2832
2833       if (value == NULL && mandatory)
2834         {
2835           g_set_error (error, G_MARKUP_ERROR,
2836                        G_MARKUP_ERROR_MISSING_ATTRIBUTE,
2837                        "element '%s' requires attribute '%s'",
2838                        element_name, attr);
2839
2840           va_end (ap);
2841           goto failure;
2842         }
2843
2844       switch (type)
2845         {
2846         case G_MARKUP_COLLECT_STRING:
2847           {
2848             const char **str_ptr;
2849
2850             str_ptr = va_arg (ap, const char **);
2851
2852             if (str_ptr != NULL)
2853               *str_ptr = value;
2854           }
2855           break;
2856
2857         case G_MARKUP_COLLECT_STRDUP:
2858           {
2859             char **str_ptr;
2860
2861             str_ptr = va_arg (ap, char **);
2862
2863             if (str_ptr != NULL)
2864               *str_ptr = g_strdup (value);
2865           }
2866           break;
2867
2868         case G_MARKUP_COLLECT_BOOLEAN:
2869         case G_MARKUP_COLLECT_TRISTATE:
2870           if (value == NULL)
2871             {
2872               gboolean *bool_ptr;
2873
2874               bool_ptr = va_arg (ap, gboolean *);
2875
2876               if (bool_ptr != NULL)
2877                 {
2878                   if (type == G_MARKUP_COLLECT_TRISTATE)
2879                     /* constructivists rejoice!
2880                      * neither false nor true...
2881                      */
2882                     *bool_ptr = -1;
2883
2884                   else /* G_MARKUP_COLLECT_BOOLEAN */
2885                     *bool_ptr = FALSE;
2886                 }
2887             }
2888           else
2889             {
2890               if (!g_markup_parse_boolean (value, va_arg (ap, gboolean *)))
2891                 {
2892                   g_set_error (error, G_MARKUP_ERROR,
2893                                G_MARKUP_ERROR_INVALID_CONTENT,
2894                                "element '%s', attribute '%s', value '%s' "
2895                                "cannot be parsed as a boolean value",
2896                                element_name, attr, value);
2897
2898                   va_end (ap);
2899                   goto failure;
2900                 }
2901             }
2902
2903           break;
2904
2905         default:
2906           g_assert_not_reached ();
2907         }
2908
2909       type = va_arg (ap, GMarkupCollectType);
2910       attr = va_arg (ap, const char *);
2911       written++;
2912     }
2913   va_end (ap);
2914
2915   /* ensure we collected all the arguments */
2916   for (i = 0; attribute_names[i]; i++)
2917     if ((collected & (G_GUINT64_CONSTANT(1) << i)) == 0)
2918       {
2919         /* attribute not collected:  could be caused by two things.
2920          *
2921          * 1) it doesn't exist in our list of attributes
2922          * 2) it existed but was matched by a duplicate attribute earlier
2923          *
2924          * find out.
2925          */
2926         int j;
2927
2928         for (j = 0; j < i; j++)
2929           if (strcmp (attribute_names[i], attribute_names[j]) == 0)
2930             /* duplicate! */
2931             break;
2932
2933         /* j is now the first occurance of attribute_names[i] */
2934         if (i == j)
2935           g_set_error (error, G_MARKUP_ERROR,
2936                        G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
2937                        "attribute '%s' invalid for element '%s'",
2938                        attribute_names[i], element_name);
2939         else
2940           g_set_error (error, G_MARKUP_ERROR,
2941                        G_MARKUP_ERROR_INVALID_CONTENT,
2942                        "attribute '%s' given multiple times for element '%s'",
2943                        attribute_names[i], element_name);
2944
2945         goto failure;
2946       }
2947
2948   return TRUE;
2949
2950 failure:
2951   /* replay the above to free allocations */
2952   type = first_type;
2953   attr = first_attr;
2954
2955   va_start (ap, first_attr);
2956   while (type != G_MARKUP_COLLECT_INVALID)
2957     {
2958       gpointer ptr;
2959
2960       ptr = va_arg (ap, gpointer);
2961
2962       if (ptr == NULL)
2963         continue;
2964
2965       switch (type & (G_MARKUP_COLLECT_OPTIONAL - 1))
2966         {
2967         case G_MARKUP_COLLECT_STRDUP:
2968           if (written)
2969             g_free (*(char **) ptr);
2970
2971         case G_MARKUP_COLLECT_STRING:
2972           *(char **) ptr = NULL;
2973           break;
2974
2975         case G_MARKUP_COLLECT_BOOLEAN:
2976           *(gboolean *) ptr = FALSE;
2977           break;
2978
2979         case G_MARKUP_COLLECT_TRISTATE:
2980           *(gboolean *) ptr = -1;
2981           break;
2982         }
2983
2984       type = va_arg (ap, GMarkupCollectType);
2985       attr = va_arg (ap, const char *);
2986
2987       if (written)
2988         written--;
2989     }
2990   va_end (ap);
2991
2992   return FALSE;
2993 }
2994
2995 #define __G_MARKUP_C__
2996 #include "galiasdef.c"