GnuCash c935c2f+
Loading...
Searching...
No Matches
sixtp.cpp
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"
44static QofLogModule log_module = "gnc.backend.file.sixtp";
45
46extern const gchar* gnc_v2_xml_version_string; /* see io-gncxml-v2.c */
47
48/************************************************************************/
49gboolean
50is_child_result_from_node_named (sixtp_child_result* cr, const char* tag)
51{
52 return ((cr->type == SIXTP_CHILD_RESULT_NODE)
53 &&
54 (g_strcmp0 (cr->tag, tag) == 0));
55}
56
57void
58sixtp_child_free_data (sixtp_child_result* result)
59{
60 if (result->data) g_free (result->data);
61}
62
63void
64sixtp_child_result_destroy (sixtp_child_result* r)
65{
66 if (r->should_cleanup && r->cleanup_handler)
67 {
68 r->cleanup_handler (r);
69 }
70 if (r->type == SIXTP_CHILD_RESULT_NODE) g_free (r->tag);
71 g_free (r);
72}
73
74void
75sixtp_child_result_print (sixtp_child_result* cr, FILE* f)
76{
77 fprintf (f, "((tag %s) (data %p))",
78 cr->tag ? cr->tag : "(null)",
79 cr->data);
80}
81
82/************************************************************************/
83
84
85void
86sixtp_set_start (sixtp* parser, sixtp_start_handler start_handler)
87{
88 parser->start_handler = start_handler;
89}
90
91void
92sixtp_set_before_child (sixtp* parser, sixtp_before_child_handler handler)
93{
94 parser->before_child = handler;
95}
96
97void
98sixtp_set_after_child (sixtp* parser, sixtp_after_child_handler handler)
99{
100 parser->after_child = handler;
101}
102
103void
104sixtp_set_end (sixtp* parser, sixtp_end_handler end_handler)
105{
106 parser->end_handler = end_handler;
107}
108
109void
110sixtp_set_chars (sixtp* parser, sixtp_characters_handler char_handler)
111{
112 parser->characters_handler = char_handler;
113}
114
115void
116sixtp_set_cleanup_result (sixtp* parser, sixtp_result_handler handler)
117{
118 parser->cleanup_result = handler;
119}
120
121void
122sixtp_set_cleanup_chars (sixtp* parser, sixtp_result_handler handler)
123{
124 parser->cleanup_chars = handler;
125}
126
127void
128sixtp_set_fail (sixtp* parser,
129 sixtp_fail_handler handler)
130{
131 parser->fail_handler = handler;
132}
133
134void
135sixtp_set_result_fail (sixtp* parser, sixtp_result_handler handler)
136{
137 parser->result_fail_handler = handler;
138}
139
140void
141sixtp_set_chars_fail (sixtp* parser, sixtp_result_handler handler)
142{
143 parser->chars_fail_handler = handler;
144}
145
146sixtp*
147sixtp_new (void)
148{
149 sixtp* s = g_new0 (sixtp, 1);
150
151 if (s)
152 {
153 s->child_parsers = g_hash_table_new (g_str_hash, g_str_equal);
154 if (!s->child_parsers)
155 {
156 g_free (s);
157 s = NULL;
158 }
159 }
160 return (s);
161}
162
163sixtp*
164sixtp_set_any (sixtp* tochange, int cleanup, ...)
165{
166 va_list ap;
167 sixtp_handler_type type;
168
169 if (!tochange)
170 {
171 PWARN ("Null tochange passed");
172 return NULL;
173 }
174
175 va_start (ap, cleanup);
176
177 do
178 {
179 type = static_cast<decltype (type)> (va_arg (ap, int));
180
181 switch (type)
182 {
183 case SIXTP_NO_MORE_HANDLERS:
184 va_end (ap);
185 return tochange;
186
187 case SIXTP_START_HANDLER_ID:
188 sixtp_set_start (tochange, va_arg (ap, sixtp_start_handler));
189 break;
190
191 case SIXTP_BEFORE_CHILD_HANDLER_ID:
192 sixtp_set_before_child (tochange,
193 va_arg (ap, sixtp_before_child_handler));
194 break;
195
196 case SIXTP_AFTER_CHILD_HANDLER_ID:
197 sixtp_set_after_child (tochange,
198 va_arg (ap, sixtp_after_child_handler));
199 break;
200
201 case SIXTP_END_HANDLER_ID:
202 sixtp_set_end (tochange, va_arg (ap, sixtp_end_handler));
203 break;
204
205 case SIXTP_CHARACTERS_HANDLER_ID:
206 sixtp_set_chars (tochange, va_arg (ap, sixtp_characters_handler));
207 break;
208
209 case SIXTP_FAIL_HANDLER_ID:
210 sixtp_set_fail (tochange, va_arg (ap, sixtp_fail_handler));
211 break;
212
213 case SIXTP_CLEANUP_RESULT_ID:
214 sixtp_set_cleanup_result (tochange,
215 va_arg (ap, sixtp_result_handler));
216 break;
217
218 case SIXTP_CLEANUP_CHARS_ID:
219 sixtp_set_cleanup_chars (tochange,
220 va_arg (ap, sixtp_result_handler));
221 break;
222
223 case SIXTP_RESULT_FAIL_ID:
224 sixtp_set_result_fail (tochange, va_arg (ap, sixtp_result_handler));
225 break;
226
227 case SIXTP_CHARS_FAIL_ID:
228 sixtp_set_chars_fail (tochange, va_arg (ap, sixtp_result_handler));
229 break;
230
231 default:
232 va_end (ap);
233 g_critical ("Bogus sixtp type %d", type);
234 if (cleanup)
235 {
236 sixtp_destroy (tochange);
237 }
238 return NULL;
239 }
240 }
241 while (1);
242
243 va_end (ap);
244 return tochange;
245}
246
247static void sixtp_destroy_child (gpointer key, gpointer value,
248 gpointer user_data);
249
250static void
251sixtp_destroy_node (sixtp* sp, GHashTable* corpses)
252{
253 g_return_if_fail (sp);
254 g_return_if_fail (corpses);
255 g_hash_table_foreach (sp->child_parsers, sixtp_destroy_child, corpses);
256 g_hash_table_destroy (sp->child_parsers);
257 g_free (sp);
258}
259
260static void
261sixtp_destroy_child (gpointer key, gpointer value, gpointer user_data)
262{
263 GHashTable* corpses = (GHashTable*) user_data;
264 sixtp* child = (sixtp*) value;
265 gpointer lookup_key;
266 gpointer lookup_value;
267
268 DEBUG ("Killing sixtp child under key <%s>", key ? (char*) key : "(null)");
269
270 if (!corpses)
271 {
272 g_critical ("no corpses in sixtp_destroy_child <%s>",
273 key ? (char*) key : "(null)");
274 g_free (key);
275 return;
276 }
277 if (!child)
278 {
279 g_critical ("no child in sixtp_destroy_child <%s>",
280 key ? (char*) key : "");
281 g_free (key);
282 return;
283 }
284 g_free (key);
285
286 if (!g_hash_table_lookup_extended (corpses, (gconstpointer) child,
287 &lookup_key, &lookup_value))
288 {
289 /* haven't killed this one yet. */
290 g_hash_table_insert (corpses, child, (gpointer) 1);
291 sixtp_destroy_node (child, corpses);
292 }
293}
294
295void
296sixtp_destroy (sixtp* sp)
297{
298 GHashTable* corpses;
299 g_return_if_fail (sp);
300 corpses = g_hash_table_new (g_direct_hash, g_direct_equal);
301 sixtp_destroy_node (sp, corpses);
302 g_hash_table_destroy (corpses);
303}
304
305
306/***********************************************************************/
307
308gboolean
309sixtp_add_sub_parser (sixtp* parser, const gchar* tag, sixtp* sub_parser)
310{
311 g_return_val_if_fail (parser, FALSE);
312 g_return_val_if_fail (tag, FALSE);
313 g_return_val_if_fail (sub_parser, FALSE);
314
315 g_hash_table_insert (parser->child_parsers,
316 g_strdup (tag), (gpointer) sub_parser);
317 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 */
324sixtp*
325sixtp_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 va_start (ap, cleanup);
333
334 have_error = 0;
335
336 if (!tochange)
337 {
338 have_error = 1;
339 }
340
341 do
342 {
343 tag = va_arg (ap, char*);
344 if (!tag)
345 {
346 break;
347 }
348
349 handler = va_arg (ap, sixtp*);
350 if (!handler)
351 {
352 PWARN ("Handler for tag %s is null",
353 tag ? tag : "(null)");
354
355 if (cleanup)
356 {
357 sixtp_destroy (tochange);
358 tochange = NULL;
359 have_error = 1;
360 }
361 else
362 {
363 va_end (ap);
364 return NULL;
365 }
366 }
367
368 if (have_error)
369 {
370 sixtp_destroy (handler);
371 }
372 else
373 {
374 sixtp_add_sub_parser (tochange, tag, handler);
375 }
376 }
377 while (1);
378
379 va_end (ap);
380 return tochange;
381}
382
383/************************************************************************/
384
385void
386sixtp_sax_start_handler (void* user_data,
387 const xmlChar* name,
388 const xmlChar** attrs)
389{
390 sixtp_sax_data* pdata = (sixtp_sax_data*) user_data;
391 sixtp_stack_frame* current_frame = NULL;
392 sixtp* current_parser = NULL;
393 sixtp* next_parser = NULL;
394 gchar* next_parser_tag = NULL;
395 gboolean lookup_success = FALSE;
396 sixtp_stack_frame* new_frame = NULL;
397
398 current_frame = (sixtp_stack_frame*) pdata->stack->data;
399 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 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 if (!lookup_success)
411 {
412 /* magic catch all value */
413 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 if (!lookup_success)
418 {
419 g_critical ("Tag <%s> not allowed in current context.",
420 name ? (char*) name : "(null)");
421 pdata->parsing_ok = FALSE;
422 next_parser = pdata->bad_xml_parser;
423 }
424 }
425
426 if (current_frame->parser->before_child)
427 {
428 GSList* parent_data_from_children = NULL;
429 gpointer parent_data_for_children = NULL;
430
431 if (g_slist_length (pdata->stack) > 1)
432 {
433 /* we're not in the top level node */
434 sixtp_stack_frame* parent_frame =
435 (sixtp_stack_frame*) pdata->stack->next->data;
436 parent_data_from_children = static_cast<decltype (parent_data_from_children)>
437 (parent_frame->data_from_children);
438 }
439
440 pdata->parsing_ok &=
441 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 current_frame->tag,
448 (gchar*) name);
449 }
450
451 /* now allocate the new stack frame and shift to it */
452 new_frame = sixtp_stack_frame_new (next_parser, g_strdup ((char*) name));
453
454 new_frame->line = xmlSAX2GetLineNumber (pdata->saxParserCtxt);
455 new_frame->col = xmlSAX2GetColumnNumber (pdata->saxParserCtxt);
456
457 pdata->stack = g_slist_prepend (pdata->stack, (gpointer) new_frame);
458
459 if (next_parser->start_handler)
460 {
461 pdata->parsing_ok &=
462 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}
471
472void
473sixtp_sax_characters_handler (void* user_data, const xmlChar* text, int len)
474{
475 sixtp_sax_data* pdata = (sixtp_sax_data*) user_data;
476 sixtp_stack_frame* frame;
477
478 frame = (sixtp_stack_frame*) pdata->stack->data;
479 if (frame->parser->characters_handler)
480 {
481 gpointer result = NULL;
482
483 pdata->parsing_ok &=
484 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 if (pdata->parsing_ok && result)
491 {
492 /* push the result onto the current "child" list. */
493 sixtp_child_result* child_data = g_new0 (sixtp_child_result, 1);
494
495 child_data->type = SIXTP_CHILD_RESULT_CHARS;
496 child_data->tag = NULL;
497 child_data->data = result;
498 child_data->should_cleanup = TRUE;
499 child_data->cleanup_handler = frame->parser->cleanup_chars;
500 child_data->fail_handler = frame->parser->chars_fail_handler;
501 frame->data_from_children = g_slist_prepend (frame->data_from_children,
502 child_data);
503 }
504 }
505}
506
507void
508sixtp_sax_end_handler (void* user_data, const xmlChar* name)
509{
510 sixtp_sax_data* pdata = (sixtp_sax_data*) user_data;
511 sixtp_stack_frame* current_frame;
512 sixtp_stack_frame* parent_frame;
513 sixtp_child_result* child_result_data = NULL;
514 gchar* end_tag = NULL;
515
516 current_frame = (sixtp_stack_frame*) pdata->stack->data;
517 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 if (g_strcmp0 (current_frame->tag, (gchar*) name) != 0)
522 {
523 PWARN ("bad closing tag (start <%s>, end <%s>)", current_frame->tag, name);
524 pdata->parsing_ok = FALSE;
525
526 /* See if we're just off by one and try to recover */
527 if (g_strcmp0 (parent_frame->tag, (gchar*) name) == 0)
528 {
529 pdata->stack = sixtp_pop_and_destroy_frame (pdata->stack);
530 current_frame = (sixtp_stack_frame*) pdata->stack->data;
531 parent_frame = (sixtp_stack_frame*) pdata->stack->next->data;
532 PWARN ("found matching start <%s> tag up one level", name);
533 }
534 }
535
536 /* tag's OK, proceed. */
537 if (current_frame->parser->end_handler)
538 {
539 pdata->parsing_ok &=
540 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 &current_frame->frame_data,
546 current_frame->tag);
547 }
548
549 if (current_frame->frame_data)
550 {
551 /* push the result onto the parent's child result list. */
552 child_result_data = g_new (sixtp_child_result, 1);
553
554 child_result_data->type = SIXTP_CHILD_RESULT_NODE;
555 child_result_data->tag = g_strdup (current_frame->tag);
556 child_result_data->data = current_frame->frame_data;
557 child_result_data->should_cleanup = TRUE;
558 child_result_data->cleanup_handler = current_frame->parser->cleanup_result;
559 child_result_data->fail_handler =
560 current_frame->parser->result_fail_handler;
561 parent_frame->data_from_children =
562 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 end_tag = current_frame->tag;
567
568 DEBUG ("Finished with end of <%s>", end_tag ? end_tag : "(null)");
569
570 /*sixtp_print_frame_stack(pdata->stack, stderr);*/
571
572 pdata->stack = sixtp_pop_and_destroy_frame (pdata->stack);
573
574 /* reset pointer after stack pop */
575 current_frame = (sixtp_stack_frame*) pdata->stack->data;
576 /* reset the parent, checking to see if we're at the top level node */
577 parent_frame = (sixtp_stack_frame*)
578 ((g_slist_length (pdata->stack) > 1) ? (pdata->stack->next->data) : NULL);
579
580 if (current_frame->parser->after_child)
581 {
582 /* reset pointer after stack pop */
583 GSList* parent_data_from_children = NULL;
584 gpointer parent_data_for_children = NULL;
585
586 if (parent_frame)
587 {
588 /* we're not in the top level node */
589 sixtp_stack_frame* parent_frame =
590 (sixtp_stack_frame*) pdata->stack->next->data;
591 parent_data_from_children = static_cast<decltype (parent_data_from_children)>
592 (parent_frame->data_for_children);
593 }
594
595 pdata->parsing_ok &=
596 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 current_frame->tag,
603 end_tag,
604 child_result_data);
605 }
606
607 g_free (end_tag);
608}
609
610xmlEntityPtr
611sixtp_sax_get_entity_handler (void* user_data, const xmlChar* name)
612{
613 return xmlGetPredefinedEntity (name);
614}
615
616
617void
618sixtp_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 GSList** stack = & (sax_data->stack);
630
631 g_critical ("parse failed at:");
632 sixtp_print_frame_stack (sax_data->stack, stderr);
633
634 while (*stack)
635 {
636 sixtp_stack_frame* current_frame = (sixtp_stack_frame*) (*stack)->data;
637
638 /* cleanup the current frame */
639 if (current_frame->parser->fail_handler)
640 {
641 GSList* sibling_data;
642 gpointer parent_data;
643
644 if ((*stack)->next == NULL)
645 {
646 /* This is the top of the stack... */
647 parent_data = NULL;
648 sibling_data = NULL;
649 }
650 else
651 {
652 sixtp_stack_frame* parent_frame =
653 (sixtp_stack_frame*) (*stack)->next->data;
654 parent_data = parent_frame->data_for_children;
655 sibling_data = parent_frame->data_from_children;
656 }
657
658 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 &current_frame->frame_data,
664 current_frame->tag);
665 }
666
667 /* now cleanup any children's results */
668 for (lp = current_frame->data_from_children; lp; lp = lp->next)
669 {
670 sixtp_child_result* cresult = (sixtp_child_result*) lp->data;
671 if (cresult->fail_handler)
672 {
673 cresult->fail_handler (cresult);
674 }
675 }
676
677 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 break;
682 }
683
684 *stack = sixtp_pop_and_destroy_frame (*stack);
685 }
686}
687
688static gboolean
689gnc_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 return TRUE;
695}
696
697static gboolean
698sixtp_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 if (! (ctxt = sixtp_context_new (sixtp, global_data, data_for_top_level)))
708 {
709 g_critical ("sixtp_context_new returned null");
710 return FALSE;
711 }
712
713 ctxt->data.saxParserCtxt = xml_context;
714 ctxt->data.saxParserCtxt->sax = &ctxt->handler;
715 ctxt->data.saxParserCtxt->userData = &ctxt->data;
716 ctxt->data.bad_xml_parser = sixtp_dom_parser_new (gnc_bad_xml_end_handler,
717 NULL, NULL);
718 parse_ret = xmlParseDocument (ctxt->data.saxParserCtxt);
719 //xmlSAXUserParseFile(&ctxt->handler, &ctxt->data, filename);
720
721 sixtp_context_run_end_handler (ctxt);
722
723 if (parse_ret == 0 && ctxt->data.parsing_ok)
724 {
725 if (parse_result)
726 *parse_result = ctxt->top_frame->frame_data;
727 sixtp_context_destroy (ctxt);
728 return TRUE;
729 }
730 else
731 {
732 if (parse_result)
733 *parse_result = NULL;
734 if (g_slist_length (ctxt->data.stack) > 1)
735 sixtp_handle_catastrophe (&ctxt->data);
736 sixtp_context_destroy (ctxt);
737 return FALSE;
738 }
739}
740
741gboolean
742sixtp_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 context = xmlCreateFileParserCtxt (filename);
764#endif
765 ret = sixtp_parse_file_common (sixtp, context, data_for_top_level,
766 global_data, parse_result);
767 return ret;
768}
769
770/* Call back function for libxml2 to read from compressed or uncompressed stream */
771static int
772sixtp_parser_read (void* context, char* buffer, int len)
773{
774 int ret;
775
776 ret = fread (&buffer[0], sizeof (char), len, (FILE*) context);
777 if (ret < 0)
778 PWARN ("Error reading XML file");
779 return ret;
780}
781
782gboolean
783sixtp_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 xmlParserCtxtPtr context = xmlCreateIOParserCtxt (NULL, NULL,
791 sixtp_parser_read, NULL /*no close */, fd,
792 XML_CHAR_ENCODING_NONE);
793 ret = sixtp_parse_file_common (sixtp, context, data_for_top_level,
794 global_data, parse_result);
795 return ret;
796}
797
798gboolean
799sixtp_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 xmlParserCtxtPtr context = xmlCreateMemoryParserCtxt (bufp, bufsz);
808 ret = sixtp_parse_file_common (sixtp, context, data_for_top_level,
809 global_data, parse_result);
810 return ret;
811}
812
813gboolean
814sixtp_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 if (!push_handler)
825 {
826 g_critical ("No push handler specified");
827 return FALSE;
828 }
829
830 if (! (ctxt = sixtp_context_new (sixtp, global_data, data_for_top_level)))
831 {
832 g_critical ("sixtp_context_new returned null");
833 return FALSE;
834 }
835
836 xml_context = xmlCreatePushParserCtxt (&ctxt->handler, &ctxt->data,
837 NULL, 0, NULL);
838 ctxt->data.saxParserCtxt = xml_context;
839 ctxt->data.bad_xml_parser = sixtp_dom_parser_new (gnc_bad_xml_end_handler,
840 NULL, NULL);
841
842 (*push_handler) (xml_context, push_user_data);
843
844 sixtp_context_run_end_handler (ctxt);
845
846 if (ctxt->data.parsing_ok)
847 {
848 if (parse_result)
849 *parse_result = ctxt->top_frame->frame_data;
850 sixtp_context_destroy (ctxt);
851 return TRUE;
852 }
853 else
854 {
855 if (parse_result)
856 *parse_result = NULL;
857 if (g_slist_length (ctxt->data.stack) > 1)
858 sixtp_handle_catastrophe (&ctxt->data);
859 sixtp_context_destroy (ctxt);
860 return FALSE;
861 }
862}
863
864/***********************************************************************/
865static gboolean
866eat_whitespace (char** cursor)
867{
868 while (**cursor && isspace (**cursor))
869 {
870 (*cursor)++;
871 }
872
873 if (**cursor == '\0')
874 {
875 return FALSE;
876 }
877 else
878 {
879 return TRUE;
880 }
881}
882
883static gboolean
884search_for (unsigned char marker, char** cursor)
885{
886 while (**cursor &&** cursor != marker)
887 {
888 (*cursor)++;
889 }
890
891 if (**cursor == '\0')
892 {
893 return FALSE;
894 }
895 else
896 {
897 (*cursor)++;
898 return TRUE;
899 }
900}
901
902QofBookFileType
903gnc_is_our_xml_file (const char* filename, gboolean* with_encoding)
904{
905 FILE* f = NULL;
906 char first_chunk[256];
907 ssize_t num_read;
908
909 g_return_val_if_fail (filename, GNC_BOOK_NOT_OURS);
910
911 f = g_fopen (filename, "r");
912 if (f == NULL)
913 {
914 return GNC_BOOK_NOT_OURS;
915 }
916
917 num_read = fread (first_chunk, sizeof (char), sizeof (first_chunk) - 1, f);
918 fclose (f);
919
920 if (num_read == 0)
921 {
922 return GNC_BOOK_NOT_OURS;
923 }
924
925 first_chunk[num_read] = '\0';
926
927 return gnc_is_our_first_xml_chunk (first_chunk, with_encoding);
928}
929
930QofBookFileType
931gnc_is_our_first_xml_chunk (char* chunk, gboolean* with_encoding)
932{
933 char* cursor = NULL;
934 size_t n;
935
936 if (with_encoding)
937 {
938 *with_encoding = FALSE;
939 }
940
941 cursor = chunk;
942
943 if (!eat_whitespace (&cursor))
944 {
945 return GNC_BOOK_NOT_OURS;
946 }
947
948 if (strncmp (cursor, "<?xml", 5) == 0)
949 {
950 if (!search_for ('>', &cursor))
951 {
952 return GNC_BOOK_NOT_OURS;
953 }
954
955 if (!eat_whitespace (&cursor))
956 {
957 return GNC_BOOK_NOT_OURS;
958 }
959
960 if (*cursor != '<')
961 {
962 return GNC_BOOK_NOT_OURS;
963 }
964
965 n = strlen (gnc_v2_xml_version_string);
966 if ((strncmp (cursor + 1, gnc_v2_xml_version_string, n) == 0)
967 && isspace (* (cursor + 1 + n)))
968 {
969 if (with_encoding)
970 {
971 *cursor = '\0';
972 cursor = chunk;
973 while (search_for ('e', &cursor))
974 {
975 if (strncmp (cursor, "ncoding=", 8) == 0)
976 {
977 *with_encoding = TRUE;
978 break;
979 }
980 }
981 }
982 return GNC_BOOK_XML2_FILE;
983 }
984
985 if (strncmp (cursor, "<gnc>", strlen ("<gnc>")) == 0)
986 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 if (strncmp (cursor, "<gnc-v", strlen ("<gnc-v")) == 0)
991 return GNC_BOOK_POST_XML2_0_0_FILE;
992
993 return GNC_BOOK_NOT_OURS;
994 }
995
996 return GNC_BOOK_NOT_OURS;
997}
998
999void
1000sixtp_run_callback (sixtp_gdv2* data, const char* type)
1001{
1002 if (data->countCallback)
1003 {
1004 data->countCallback (data, type);
1005 }
1006}
1007
1008/************************* END OF FILE *********************************/
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
Definition sixtp.h:130