27 #include <glib/gstdio.h>
32 #include <sys/types.h>
35 # define g_fopen fopen
39 #include "sixtp-parsers.h"
40 #include "sixtp-stack.h"
43 #define G_LOG_DOMAIN "gnc.backend.file.sixtp"
45 extern const gchar *gnc_v2_xml_version_string;
51 return((cr->type == SIXTP_CHILD_RESULT_NODE)
53 (g_strcmp0(cr->tag, tag) == 0));
59 if (result->data) g_free(result->data);
65 if (r->should_cleanup && r->cleanup_handler)
67 r->cleanup_handler(r);
69 if (r->type == SIXTP_CHILD_RESULT_NODE) g_free(r->tag);
76 fprintf(f,
"((tag %s) (data %p))",
77 cr->tag ? cr->tag :
"(null)",
85 sixtp_set_start(
sixtp *parser, sixtp_start_handler start_handler)
87 parser->start_handler = start_handler;
91 sixtp_set_before_child(
sixtp *parser, sixtp_before_child_handler handler)
93 parser->before_child = handler;
97 sixtp_set_after_child(
sixtp *parser, sixtp_after_child_handler handler)
99 parser->after_child = handler;
103 sixtp_set_end(
sixtp *parser, sixtp_end_handler end_handler)
105 parser->end_handler = end_handler;
109 sixtp_set_chars(
sixtp *parser, sixtp_characters_handler char_handler)
111 parser->characters_handler = char_handler;
115 sixtp_set_cleanup_result(
sixtp *parser, sixtp_result_handler handler)
117 parser->cleanup_result = handler;
121 sixtp_set_cleanup_chars(
sixtp *parser, sixtp_result_handler handler)
123 parser->cleanup_chars = handler;
127 sixtp_set_fail(
sixtp *parser,
128 sixtp_fail_handler handler)
130 parser->fail_handler = handler;
134 sixtp_set_result_fail(
sixtp *parser, sixtp_result_handler handler)
136 parser->result_fail_handler = handler;
140 sixtp_set_chars_fail(
sixtp *parser, sixtp_result_handler handler)
142 parser->chars_fail_handler = handler;
152 s->child_parsers = g_hash_table_new(g_str_hash, g_str_equal);
153 if (!s->child_parsers)
163 sixtp_set_any(
sixtp *tochange,
int cleanup, ...)
166 sixtp_handler_type type;
168 va_start(ap, cleanup);
172 g_warning(
"Null tochange passed");
178 type = va_arg(ap, sixtp_handler_type);
182 case SIXTP_NO_MORE_HANDLERS:
186 case SIXTP_START_HANDLER_ID:
187 sixtp_set_start(tochange, va_arg(ap, sixtp_start_handler));
190 case SIXTP_BEFORE_CHILD_HANDLER_ID:
191 sixtp_set_before_child(tochange,
192 va_arg(ap, sixtp_before_child_handler));
195 case SIXTP_AFTER_CHILD_HANDLER_ID:
196 sixtp_set_after_child(tochange,
197 va_arg(ap, sixtp_after_child_handler));
200 case SIXTP_END_HANDLER_ID:
201 sixtp_set_end(tochange, va_arg(ap, sixtp_end_handler));
204 case SIXTP_CHARACTERS_HANDLER_ID:
205 sixtp_set_chars(tochange, va_arg(ap, sixtp_characters_handler));
208 case SIXTP_FAIL_HANDLER_ID:
209 sixtp_set_fail(tochange, va_arg(ap, sixtp_fail_handler));
212 case SIXTP_CLEANUP_RESULT_ID:
213 sixtp_set_cleanup_result(tochange,
214 va_arg(ap, sixtp_result_handler));
217 case SIXTP_CLEANUP_CHARS_ID:
218 sixtp_set_cleanup_chars(tochange,
219 va_arg(ap, sixtp_result_handler));
222 case SIXTP_RESULT_FAIL_ID:
223 sixtp_set_result_fail(tochange, va_arg(ap, sixtp_result_handler));
226 case SIXTP_CHARS_FAIL_ID:
227 sixtp_set_chars_fail(tochange, va_arg(ap, sixtp_result_handler));
232 g_critical(
"Bogus sixtp type %d", type);
235 sixtp_destroy(tochange);
246 static void sixtp_destroy_child(gpointer key, gpointer value,
250 sixtp_destroy_node(
sixtp *sp, GHashTable *corpses)
252 g_return_if_fail(sp);
253 g_return_if_fail(corpses);
254 g_hash_table_foreach(sp->child_parsers, sixtp_destroy_child, corpses);
255 g_hash_table_destroy(sp->child_parsers);
260 sixtp_destroy_child(gpointer key, gpointer value, gpointer user_data)
262 GHashTable *corpses = (GHashTable *) user_data;
265 gpointer lookup_value;
267 g_debug(
"Killing sixtp child under key <%s>", key ? (
char *) key :
"(null)");
272 g_critical(
"no corpses in sixtp_destroy_child <%s>",
273 key ? (
char *) key :
"(null)");
278 g_critical(
"no child in sixtp_destroy_child <%s>",
279 key ? (
char *) key :
"");
283 if (!g_hash_table_lookup_extended(corpses, (gconstpointer) child,
284 &lookup_key, &lookup_value))
287 g_hash_table_insert(corpses, child, (gpointer) 1);
288 sixtp_destroy_node(child, corpses);
293 sixtp_destroy(
sixtp *sp)
296 g_return_if_fail(sp);
297 corpses = g_hash_table_new(g_direct_hash, g_direct_equal);
298 sixtp_destroy_node(sp, corpses);
299 g_hash_table_destroy(corpses);
306 sixtp_add_sub_parser(
sixtp *parser,
const gchar* tag,
sixtp *sub_parser)
308 g_return_val_if_fail(parser, FALSE);
309 g_return_val_if_fail(tag, FALSE);
310 g_return_val_if_fail(sub_parser, FALSE);
312 g_hash_table_insert(parser->child_parsers,
313 g_strdup(tag), (gpointer) sub_parser);
322 sixtp_add_some_sub_parsers(
sixtp *tochange,
int cleanup, ...)
329 va_start(ap, cleanup);
340 tag = va_arg(ap,
char*);
346 handler = va_arg(ap,
sixtp*);
349 g_warning(
"Handler for tag %s is null",
350 tag ? tag :
"(null)");
354 sixtp_destroy(tochange);
367 sixtp_destroy(handler);
371 sixtp_add_sub_parser(tochange, tag, handler);
383 sixtp_sax_start_handler(
void *user_data,
385 const xmlChar **attrs)
389 sixtp *current_parser = NULL;
390 sixtp *next_parser = NULL;
391 gchar *next_parser_tag = NULL;
392 gboolean lookup_success = FALSE;
396 current_parser = current_frame->parser;
400 lookup_success = g_hash_table_lookup_extended(current_parser->child_parsers,
402 (gpointer) & next_parser_tag,
403 (gpointer) & next_parser);
408 lookup_success = g_hash_table_lookup_extended(
409 current_parser->child_parsers, SIXTP_MAGIC_CATCHER,
410 (gpointer) & next_parser_tag, (gpointer) & next_parser);
413 g_critical(
"Tag <%s> not allowed in current context.",
414 name ? (
char *) name :
"(null)");
415 pdata->parsing_ok = FALSE;
416 next_parser = pdata->bad_xml_parser;
420 if (current_frame->parser->before_child)
422 GSList *parent_data_from_children = NULL;
423 gpointer parent_data_for_children = NULL;
425 if (g_slist_length(pdata->stack) > 1)
430 parent_data_from_children = parent_frame->data_from_children;
431 parent_data_from_children = parent_frame->data_for_children;
435 current_frame->parser->before_child(current_frame->data_for_children,
436 current_frame->data_from_children,
437 parent_data_from_children,
438 parent_data_for_children,
440 &(current_frame->frame_data),
446 new_frame = sixtp_stack_frame_new( next_parser, g_strdup((
char*) name));
448 new_frame->line = xmlSAX2GetLineNumber( pdata->saxParserCtxt );
449 new_frame->col = xmlSAX2GetColumnNumber( pdata->saxParserCtxt );
451 pdata->stack = g_slist_prepend(pdata->stack, (gpointer) new_frame);
453 if (next_parser->start_handler)
456 next_parser->start_handler(current_frame->data_from_children,
457 current_frame->data_for_children,
459 &new_frame->data_for_children,
460 &new_frame->frame_data,
467 sixtp_sax_characters_handler(
void *user_data,
const xmlChar *text,
int len)
473 if (frame->parser->characters_handler)
475 gpointer result = NULL;
478 frame->parser->characters_handler(frame->data_from_children,
479 frame->data_for_children,
484 if (pdata->parsing_ok && result)
489 child_data->type = SIXTP_CHILD_RESULT_CHARS;
490 child_data->tag = NULL;
491 child_data->data = result;
492 child_data->should_cleanup = TRUE;
493 child_data->cleanup_handler = frame->parser->cleanup_chars;
494 child_data->fail_handler = frame->parser->chars_fail_handler;
495 frame->data_from_children = g_slist_prepend(frame->data_from_children,
502 sixtp_sax_end_handler(
void *user_data,
const xmlChar *name)
508 gchar *end_tag = NULL;
515 if (g_strcmp0(current_frame->tag, (gchar*) name) != 0)
517 g_warning(
"bad closing tag (start <%s>, end <%s>)", current_frame->tag, name);
518 pdata->parsing_ok = FALSE;
521 if (g_strcmp0(parent_frame->tag, (gchar*) name) == 0)
523 pdata->stack = sixtp_pop_and_destroy_frame(pdata->stack);
526 g_warning(
"found matching start <%s> tag up one level", name);
531 if (current_frame->parser->end_handler)
534 current_frame->parser->end_handler(current_frame->data_for_children,
535 current_frame->data_from_children,
536 parent_frame->data_from_children,
537 parent_frame->data_for_children,
539 ¤t_frame->frame_data,
543 if (current_frame->frame_data)
548 child_result_data->type = SIXTP_CHILD_RESULT_NODE;
549 child_result_data->tag = g_strdup(current_frame->tag);
550 child_result_data->data = current_frame->frame_data;
551 child_result_data->should_cleanup = TRUE;
552 child_result_data->cleanup_handler = current_frame->parser->cleanup_result;
553 child_result_data->fail_handler =
554 current_frame->parser->result_fail_handler;
555 parent_frame->data_from_children =
556 g_slist_prepend(parent_frame->data_from_children, child_result_data);
560 end_tag = current_frame->tag;
562 g_debug(
"Finished with end of <%s>", end_tag ? end_tag :
"(null)");
566 pdata->stack = sixtp_pop_and_destroy_frame(pdata->stack);
572 ((g_slist_length(pdata->stack) > 1) ? (pdata->stack->next->data) : NULL);
574 if (current_frame->parser->after_child)
577 GSList *parent_data_from_children = NULL;
578 gpointer parent_data_for_children = NULL;
585 parent_data_from_children = parent_frame->data_from_children;
586 parent_data_from_children = parent_frame->data_for_children;
590 current_frame->parser->after_child(current_frame->data_for_children,
591 current_frame->data_from_children,
592 parent_data_from_children,
593 parent_data_for_children,
595 &(current_frame->frame_data),
605 sixtp_sax_get_entity_handler(
void *user_data,
const xmlChar *name)
607 return xmlGetPredefinedEntity(name);
623 GSList **stack = &(sax_data->stack);
625 g_critical(
"parse failed at:");
626 sixtp_print_frame_stack(sax_data->stack, stderr);
633 if (current_frame->parser->fail_handler)
635 GSList *sibling_data;
636 gpointer parent_data;
638 if ((*stack)->next == NULL)
648 parent_data = parent_frame->data_for_children;
649 sibling_data = parent_frame->data_from_children;
652 current_frame->parser->fail_handler(current_frame->data_for_children,
653 current_frame->data_from_children,
656 sax_data->global_data,
657 ¤t_frame->frame_data,
662 for (lp = current_frame->data_from_children; lp; lp = lp->next)
665 if (cresult->fail_handler)
667 cresult->fail_handler(cresult);
671 if ((*stack)->next == NULL)
678 *stack = sixtp_pop_and_destroy_frame(*stack);
683 gnc_bad_xml_end_handler(gpointer data_for_children,
684 GSList* data_from_children, GSList* sibling_data,
685 gpointer parent_data, gpointer global_data,
686 gpointer *result,
const gchar *tag)
693 xmlParserCtxtPtr xml_context,
694 gpointer data_for_top_level,
695 gpointer global_data,
696 gpointer *parse_result)
701 if (!(ctxt = sixtp_context_new(sixtp, global_data, data_for_top_level)))
703 g_critical(
"sixtp_context_new returned null");
707 ctxt->data.saxParserCtxt = xml_context;
708 ctxt->data.saxParserCtxt->sax = &ctxt->handler;
709 ctxt->data.saxParserCtxt->userData = &ctxt->data;
710 ctxt->data.bad_xml_parser = sixtp_dom_parser_new(gnc_bad_xml_end_handler, NULL, NULL);
711 parse_ret = xmlParseDocument( ctxt->data.saxParserCtxt );
714 sixtp_context_run_end_handler(ctxt);
716 if (parse_ret == 0 && ctxt->data.parsing_ok)
719 *parse_result = ctxt->top_frame->frame_data;
720 sixtp_context_destroy(ctxt);
726 *parse_result = NULL;
727 if (g_slist_length(ctxt->data.stack) > 1)
728 sixtp_handle_catastrophe(&ctxt->data);
729 sixtp_context_destroy(ctxt);
735 sixtp_parse_file(sixtp *sixtp,
736 const char *filename,
737 gpointer data_for_top_level,
738 gpointer global_data,
739 gpointer *parse_result)
742 xmlParserCtxtPtr context;
746 gchar *conv_name = g_win32_locale_filename_from_utf8(filename);
749 g_warning(
"Could not convert '%s' to system codepage", filename);
752 context = xmlCreateFileParserCtxt(conv_name);
756 context = xmlCreateFileParserCtxt(filename);
758 ret = sixtp_parse_file_common(sixtp, context, data_for_top_level,
759 global_data, parse_result);
765 sixtp_parser_read(
void *context,
char *buffer,
int len)
769 ret = fread(&buffer[0],
sizeof(
char), len, (FILE *) context);
771 g_warning(
"Error reading XML file");
776 sixtp_parse_fd(sixtp *sixtp,
778 gpointer data_for_top_level,
779 gpointer global_data,
780 gpointer *parse_result)
783 xmlParserCtxtPtr context = xmlCreateIOParserCtxt( NULL, NULL,
784 sixtp_parser_read, NULL , fd,
785 XML_CHAR_ENCODING_NONE);
786 ret = sixtp_parse_file_common(sixtp, context, data_for_top_level,
787 global_data, parse_result);
792 sixtp_parse_buffer(sixtp *sixtp,
795 gpointer data_for_top_level,
796 gpointer global_data,
797 gpointer *parse_result)
800 xmlParserCtxtPtr context = xmlCreateMemoryParserCtxt( bufp, bufsz );
801 ret = sixtp_parse_file_common(sixtp, context, data_for_top_level,
802 global_data, parse_result);
807 sixtp_parse_push (sixtp *sixtp,
808 sixtp_push_handler push_handler,
809 gpointer push_user_data,
810 gpointer data_for_top_level,
811 gpointer global_data,
812 gpointer *parse_result)
815 xmlParserCtxtPtr xml_context;
819 g_critical(
"No push handler specified");
823 if (!(ctxt = sixtp_context_new(sixtp, global_data, data_for_top_level)))
825 g_critical(
"sixtp_context_new returned null");
829 xml_context = xmlCreatePushParserCtxt(&ctxt->handler, &ctxt->data,
831 ctxt->data.saxParserCtxt = xml_context;
832 ctxt->data.bad_xml_parser = sixtp_dom_parser_new(gnc_bad_xml_end_handler,
835 (*push_handler)(xml_context, push_user_data);
837 sixtp_context_run_end_handler(ctxt);
839 if (ctxt->data.parsing_ok)
842 *parse_result = ctxt->top_frame->frame_data;
843 sixtp_context_destroy(ctxt);
849 *parse_result = NULL;
850 if (g_slist_length(ctxt->data.stack) > 1)
851 sixtp_handle_catastrophe(&ctxt->data);
852 sixtp_context_destroy(ctxt);
859 eat_whitespace(
char **cursor)
861 while (**cursor && isspace(**cursor))
866 if (**cursor ==
'\0')
877 search_for(
unsigned char marker,
char **cursor)
879 while (**cursor && **cursor != marker)
884 if (**cursor ==
'\0')
896 gnc_is_our_xml_file(
const char *filename, gboolean *with_encoding)
899 char first_chunk[256];
902 g_return_val_if_fail(filename, GNC_BOOK_NOT_OURS);
904 f = g_fopen(filename,
"r");
907 return GNC_BOOK_NOT_OURS;
910 num_read = fread(first_chunk,
sizeof(
char),
sizeof(first_chunk) - 1, f);
915 return GNC_BOOK_NOT_OURS;
918 first_chunk[num_read] =
'\0';
920 return gnc_is_our_first_xml_chunk(first_chunk, with_encoding);
924 gnc_is_our_first_xml_chunk(
char *chunk, gboolean *with_encoding)
931 *with_encoding = FALSE;
936 if (!eat_whitespace(&cursor))
938 return GNC_BOOK_NOT_OURS;
941 if (strncmp(cursor,
"<?xml", 5) == 0)
943 if (!search_for(
'>', &cursor))
945 return GNC_BOOK_NOT_OURS;
948 if (!eat_whitespace(&cursor))
950 return GNC_BOOK_NOT_OURS;
955 return GNC_BOOK_NOT_OURS;
958 n = strlen(gnc_v2_xml_version_string);
959 if ((strncmp(cursor + 1, gnc_v2_xml_version_string, n) == 0)
960 && isspace(*(cursor + 1 + n)))
966 while (search_for(
'e', &cursor))
968 if (strncmp(cursor,
"ncoding=", 8) == 0)
970 *with_encoding = TRUE;
975 return GNC_BOOK_XML2_FILE;
978 if (strncmp(cursor,
"<gnc>", strlen(
"<gnc>")) == 0)
979 return GNC_BOOK_XML1_FILE;
983 if (strncmp(cursor,
"<gnc-v", strlen(
"<gnc-v")) == 0)
984 return GNC_BOOK_POST_XML2_0_0_FILE;
986 return GNC_BOOK_NOT_OURS;
989 return GNC_BOOK_NOT_OURS;