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