1/*
2 * Copyright © 2008 Ryan Lortie
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * See the included COPYING file for more information.
10 */
11
12#include <string.h>
13#include <stdio.h>
14#include <glib.h>
15
16/* keep track of GString instances to make sure nothing leaks */
17static int strings_allocated;
18
19/* === the GMarkupParser functions === */
20static void
21subparser_start_element (GMarkupParseContext *context,
22 const gchar *element_name,
23 const gchar **attribute_names,
24 const gchar **attribute_values,
25 gpointer user_data,
26 GError **error)
27{
28 g_string_append_printf (string: user_data, format: "{%s}", element_name);
29
30 /* we don't like trouble... */
31 if (strcmp (s1: element_name, s2: "trouble") == 0)
32 g_set_error (err: error, G_MARKUP_ERROR, code: G_MARKUP_ERROR_INVALID_CONTENT,
33 format: "we don't like trouble");
34}
35
36static void
37subparser_end_element (GMarkupParseContext *context,
38 const gchar *element_name,
39 gpointer user_data,
40 GError **error)
41{
42 g_string_append_printf (string: user_data, format: "{/%s}", element_name);
43}
44
45static void
46subparser_error (GMarkupParseContext *context,
47 GError *error,
48 gpointer user_data)
49{
50 g_string_free (string: user_data, TRUE);
51 strings_allocated--;
52}
53
54static GMarkupParser subparser_parser =
55{
56 subparser_start_element,
57 subparser_end_element,
58 NULL,
59 NULL,
60 subparser_error
61};
62
63/* convenience functions for a parser that does not
64 * replay the starting tag into the subparser...
65 */
66static void
67subparser_start (GMarkupParseContext *ctx)
68{
69 gpointer user_data;
70
71 user_data = g_string_new (NULL);
72 strings_allocated++;
73 g_markup_parse_context_push (context: ctx, parser: &subparser_parser, user_data);
74}
75
76static char *
77subparser_end (GMarkupParseContext *ctx,
78 GError **error)
79{
80 GString *string;
81 char *result;
82
83 string = g_markup_parse_context_pop (context: ctx);
84 result = string->str;
85
86 g_string_free (string, FALSE);
87 strings_allocated--;
88
89 if (result == NULL || result[0] == '\0')
90 {
91 g_free (mem: result);
92 g_set_error (err: error, G_MARKUP_ERROR, code: G_MARKUP_ERROR_INVALID_CONTENT,
93 format: "got no data");
94
95 return NULL;
96 }
97
98 return result;
99}
100
101/* convenience functions for a parser that -does-
102 * replay the starting tag into the subparser...
103 */
104static gboolean
105replay_parser_start (GMarkupParseContext *ctx,
106 const char *element_name,
107 const char **attribute_names,
108 const char **attribute_values,
109 GError **error)
110{
111 GError *tmp_error = NULL;
112 gpointer user_data;
113
114 user_data = g_string_new (NULL);
115 strings_allocated++;
116
117 subparser_parser.start_element (ctx, element_name,
118 attribute_names, attribute_values,
119 user_data, &tmp_error);
120
121 if (tmp_error)
122 {
123 g_propagate_error (dest: error, src: tmp_error);
124 g_string_free (string: user_data, TRUE);
125 strings_allocated--;
126
127 return FALSE;
128 }
129
130 g_markup_parse_context_push (context: ctx, parser: &subparser_parser, user_data);
131
132 return TRUE;
133}
134
135static char *
136replay_parser_end (GMarkupParseContext *ctx,
137 GError **error)
138{
139 GError *tmp_error = NULL;
140 GString *string;
141 char *result;
142
143 string = g_markup_parse_context_pop (context: ctx);
144
145 subparser_parser.end_element (ctx, g_markup_parse_context_get_element (context: ctx),
146 string, &tmp_error);
147
148 if (tmp_error)
149 {
150 g_propagate_error (dest: error, src: tmp_error);
151 g_string_free (string, TRUE);
152 strings_allocated--;
153
154 return NULL;
155 }
156
157 result = string->str;
158
159 g_string_free (string, FALSE);
160 strings_allocated--;
161
162 if (result == NULL || result[0] == '\0')
163 {
164 g_free (mem: result);
165 g_set_error (err: error, G_MARKUP_ERROR, code: G_MARKUP_ERROR_INVALID_CONTENT,
166 format: "got no data");
167
168 return NULL;
169 }
170
171 return result;
172}
173
174
175/* === start interface between subparser and calling parser === */
176static void subparser_start (GMarkupParseContext *ctx);
177static char *subparser_end (GMarkupParseContext *ctx,
178 GError **error);
179/* === end interface between subparser and calling parser === */
180
181/* === start interface between replay parser and calling parser === */
182static gboolean replay_parser_start (GMarkupParseContext *ctx,
183 const char *element_name,
184 const char **attribute_names,
185 const char **attribute_values,
186 GError **error);
187static char *replay_parser_end (GMarkupParseContext *ctx,
188 GError **error);
189/* === end interface between replay parser and calling parser === */
190
191
192
193/* now comes our parser for the test.
194 *
195 * we recognise the tags <test> and <sub>.
196 * <test> is ignored.
197 * <sub> invokes the subparser (no replay).
198 *
199 * "unknown tags" are passed to the reply subparser
200 * (so the unknown tag is fed to the subparser...)
201 */
202static void
203start_element (GMarkupParseContext *context,
204 const gchar *element_name,
205 const gchar **attribute_names,
206 const gchar **attribute_values,
207 gpointer user_data,
208 GError **error)
209{
210 g_string_append_printf (string: user_data, format: "<%s>", element_name);
211
212 if (strcmp (s1: element_name, s2: "test") == 0)
213 {
214 /* do nothing */
215 }
216 else if (strcmp (s1: element_name, s2: "sub") == 0)
217 {
218 /* invoke subparser */
219 subparser_start (ctx: context);
220 }
221 else
222 {
223 /* unknown tag. invoke replay subparser */
224 if (!replay_parser_start (ctx: context, element_name,
225 attribute_names, attribute_values,
226 error))
227 return;
228 }
229}
230
231static void
232end_element (GMarkupParseContext *context,
233 const gchar *element_name,
234 gpointer user_data,
235 GError **error)
236{
237 if (strcmp (s1: element_name, s2: "test") == 0)
238 {
239 /* do nothing */
240 }
241 else if (strcmp (s1: element_name, s2: "sub") == 0)
242 {
243 char *result;
244
245 if ((result = subparser_end (ctx: context, error)) == NULL)
246 return;
247
248 g_string_append_printf (string: user_data, format: "<<%s>>", result);
249 g_free (mem: result);
250 }
251 else
252 {
253 char *result;
254
255 if ((result = replay_parser_end (ctx: context, error)) == NULL)
256 return;
257
258 g_string_append_printf (string: user_data, format: "[[%s]]", result);
259 g_free (mem: result);
260 }
261
262 g_string_append_printf (string: user_data, format: "</%s>", element_name);
263}
264
265static GMarkupParser parser =
266{
267 start_element,
268 end_element,
269 NULL,
270 NULL,
271 NULL
272};
273
274typedef struct
275{
276 const char *markup;
277 const char *result;
278 const char *error_message;
279} TestCase;
280
281static void
282test (gconstpointer user_data)
283{
284 const TestCase *tc = user_data;
285 GMarkupParseContext *ctx;
286 GString *string;
287 gboolean result;
288 GError *error;
289
290 error = NULL;
291 string = g_string_new (NULL);
292 ctx = g_markup_parse_context_new (parser: &parser, flags: 0, user_data: string, NULL);
293 result = g_markup_parse_context_parse (context: ctx, text: tc->markup,
294 text_len: strlen (s: tc->markup), error: &error);
295 if (result)
296 result = g_markup_parse_context_end_parse (context: ctx, error: &error);
297 g_markup_parse_context_free (context: ctx);
298 g_assert (strings_allocated == 0);
299
300 if (result)
301 {
302 if (tc->error_message)
303 g_error ("expected failure (about '%s') passed!\n"
304 " in: %s\n out: %s",
305 tc->error_message, tc->markup, string->str);
306 }
307 else
308 {
309 if (!tc->error_message)
310 g_error ("unexpected failure: '%s'\n"
311 " in: %s\n out: %s",
312 error->message, tc->markup, string->str);
313
314 if (!strstr (haystack: error->message, needle: tc->error_message))
315 g_error ("failed for the wrong reason.\n"
316 " expecting message about '%s'\n"
317 " got message '%s'\n"
318 " in: %s\n out: %s",
319 tc->error_message, error->message, tc->markup, string->str);
320 }
321
322 if (strcmp (s1: string->str, s2: tc->result) != 0)
323 g_error ("got the wrong result.\n"
324 " expected: '%s'\n"
325 " got: '%s'\n"
326 " input: %s",
327 tc->result, string->str, tc->markup);
328
329 if (error)
330 g_error_free (error);
331
332 g_string_free (string, TRUE);
333}
334
335TestCase test_cases[] = /* successful runs */
336{
337 /* in */ /* out */ /* error */
338 { "<test/>", "<test></test>", NULL },
339 { "<sub><foo/></sub>", "<sub><<{foo}{/foo}>></sub>", NULL },
340 { "<sub><foo/><bar/></sub>", "<sub><<{foo}{/foo}{bar}{/bar}>></sub>", NULL },
341 { "<foo><bar/></foo>", "<foo>[[{foo}{bar}{/bar}{/foo}]]</foo>", NULL },
342 { "<foo><x/><y/></foo>", "<foo>[[{foo}{x}{/x}{y}{/y}{/foo}]]</foo>", NULL },
343 { "<foo/>", "<foo>[[{foo}{/foo}]]</foo>", NULL },
344 { "<sub><foo/></sub><bar/>", "<sub><<{foo}{/foo}>></sub>"
345 "<bar>[[{bar}{/bar}]]</bar>", NULL }
346};
347
348TestCase error_cases[] = /* error cases */
349{
350 /* in */ /* out */ /* error */
351 { "<foo><>", "<foo>", ">"},
352 { "", "", "empty" },
353 { "<trouble/>", "<trouble>", "trouble" },
354 { "<sub><trouble>", "<sub>", "trouble" },
355 { "<foo><trouble>", "<foo>", "trouble" },
356 { "<sub></sub>", "<sub>", "no data" },
357 { "<sub/>", "<sub>", "no data" }
358};
359
360#define add_tests(func, basename, array) \
361 G_STMT_START { \
362 gsize __add_tests_i; \
363 \
364 for (__add_tests_i = 0; \
365 __add_tests_i < G_N_ELEMENTS (array); \
366 __add_tests_i++) \
367 { \
368 char *testname; \
369 \
370 testname = g_strdup_printf ("%s/%" G_GSIZE_FORMAT, \
371 basename, __add_tests_i); \
372 g_test_add_data_func (testname, &array[__add_tests_i], func); \
373 g_free (testname); \
374 } \
375 } G_STMT_END
376
377int
378main (int argc, char **argv)
379{
380 g_setenv (variable: "LC_ALL", value: "C", TRUE);
381 g_test_init (argc: &argc, argv: &argv, NULL);
382 add_tests (test, "/glib/markup/subparser/success", test_cases);
383 add_tests (test, "/glib/markup/subparser/failure", error_cases);
384 return g_test_run ();
385}
386

source code of gtk/subprojects/glib/glib/tests/markup-subparser.c