Branch data Line data Source code
1 : : /********************************************************************
2 : : * sixtp.c -- functions for XML parsing *
3 : : * Copyright (c) 2001 Gnumatic, Inc. *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : ********************************************************************/
23 : : #include <glib.h>
24 : : #include <glib/gstdio.h>
25 : :
26 : : #include <config.h>
27 : : #include <string.h>
28 : : #include <ctype.h>
29 : : #include <stdarg.h>
30 : : #include <stdio.h>
31 : : #include <sys/types.h>
32 : : #include <qoflog.h>
33 : : #ifdef _MSC_VER
34 : : typedef int ssize_t;
35 : : # define g_fopen fopen
36 : : #endif
37 : :
38 : : #include "sixtp.h"
39 : : #include "sixtp-parsers.h"
40 : : #include "sixtp-stack.h"
41 : :
42 : : #undef G_LOG_DOMAIN
43 : : #define G_LOG_DOMAIN "gnc.backend.file.sixtp"
44 : : static QofLogModule log_module = "gnc.backend.file.sixtp";
45 : :
46 : : extern const gchar* gnc_v2_xml_version_string; /* see io-gncxml-v2.c */
47 : :
48 : : /************************************************************************/
49 : : gboolean
50 : 0 : is_child_result_from_node_named (sixtp_child_result* cr, const char* tag)
51 : : {
52 : 0 : return ((cr->type == SIXTP_CHILD_RESULT_NODE)
53 : 0 : &&
54 : 0 : (g_strcmp0 (cr->tag, tag) == 0));
55 : : }
56 : :
57 : : void
58 : 0 : sixtp_child_free_data (sixtp_child_result* result)
59 : : {
60 : 0 : if (result->data) g_free (result->data);
61 : 0 : }
62 : :
63 : : void
64 : 3113 : sixtp_child_result_destroy (sixtp_child_result* r)
65 : : {
66 : 3113 : if (r->should_cleanup && r->cleanup_handler)
67 : : {
68 : 746 : r->cleanup_handler (r);
69 : : }
70 : 3113 : if (r->type == SIXTP_CHILD_RESULT_NODE) g_free (r->tag);
71 : 3113 : g_free (r);
72 : 3113 : }
73 : :
74 : : void
75 : 0 : sixtp_child_result_print (sixtp_child_result* cr, FILE* f)
76 : : {
77 : 0 : fprintf (f, "((tag %s) (data %p))",
78 : 0 : cr->tag ? cr->tag : "(null)",
79 : : cr->data);
80 : 0 : }
81 : :
82 : : /************************************************************************/
83 : :
84 : :
85 : : void
86 : 1089 : sixtp_set_start (sixtp* parser, sixtp_start_handler start_handler)
87 : : {
88 : 1089 : parser->start_handler = start_handler;
89 : 1089 : }
90 : :
91 : : void
92 : 0 : sixtp_set_before_child (sixtp* parser, sixtp_before_child_handler handler)
93 : : {
94 : 0 : parser->before_child = handler;
95 : 0 : }
96 : :
97 : : void
98 : 62 : sixtp_set_after_child (sixtp* parser, sixtp_after_child_handler handler)
99 : : {
100 : 62 : parser->after_child = handler;
101 : 62 : }
102 : :
103 : : void
104 : 1089 : sixtp_set_end (sixtp* parser, sixtp_end_handler end_handler)
105 : : {
106 : 1089 : parser->end_handler = end_handler;
107 : 1089 : }
108 : :
109 : : void
110 : 1089 : sixtp_set_chars (sixtp* parser, sixtp_characters_handler char_handler)
111 : : {
112 : 1089 : parser->characters_handler = char_handler;
113 : 1089 : }
114 : :
115 : : void
116 : 124 : sixtp_set_cleanup_result (sixtp* parser, sixtp_result_handler handler)
117 : : {
118 : 124 : parser->cleanup_result = handler;
119 : 124 : }
120 : :
121 : : void
122 : 0 : sixtp_set_cleanup_chars (sixtp* parser, sixtp_result_handler handler)
123 : : {
124 : 0 : parser->cleanup_chars = handler;
125 : 0 : }
126 : :
127 : : void
128 : 1027 : sixtp_set_fail (sixtp* parser,
129 : : sixtp_fail_handler handler)
130 : : {
131 : 1027 : parser->fail_handler = handler;
132 : 1027 : }
133 : :
134 : : void
135 : 124 : sixtp_set_result_fail (sixtp* parser, sixtp_result_handler handler)
136 : : {
137 : 124 : parser->result_fail_handler = handler;
138 : 124 : }
139 : :
140 : : void
141 : 0 : sixtp_set_chars_fail (sixtp* parser, sixtp_result_handler handler)
142 : : {
143 : 0 : parser->chars_fail_handler = handler;
144 : 0 : }
145 : :
146 : : sixtp*
147 : 1240 : sixtp_new (void)
148 : : {
149 : 1240 : sixtp* s = g_new0 (sixtp, 1);
150 : :
151 : 1240 : if (s)
152 : : {
153 : 1240 : s->child_parsers = g_hash_table_new (g_str_hash, g_str_equal);
154 : 1240 : if (!s->child_parsers)
155 : : {
156 : 0 : g_free (s);
157 : 0 : s = NULL;
158 : : }
159 : : }
160 : 1240 : return (s);
161 : : }
162 : :
163 : : sixtp*
164 : 1089 : sixtp_set_any (sixtp* tochange, int cleanup, ...)
165 : : {
166 : : va_list ap;
167 : : sixtp_handler_type type;
168 : :
169 : 1089 : if (!tochange)
170 : : {
171 : 0 : PWARN ("Null tochange passed");
172 : 0 : return NULL;
173 : : }
174 : :
175 : 1089 : va_start (ap, cleanup);
176 : :
177 : : do
178 : : {
179 : 5507 : type = static_cast<decltype (type)> (va_arg (ap, int));
180 : :
181 : 5507 : switch (type)
182 : : {
183 : 1089 : case SIXTP_NO_MORE_HANDLERS:
184 : 1089 : va_end (ap);
185 : 1089 : return tochange;
186 : :
187 : 1089 : case SIXTP_START_HANDLER_ID:
188 : 1089 : sixtp_set_start (tochange, va_arg (ap, sixtp_start_handler));
189 : 1089 : break;
190 : :
191 : 0 : case SIXTP_BEFORE_CHILD_HANDLER_ID:
192 : 0 : sixtp_set_before_child (tochange,
193 : : va_arg (ap, sixtp_before_child_handler));
194 : 0 : break;
195 : :
196 : 62 : case SIXTP_AFTER_CHILD_HANDLER_ID:
197 : 62 : sixtp_set_after_child (tochange,
198 : : va_arg (ap, sixtp_after_child_handler));
199 : 62 : break;
200 : :
201 : 1027 : case SIXTP_END_HANDLER_ID:
202 : 1027 : sixtp_set_end (tochange, va_arg (ap, sixtp_end_handler));
203 : 1027 : break;
204 : :
205 : 1089 : case SIXTP_CHARACTERS_HANDLER_ID:
206 : 1089 : sixtp_set_chars (tochange, va_arg (ap, sixtp_characters_handler));
207 : 1089 : break;
208 : :
209 : 1027 : case SIXTP_FAIL_HANDLER_ID:
210 : 1027 : sixtp_set_fail (tochange, va_arg (ap, sixtp_fail_handler));
211 : 1027 : break;
212 : :
213 : 62 : case SIXTP_CLEANUP_RESULT_ID:
214 : 62 : sixtp_set_cleanup_result (tochange,
215 : : va_arg (ap, sixtp_result_handler));
216 : 62 : break;
217 : :
218 : 0 : case SIXTP_CLEANUP_CHARS_ID:
219 : 0 : sixtp_set_cleanup_chars (tochange,
220 : : va_arg (ap, sixtp_result_handler));
221 : 0 : break;
222 : :
223 : 62 : case SIXTP_RESULT_FAIL_ID:
224 : 62 : sixtp_set_result_fail (tochange, va_arg (ap, sixtp_result_handler));
225 : 62 : break;
226 : :
227 : 0 : case SIXTP_CHARS_FAIL_ID:
228 : 0 : sixtp_set_chars_fail (tochange, va_arg (ap, sixtp_result_handler));
229 : 0 : break;
230 : :
231 : 0 : default:
232 : 0 : va_end (ap);
233 : 0 : g_critical ("Bogus sixtp type %d", type);
234 : 0 : if (cleanup)
235 : : {
236 : 0 : sixtp_destroy (tochange);
237 : : }
238 : 0 : return NULL;
239 : : }
240 : : }
241 : : while (1);
242 : :
243 : : va_end (ap);
244 : : return tochange;
245 : : }
246 : :
247 : : static void sixtp_destroy_child (gpointer key, gpointer value,
248 : : gpointer user_data);
249 : :
250 : : static void
251 : 711 : sixtp_destroy_node (sixtp* sp, GHashTable* corpses)
252 : : {
253 : 711 : g_return_if_fail (sp);
254 : 711 : g_return_if_fail (corpses);
255 : 711 : g_hash_table_foreach (sp->child_parsers, sixtp_destroy_child, corpses);
256 : 711 : g_hash_table_destroy (sp->child_parsers);
257 : 711 : g_free (sp);
258 : : }
259 : :
260 : : static void
261 : 1236 : sixtp_destroy_child (gpointer key, gpointer value, gpointer user_data)
262 : : {
263 : 1236 : GHashTable* corpses = (GHashTable*) user_data;
264 : 1236 : sixtp* child = (sixtp*) value;
265 : : gpointer lookup_key;
266 : : gpointer lookup_value;
267 : :
268 : 1236 : DEBUG ("Killing sixtp child under key <%s>", key ? (char*) key : "(null)");
269 : :
270 : 1236 : if (!corpses)
271 : : {
272 : 0 : g_critical ("no corpses in sixtp_destroy_child <%s>",
273 : : key ? (char*) key : "(null)");
274 : 0 : g_free (key);
275 : 0 : return;
276 : : }
277 : 1236 : if (!child)
278 : : {
279 : 0 : g_critical ("no child in sixtp_destroy_child <%s>",
280 : : key ? (char*) key : "");
281 : 0 : g_free (key);
282 : 0 : return;
283 : : }
284 : 1236 : g_free (key);
285 : :
286 : 1236 : if (!g_hash_table_lookup_extended (corpses, (gconstpointer) child,
287 : : &lookup_key, &lookup_value))
288 : : {
289 : : /* haven't killed this one yet. */
290 : 670 : g_hash_table_insert (corpses, child, (gpointer) 1);
291 : 670 : sixtp_destroy_node (child, corpses);
292 : : }
293 : : }
294 : :
295 : : void
296 : 41 : sixtp_destroy (sixtp* sp)
297 : : {
298 : : GHashTable* corpses;
299 : 41 : g_return_if_fail (sp);
300 : 41 : corpses = g_hash_table_new (g_direct_hash, g_direct_equal);
301 : 41 : sixtp_destroy_node (sp, corpses);
302 : 41 : g_hash_table_destroy (corpses);
303 : : }
304 : :
305 : :
306 : : /***********************************************************************/
307 : :
308 : : gboolean
309 : 1935 : sixtp_add_sub_parser (sixtp* parser, const gchar* tag, sixtp* sub_parser)
310 : : {
311 : 1935 : g_return_val_if_fail (parser, FALSE);
312 : 1935 : g_return_val_if_fail (tag, FALSE);
313 : 1935 : g_return_val_if_fail (sub_parser, FALSE);
314 : :
315 : 1935 : g_hash_table_insert (parser->child_parsers,
316 : 1935 : g_strdup (tag), (gpointer) sub_parser);
317 : 1935 : return (TRUE);
318 : : }
319 : :
320 : : /*
321 : : * This is a bit complex because of having to make sure to
322 : : * cleanup things we haven't looked at on an error condition
323 : : */
324 : : sixtp*
325 : 340 : sixtp_add_some_sub_parsers (sixtp* tochange, int cleanup, ...)
326 : : {
327 : : int have_error;
328 : : va_list ap;
329 : : char* tag;
330 : : sixtp* handler;
331 : :
332 : 340 : va_start (ap, cleanup);
333 : :
334 : 340 : have_error = 0;
335 : :
336 : 340 : if (!tochange)
337 : : {
338 : 0 : have_error = 1;
339 : : }
340 : :
341 : : do
342 : : {
343 : 1186 : tag = va_arg (ap, char*);
344 : 1186 : if (!tag)
345 : : {
346 : 340 : break;
347 : : }
348 : :
349 : 846 : handler = va_arg (ap, sixtp*);
350 : 846 : if (!handler)
351 : : {
352 : 0 : PWARN ("Handler for tag %s is null",
353 : : tag ? tag : "(null)");
354 : :
355 : 0 : if (cleanup)
356 : : {
357 : 0 : sixtp_destroy (tochange);
358 : 0 : tochange = NULL;
359 : 0 : have_error = 1;
360 : : }
361 : : else
362 : : {
363 : 0 : va_end (ap);
364 : 0 : return NULL;
365 : : }
366 : : }
367 : :
368 : 846 : if (have_error)
369 : : {
370 : 0 : sixtp_destroy (handler);
371 : : }
372 : : else
373 : : {
374 : 846 : sixtp_add_sub_parser (tochange, tag, handler);
375 : : }
376 : : }
377 : : while (1);
378 : :
379 : 340 : va_end (ap);
380 : 340 : return tochange;
381 : : }
382 : :
383 : : /************************************************************************/
384 : :
385 : : void
386 : 50438 : sixtp_sax_start_handler (void* user_data,
387 : : const xmlChar* name,
388 : : const xmlChar** attrs)
389 : : {
390 : 50438 : sixtp_sax_data* pdata = (sixtp_sax_data*) user_data;
391 : 50438 : sixtp_stack_frame* current_frame = NULL;
392 : 50438 : sixtp* current_parser = NULL;
393 : 50438 : sixtp* next_parser = NULL;
394 : 50438 : gchar* next_parser_tag = NULL;
395 : 50438 : gboolean lookup_success = FALSE;
396 : 50438 : sixtp_stack_frame* new_frame = NULL;
397 : :
398 : 50438 : current_frame = (sixtp_stack_frame*) pdata->stack->data;
399 : 50438 : current_parser = current_frame->parser;
400 : :
401 : : /* Use an extended lookup so we can get *our* copy of the key.
402 : : Since we've strduped it, we know its lifetime... */
403 : : lookup_success =
404 : 50438 : g_hash_table_lookup_extended (current_parser->child_parsers,
405 : : name,
406 : : reinterpret_cast<void**> (&next_parser_tag),
407 : : reinterpret_cast<void**> (&next_parser));
408 : :
409 : :
410 : 50438 : if (!lookup_success)
411 : : {
412 : : /* magic catch all value */
413 : 47319 : lookup_success = g_hash_table_lookup_extended (
414 : : current_parser->child_parsers, SIXTP_MAGIC_CATCHER,
415 : : reinterpret_cast<void**> (&next_parser_tag),
416 : : reinterpret_cast<void**> (&next_parser));
417 : 47319 : if (!lookup_success)
418 : : {
419 : 0 : g_critical ("Tag <%s> not allowed in current context.",
420 : : name ? (char*) name : "(null)");
421 : 0 : pdata->parsing_ok = FALSE;
422 : 0 : next_parser = pdata->bad_xml_parser;
423 : : }
424 : : }
425 : :
426 : 50438 : if (current_frame->parser->before_child)
427 : : {
428 : 0 : GSList* parent_data_from_children = NULL;
429 : 0 : gpointer parent_data_for_children = NULL;
430 : :
431 : 0 : if (g_slist_length (pdata->stack) > 1)
432 : : {
433 : : /* we're not in the top level node */
434 : 0 : sixtp_stack_frame* parent_frame =
435 : 0 : (sixtp_stack_frame*) pdata->stack->next->data;
436 : 0 : parent_data_from_children = static_cast<decltype (parent_data_from_children)>
437 : : (parent_frame->data_from_children);
438 : : }
439 : :
440 : 0 : pdata->parsing_ok &=
441 : 0 : current_frame->parser->before_child (current_frame->data_for_children,
442 : : current_frame->data_from_children,
443 : : parent_data_from_children,
444 : : parent_data_for_children,
445 : : pdata->global_data,
446 : : & (current_frame->frame_data),
447 : 0 : current_frame->tag,
448 : : (gchar*) name);
449 : : }
450 : :
451 : : /* now allocate the new stack frame and shift to it */
452 : 50438 : new_frame = sixtp_stack_frame_new (next_parser, g_strdup ((char*) name));
453 : :
454 : 50438 : new_frame->line = xmlSAX2GetLineNumber (pdata->saxParserCtxt);
455 : 50438 : new_frame->col = xmlSAX2GetColumnNumber (pdata->saxParserCtxt);
456 : :
457 : 50438 : pdata->stack = g_slist_prepend (pdata->stack, (gpointer) new_frame);
458 : :
459 : 50438 : if (next_parser->start_handler)
460 : : {
461 : 50362 : pdata->parsing_ok &=
462 : 50362 : next_parser->start_handler (current_frame->data_from_children,
463 : : current_frame->data_for_children,
464 : : pdata->global_data,
465 : : &new_frame->data_for_children,
466 : : &new_frame->frame_data,
467 : : (gchar*) name,
468 : : (gchar**)attrs);
469 : : }
470 : 50438 : }
471 : :
472 : : void
473 : 106046 : sixtp_sax_characters_handler (void* user_data, const xmlChar* text, int len)
474 : : {
475 : 106046 : sixtp_sax_data* pdata = (sixtp_sax_data*) user_data;
476 : : sixtp_stack_frame* frame;
477 : :
478 : 106046 : frame = (sixtp_stack_frame*) pdata->stack->data;
479 : 106046 : if (frame->parser->characters_handler)
480 : : {
481 : 103672 : gpointer result = NULL;
482 : :
483 : 103672 : pdata->parsing_ok &=
484 : 103672 : frame->parser->characters_handler (frame->data_from_children,
485 : : frame->data_for_children,
486 : : pdata->global_data,
487 : : &result,
488 : : (gchar*) text,
489 : : len);
490 : 103672 : if (pdata->parsing_ok && result)
491 : : {
492 : : /* push the result onto the current "child" list. */
493 : 0 : sixtp_child_result* child_data = g_new0 (sixtp_child_result, 1);
494 : :
495 : 0 : child_data->type = SIXTP_CHILD_RESULT_CHARS;
496 : 0 : child_data->tag = NULL;
497 : 0 : child_data->data = result;
498 : 0 : child_data->should_cleanup = TRUE;
499 : 0 : child_data->cleanup_handler = frame->parser->cleanup_chars;
500 : 0 : child_data->fail_handler = frame->parser->chars_fail_handler;
501 : 0 : frame->data_from_children = g_slist_prepend (frame->data_from_children,
502 : : child_data);
503 : : }
504 : : }
505 : 106046 : }
506 : :
507 : : void
508 : 50438 : sixtp_sax_end_handler (void* user_data, const xmlChar* name)
509 : : {
510 : 50438 : sixtp_sax_data* pdata = (sixtp_sax_data*) user_data;
511 : : sixtp_stack_frame* current_frame;
512 : : sixtp_stack_frame* parent_frame;
513 : 50438 : sixtp_child_result* child_result_data = NULL;
514 : 50438 : gchar* end_tag = NULL;
515 : :
516 : 50438 : current_frame = (sixtp_stack_frame*) pdata->stack->data;
517 : 50438 : parent_frame = (sixtp_stack_frame*) pdata->stack->next->data;
518 : :
519 : : /* time to make sure we got the right closing tag. Is this really
520 : : necessary? */
521 : 50438 : if (g_strcmp0 (current_frame->tag, (gchar*) name) != 0)
522 : : {
523 : 0 : PWARN ("bad closing tag (start <%s>, end <%s>)", current_frame->tag, name);
524 : 0 : pdata->parsing_ok = FALSE;
525 : :
526 : : /* See if we're just off by one and try to recover */
527 : 0 : if (g_strcmp0 (parent_frame->tag, (gchar*) name) == 0)
528 : : {
529 : 0 : pdata->stack = sixtp_pop_and_destroy_frame (pdata->stack);
530 : 0 : current_frame = (sixtp_stack_frame*) pdata->stack->data;
531 : 0 : parent_frame = (sixtp_stack_frame*) pdata->stack->next->data;
532 : 0 : PWARN ("found matching start <%s> tag up one level", name);
533 : : }
534 : : }
535 : :
536 : : /* tag's OK, proceed. */
537 : 50438 : if (current_frame->parser->end_handler)
538 : : {
539 : 50362 : pdata->parsing_ok &=
540 : 50362 : current_frame->parser->end_handler (current_frame->data_for_children,
541 : : current_frame->data_from_children,
542 : : parent_frame->data_from_children,
543 : : parent_frame->data_for_children,
544 : : pdata->global_data,
545 : : ¤t_frame->frame_data,
546 : 50362 : current_frame->tag);
547 : : }
548 : :
549 : 50438 : if (current_frame->frame_data)
550 : : {
551 : : /* push the result onto the parent's child result list. */
552 : 3113 : child_result_data = g_new (sixtp_child_result, 1);
553 : :
554 : 3113 : child_result_data->type = SIXTP_CHILD_RESULT_NODE;
555 : 3113 : child_result_data->tag = g_strdup (current_frame->tag);
556 : 3113 : child_result_data->data = current_frame->frame_data;
557 : 3113 : child_result_data->should_cleanup = TRUE;
558 : 3113 : child_result_data->cleanup_handler = current_frame->parser->cleanup_result;
559 : 3113 : child_result_data->fail_handler =
560 : 3113 : current_frame->parser->result_fail_handler;
561 : 3113 : parent_frame->data_from_children =
562 : 3113 : g_slist_prepend (parent_frame->data_from_children, child_result_data);
563 : : }
564 : :
565 : : /* grab it before it goes away - we own the reference */
566 : 50438 : end_tag = current_frame->tag;
567 : :
568 : 50438 : DEBUG ("Finished with end of <%s>", end_tag ? end_tag : "(null)");
569 : :
570 : : /*sixtp_print_frame_stack(pdata->stack, stderr);*/
571 : :
572 : 50438 : pdata->stack = sixtp_pop_and_destroy_frame (pdata->stack);
573 : :
574 : : /* reset pointer after stack pop */
575 : 50438 : current_frame = (sixtp_stack_frame*) pdata->stack->data;
576 : : /* reset the parent, checking to see if we're at the top level node */
577 : 166 : parent_frame = (sixtp_stack_frame*)
578 : 50438 : ((g_slist_length (pdata->stack) > 1) ? (pdata->stack->next->data) : NULL);
579 : :
580 : 50438 : if (current_frame->parser->after_child)
581 : : {
582 : : /* reset pointer after stack pop */
583 : 746 : GSList* parent_data_from_children = NULL;
584 : 746 : gpointer parent_data_for_children = NULL;
585 : :
586 : 746 : if (parent_frame)
587 : : {
588 : : /* we're not in the top level node */
589 : 746 : sixtp_stack_frame* parent_frame =
590 : 746 : (sixtp_stack_frame*) pdata->stack->next->data;
591 : 746 : parent_data_from_children = static_cast<decltype (parent_data_from_children)>
592 : : (parent_frame->data_for_children);
593 : : }
594 : :
595 : 746 : pdata->parsing_ok &=
596 : 746 : current_frame->parser->after_child (current_frame->data_for_children,
597 : : current_frame->data_from_children,
598 : : parent_data_from_children,
599 : : parent_data_for_children,
600 : : pdata->global_data,
601 : : & (current_frame->frame_data),
602 : 746 : current_frame->tag,
603 : : end_tag,
604 : : child_result_data);
605 : : }
606 : :
607 : 50438 : g_free (end_tag);
608 : 50438 : }
609 : :
610 : : xmlEntityPtr
611 : 0 : sixtp_sax_get_entity_handler (void* user_data, const xmlChar* name)
612 : : {
613 : 0 : return xmlGetPredefinedEntity (name);
614 : : }
615 : :
616 : :
617 : : void
618 : 0 : sixtp_handle_catastrophe (sixtp_sax_data* sax_data)
619 : : {
620 : : /* Something has gone wrong. To handle it, we have to traverse the
621 : : stack, calling, at each level, the frame failure handler (the
622 : : handler for the current, unfinished block) and then the sibling
623 : : handlers. The order is reverse chronological - oldest child
624 : : results cleaned up last. This holds overall as well, stack
625 : : frames are cleaned up in their order on the stack which will be
626 : : youngest to oldest. */
627 : :
628 : : GSList* lp;
629 : 0 : GSList** stack = & (sax_data->stack);
630 : :
631 : 0 : g_critical ("parse failed at:");
632 : 0 : sixtp_print_frame_stack (sax_data->stack, stderr);
633 : :
634 : 0 : while (*stack)
635 : : {
636 : 0 : sixtp_stack_frame* current_frame = (sixtp_stack_frame*) (*stack)->data;
637 : :
638 : : /* cleanup the current frame */
639 : 0 : if (current_frame->parser->fail_handler)
640 : : {
641 : : GSList* sibling_data;
642 : : gpointer parent_data;
643 : :
644 : 0 : if ((*stack)->next == NULL)
645 : : {
646 : : /* This is the top of the stack... */
647 : 0 : parent_data = NULL;
648 : 0 : sibling_data = NULL;
649 : : }
650 : : else
651 : : {
652 : 0 : sixtp_stack_frame* parent_frame =
653 : 0 : (sixtp_stack_frame*) (*stack)->next->data;
654 : 0 : parent_data = parent_frame->data_for_children;
655 : 0 : sibling_data = parent_frame->data_from_children;
656 : : }
657 : :
658 : 0 : current_frame->parser->fail_handler (current_frame->data_for_children,
659 : : current_frame->data_from_children,
660 : : sibling_data,
661 : : parent_data,
662 : : sax_data->global_data,
663 : : ¤t_frame->frame_data,
664 : 0 : current_frame->tag);
665 : : }
666 : :
667 : : /* now cleanup any children's results */
668 : 0 : for (lp = current_frame->data_from_children; lp; lp = lp->next)
669 : : {
670 : 0 : sixtp_child_result* cresult = (sixtp_child_result*) lp->data;
671 : 0 : if (cresult->fail_handler)
672 : : {
673 : 0 : cresult->fail_handler (cresult);
674 : : }
675 : : }
676 : :
677 : 0 : if ((*stack)->next == NULL)
678 : : {
679 : : /* This is the top of the stack. The top frame seems to want to
680 : : * be destroyed by sixtp_context_destroy. */
681 : 0 : break;
682 : : }
683 : :
684 : 0 : *stack = sixtp_pop_and_destroy_frame (*stack);
685 : : }
686 : 0 : }
687 : :
688 : : static gboolean
689 : 0 : gnc_bad_xml_end_handler (gpointer data_for_children,
690 : : GSList* data_from_children, GSList* sibling_data,
691 : : gpointer parent_data, gpointer global_data,
692 : : gpointer* result, const gchar* tag)
693 : : {
694 : 0 : return TRUE;
695 : : }
696 : :
697 : : static gboolean
698 : 166 : sixtp_parse_file_common (sixtp* sixtp,
699 : : xmlParserCtxtPtr xml_context,
700 : : gpointer data_for_top_level,
701 : : gpointer global_data,
702 : : gpointer* parse_result)
703 : : {
704 : : sixtp_parser_context* ctxt;
705 : : int parse_ret;
706 : :
707 : 166 : if (! (ctxt = sixtp_context_new (sixtp, global_data, data_for_top_level)))
708 : : {
709 : 0 : g_critical ("sixtp_context_new returned null");
710 : 0 : return FALSE;
711 : : }
712 : :
713 : 166 : ctxt->data.saxParserCtxt = xml_context;
714 : 166 : ctxt->data.saxParserCtxt->sax = &ctxt->handler;
715 : 166 : ctxt->data.saxParserCtxt->userData = &ctxt->data;
716 : 166 : ctxt->data.bad_xml_parser = sixtp_dom_parser_new (gnc_bad_xml_end_handler,
717 : : NULL, NULL);
718 : 166 : parse_ret = xmlParseDocument (ctxt->data.saxParserCtxt);
719 : : //xmlSAXUserParseFile(&ctxt->handler, &ctxt->data, filename);
720 : :
721 : 166 : sixtp_context_run_end_handler (ctxt);
722 : :
723 : 166 : if (parse_ret == 0 && ctxt->data.parsing_ok)
724 : : {
725 : 166 : if (parse_result)
726 : 166 : *parse_result = ctxt->top_frame->frame_data;
727 : 166 : sixtp_context_destroy (ctxt);
728 : 166 : return TRUE;
729 : : }
730 : : else
731 : : {
732 : 0 : if (parse_result)
733 : 0 : *parse_result = NULL;
734 : 0 : if (g_slist_length (ctxt->data.stack) > 1)
735 : 0 : sixtp_handle_catastrophe (&ctxt->data);
736 : 0 : sixtp_context_destroy (ctxt);
737 : 0 : return FALSE;
738 : : }
739 : : }
740 : :
741 : : gboolean
742 : 145 : sixtp_parse_file (sixtp* sixtp,
743 : : const char* filename,
744 : : gpointer data_for_top_level,
745 : : gpointer global_data,
746 : : gpointer* parse_result)
747 : : {
748 : : gboolean ret;
749 : : xmlParserCtxtPtr context;
750 : :
751 : : #ifdef G_OS_WIN32
752 : : {
753 : : gchar* conv_name = g_win32_locale_filename_from_utf8 (filename);
754 : : if (!conv_name)
755 : : {
756 : : PWARN ("Could not convert '%s' to system codepage", filename);
757 : : return FALSE;
758 : : }
759 : : context = xmlCreateFileParserCtxt (conv_name);
760 : : g_free (conv_name);
761 : : }
762 : : #else
763 : 145 : context = xmlCreateFileParserCtxt (filename);
764 : : #endif
765 : 145 : ret = sixtp_parse_file_common (sixtp, context, data_for_top_level,
766 : : global_data, parse_result);
767 : 145 : return ret;
768 : : }
769 : :
770 : : /* Call back function for libxml2 to read from compressed or uncompressed stream */
771 : : static int
772 : 389 : sixtp_parser_read (void* context, char* buffer, int len)
773 : : {
774 : : int ret;
775 : :
776 : 389 : ret = fread (&buffer[0], sizeof (char), len, (FILE*) context);
777 : 389 : if (ret < 0)
778 : 0 : PWARN ("Error reading XML file");
779 : 389 : return ret;
780 : : }
781 : :
782 : : gboolean
783 : 21 : sixtp_parse_fd (sixtp* sixtp,
784 : : FILE* fd,
785 : : gpointer data_for_top_level,
786 : : gpointer global_data,
787 : : gpointer* parse_result)
788 : : {
789 : : gboolean ret;
790 : 21 : xmlParserCtxtPtr context = xmlCreateIOParserCtxt (NULL, NULL,
791 : : sixtp_parser_read, NULL /*no close */, fd,
792 : : XML_CHAR_ENCODING_NONE);
793 : 21 : ret = sixtp_parse_file_common (sixtp, context, data_for_top_level,
794 : : global_data, parse_result);
795 : 21 : return ret;
796 : : }
797 : :
798 : : gboolean
799 : 0 : sixtp_parse_buffer (sixtp* sixtp,
800 : : char* bufp,
801 : : int bufsz,
802 : : gpointer data_for_top_level,
803 : : gpointer global_data,
804 : : gpointer* parse_result)
805 : : {
806 : : gboolean ret;
807 : 0 : xmlParserCtxtPtr context = xmlCreateMemoryParserCtxt (bufp, bufsz);
808 : 0 : ret = sixtp_parse_file_common (sixtp, context, data_for_top_level,
809 : : global_data, parse_result);
810 : 0 : return ret;
811 : : }
812 : :
813 : : gboolean
814 : 0 : sixtp_parse_push (sixtp* sixtp,
815 : : sixtp_push_handler push_handler,
816 : : gpointer push_user_data,
817 : : gpointer data_for_top_level,
818 : : gpointer global_data,
819 : : gpointer* parse_result)
820 : : {
821 : : sixtp_parser_context* ctxt;
822 : : xmlParserCtxtPtr xml_context;
823 : :
824 : 0 : if (!push_handler)
825 : : {
826 : 0 : g_critical ("No push handler specified");
827 : 0 : return FALSE;
828 : : }
829 : :
830 : 0 : if (! (ctxt = sixtp_context_new (sixtp, global_data, data_for_top_level)))
831 : : {
832 : 0 : g_critical ("sixtp_context_new returned null");
833 : 0 : return FALSE;
834 : : }
835 : :
836 : 0 : xml_context = xmlCreatePushParserCtxt (&ctxt->handler, &ctxt->data,
837 : : NULL, 0, NULL);
838 : 0 : ctxt->data.saxParserCtxt = xml_context;
839 : 0 : ctxt->data.bad_xml_parser = sixtp_dom_parser_new (gnc_bad_xml_end_handler,
840 : : NULL, NULL);
841 : :
842 : 0 : (*push_handler) (xml_context, push_user_data);
843 : :
844 : 0 : sixtp_context_run_end_handler (ctxt);
845 : :
846 : 0 : if (ctxt->data.parsing_ok)
847 : : {
848 : 0 : if (parse_result)
849 : 0 : *parse_result = ctxt->top_frame->frame_data;
850 : 0 : sixtp_context_destroy (ctxt);
851 : 0 : return TRUE;
852 : : }
853 : : else
854 : : {
855 : 0 : if (parse_result)
856 : 0 : *parse_result = NULL;
857 : 0 : if (g_slist_length (ctxt->data.stack) > 1)
858 : 0 : sixtp_handle_catastrophe (&ctxt->data);
859 : 0 : sixtp_context_destroy (ctxt);
860 : 0 : return FALSE;
861 : : }
862 : : }
863 : :
864 : : /***********************************************************************/
865 : : static gboolean
866 : 86 : eat_whitespace (char** cursor)
867 : : {
868 : 129 : while (**cursor && isspace (**cursor))
869 : : {
870 : 43 : (*cursor)++;
871 : : }
872 : :
873 : 86 : if (**cursor == '\0')
874 : : {
875 : 0 : return FALSE;
876 : : }
877 : : else
878 : : {
879 : 86 : return TRUE;
880 : : }
881 : : }
882 : :
883 : : static gboolean
884 : 85 : search_for (unsigned char marker, char** cursor)
885 : : {
886 : 2118 : while (**cursor &&** cursor != marker)
887 : : {
888 : 2033 : (*cursor)++;
889 : : }
890 : :
891 : 85 : if (**cursor == '\0')
892 : : {
893 : 0 : return FALSE;
894 : : }
895 : : else
896 : : {
897 : 85 : (*cursor)++;
898 : 85 : return TRUE;
899 : : }
900 : : }
901 : :
902 : : QofBookFileType
903 : 39 : gnc_is_our_xml_file (const char* filename, gboolean* with_encoding)
904 : : {
905 : 39 : FILE* f = NULL;
906 : : char first_chunk[256];
907 : : ssize_t num_read;
908 : :
909 : 39 : g_return_val_if_fail (filename, GNC_BOOK_NOT_OURS);
910 : :
911 : 39 : f = g_fopen (filename, "r");
912 : 39 : if (f == NULL)
913 : : {
914 : 0 : return GNC_BOOK_NOT_OURS;
915 : : }
916 : :
917 : 39 : num_read = fread (first_chunk, sizeof (char), sizeof (first_chunk) - 1, f);
918 : 39 : fclose (f);
919 : :
920 : 39 : if (num_read == 0)
921 : : {
922 : 0 : return GNC_BOOK_NOT_OURS;
923 : : }
924 : :
925 : 39 : first_chunk[num_read] = '\0';
926 : :
927 : 39 : return gnc_is_our_first_xml_chunk (first_chunk, with_encoding);
928 : : }
929 : :
930 : : QofBookFileType
931 : 43 : gnc_is_our_first_xml_chunk (char* chunk, gboolean* with_encoding)
932 : : {
933 : 43 : char* cursor = NULL;
934 : : size_t n;
935 : :
936 : 43 : if (with_encoding)
937 : : {
938 : 21 : *with_encoding = FALSE;
939 : : }
940 : :
941 : 43 : cursor = chunk;
942 : :
943 : 43 : if (!eat_whitespace (&cursor))
944 : : {
945 : 0 : return GNC_BOOK_NOT_OURS;
946 : : }
947 : :
948 : 43 : if (strncmp (cursor, "<?xml", 5) == 0)
949 : : {
950 : 43 : if (!search_for ('>', &cursor))
951 : : {
952 : 0 : return GNC_BOOK_NOT_OURS;
953 : : }
954 : :
955 : 43 : if (!eat_whitespace (&cursor))
956 : : {
957 : 0 : return GNC_BOOK_NOT_OURS;
958 : : }
959 : :
960 : 43 : if (*cursor != '<')
961 : : {
962 : 0 : return GNC_BOOK_NOT_OURS;
963 : : }
964 : :
965 : 43 : n = strlen (gnc_v2_xml_version_string);
966 : 43 : if ((strncmp (cursor + 1, gnc_v2_xml_version_string, n) == 0)
967 : 43 : && isspace (* (cursor + 1 + n)))
968 : : {
969 : 43 : if (with_encoding)
970 : : {
971 : 21 : *cursor = '\0';
972 : 21 : cursor = chunk;
973 : 42 : while (search_for ('e', &cursor))
974 : : {
975 : 42 : if (strncmp (cursor, "ncoding=", 8) == 0)
976 : : {
977 : 21 : *with_encoding = TRUE;
978 : 21 : break;
979 : : }
980 : : }
981 : : }
982 : 43 : return GNC_BOOK_XML2_FILE;
983 : : }
984 : :
985 : 0 : if (strncmp (cursor, "<gnc>", strlen ("<gnc>")) == 0)
986 : 0 : return GNC_BOOK_XML1_FILE;
987 : :
988 : : /* If it doesn't match any of the above but has '<gnc-v...', it must */
989 : : /* be a later version */
990 : 0 : if (strncmp (cursor, "<gnc-v", strlen ("<gnc-v")) == 0)
991 : 0 : return GNC_BOOK_POST_XML2_0_0_FILE;
992 : :
993 : 0 : return GNC_BOOK_NOT_OURS;
994 : : }
995 : :
996 : 0 : return GNC_BOOK_NOT_OURS;
997 : : }
998 : :
999 : : void
1000 : 2324 : sixtp_run_callback (sixtp_gdv2* data, const char* type)
1001 : : {
1002 : 2324 : if (data->countCallback)
1003 : : {
1004 : 1906 : data->countCallback (data, type);
1005 : : }
1006 : 2324 : }
1007 : :
1008 : : /************************* END OF FILE *********************************/
|