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