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