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