GnuCash  5.6-150-g038405b370+
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"
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 is_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 
57 void
58 sixtp_child_free_data (sixtp_child_result* result)
59 {
60  if (result->data) g_free (result->data);
61 }
62 
63 void
64 sixtp_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 
74 void
75 sixtp_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 
85 void
86 sixtp_set_start (sixtp* parser, sixtp_start_handler start_handler)
87 {
88  parser->start_handler = start_handler;
89 }
90 
91 void
92 sixtp_set_before_child (sixtp* parser, sixtp_before_child_handler handler)
93 {
94  parser->before_child = handler;
95 }
96 
97 void
98 sixtp_set_after_child (sixtp* parser, sixtp_after_child_handler handler)
99 {
100  parser->after_child = handler;
101 }
102 
103 void
104 sixtp_set_end (sixtp* parser, sixtp_end_handler end_handler)
105 {
106  parser->end_handler = end_handler;
107 }
108 
109 void
110 sixtp_set_chars (sixtp* parser, sixtp_characters_handler char_handler)
111 {
112  parser->characters_handler = char_handler;
113 }
114 
115 void
116 sixtp_set_cleanup_result (sixtp* parser, sixtp_result_handler handler)
117 {
118  parser->cleanup_result = handler;
119 }
120 
121 void
122 sixtp_set_cleanup_chars (sixtp* parser, sixtp_result_handler handler)
123 {
124  parser->cleanup_chars = handler;
125 }
126 
127 void
128 sixtp_set_fail (sixtp* parser,
129  sixtp_fail_handler handler)
130 {
131  parser->fail_handler = handler;
132 }
133 
134 void
135 sixtp_set_result_fail (sixtp* parser, sixtp_result_handler handler)
136 {
137  parser->result_fail_handler = handler;
138 }
139 
140 void
141 sixtp_set_chars_fail (sixtp* parser, sixtp_result_handler handler)
142 {
143  parser->chars_fail_handler = handler;
144 }
145 
146 sixtp*
147 sixtp_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 
163 sixtp*
164 sixtp_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 
247 static void sixtp_destroy_child (gpointer key, gpointer value,
248  gpointer user_data);
249 
250 static void
251 sixtp_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 
260 static void
261 sixtp_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 
295 void
296 sixtp_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 
308 gboolean
309 sixtp_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  */
324 sixtp*
325 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  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 
385 void
386 sixtp_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 
472 void
473 sixtp_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 
507 void
508 sixtp_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 
610 xmlEntityPtr
611 sixtp_sax_get_entity_handler (void* user_data, const xmlChar* name)
612 {
613  return xmlGetPredefinedEntity (name);
614 }
615 
616 
617 void
618 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  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 
688 static gboolean
689 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  return TRUE;
695 }
696 
697 static gboolean
698 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  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 
741 gboolean
742 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  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 */
771 static int
772 sixtp_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 
782 gboolean
783 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  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 
798 gboolean
799 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  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 
813 gboolean
814 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  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 /***********************************************************************/
865 static gboolean
866 eat_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 
883 static gboolean
884 search_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 
902 QofBookFileType
903 gnc_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 
930 QofBookFileType
931 gnc_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 
999 void
1000 sixtp_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 *********************************/
Definition: sixtp.h:129
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250