GnuCash  5.6-150-g038405b370+
io-gncxml-v1.cpp
1 /********************************************************************
2  * io-gncxml-r.c -- read XML-format gnucash data file *
3  * Copyright (C) 2000 Gnumatic, Inc. *
4  * *
5  * Initial code by Rob L. Browning 4Q 2000 *
6  * Tuneups by James LewisMoss Dec 2000 *
7  * Excessive hacking Linas Vepstas January 2001 *
8  * *
9  * This program is free software; you can redistribute it and/or *
10  * modify it under the terms of the GNU General Public License as *
11  * published by the Free Software Foundation; either version 2 of *
12  * the License, or (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License*
20  * along with this program; if not, contact: *
21  * *
22  * Free Software Foundation Voice: +1-617-542-5942 *
23  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
24  * Boston, MA 02110-1301, USA gnu@gnu.org *
25  * *
26  *******************************************************************/
27 #include <guid.hpp>
28 #include <config.h>
29 
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include <glib.h>
34 
35 #include <gnc-xml-helper.h>
36 #include <Account.h>
37 #include <AccountP.hpp>
38 #include <Query.h>
39 #include <Scrub.h>
40 #include <Transaction.h>
41 #include <TransactionP.hpp>
42 #include <TransLog.h>
43 #include <gnc-pricedb.h>
44 #include <gnc-pricedb-p.h>
45 
46 #include "io-gncxml.h"
47 #include "sixtp.h"
48 #include "sixtp-dom-parsers.h"
49 #include "sixtp-stack.h"
50 #include "sixtp-parsers.h"
51 #include "sixtp-utils.h"
52 
53 #include <kvp-frame.hpp>
54 
55 /* from Transaction-xml-parser-v1.c */
56 static sixtp* gnc_transaction_parser_new (void);
57 
58 /* from Account-xml-parser-v1.c */
59 static sixtp* gnc_account_parser_new (void);
60 
61 /* from Ledger-xml-parser-v1.c */
62 static sixtp* ledger_data_parser_new (void);
63 
64 /* from Commodity-xml-parser-v1.c */
65 static sixtp* commodity_restore_parser_new (void);
66 
67 /* from Commodity-xml-parser-v1.c */
68 static sixtp* generic_gnc_commodity_lookup_parser_new (void);
69 
70 /* from Query-xml-parser-v1.c */
71 //static sixtp* query_server_parser_new (void);
72 
73 /* from sixtp-kvp-parser.c */
74 static sixtp* kvp_frame_parser_new (void);
75 
76 /* from gnc-pricedb-xml-v1.c */
77 static sixtp* gnc_pricedb_parser_new (void);
78 
79 
80 typedef enum
81 {
82  GNC_PARSE_ERR_NONE,
83  GNC_PARSE_ERR_BAD_VERSION,
84 } GNCParseErr;
85 
86 typedef struct
87 {
88  /* have we gotten the file version yet? */
89  gboolean seen_version;
90  gint64 version;
91 
92  /* top level <gnc-data> parser - we need this so we can set it up
93  after we see the file version. */
94  sixtp* gnc_parser;
95 
96  /* The book */
97  QofBook* book;
98 
99  /* The root account */
100  Account* root_account;
101 
102  /* The pricedb */
103  GNCPriceDB* pricedb;
104 
105  /* The query */
106  // Query *query;
107 
108  GNCParseErr error;
110 
111 
112 /* result for a characters handler is shared across all character
113  handlers for a given node. result for start/end pair is shared as
114  well.
115 
116  Cleaning up the results returned by children and characters
117  handlers is the user's responsibility.
118 
119  results have to have cleanup pointers for exceptional failures
120 
121  stack frames also have to have cleanup funcs for exceptional
122  failures after the start tag handler has been called, but before
123  the end tag handler has been called. If the end tag handler is
124  called, but returns FALSE, it is expected that the end tag handler
125  has taken care of any cleanup itself.
126 
127  result cleanup functions are called for a node's children just
128  after the end handler unless should_cleanup has been set to FALSE,
129  or unless there's a failure. If there's a failure, then the
130  cleanup is left to the failure handler.
131 
132 
133 */
134 
135 static QofLogModule log_module = GNC_MOD_IO;
136 
137 /* ================================================================= */
138 /* <version> (lineage <gnc>)
139 
140  Fancy and strange - look for an integer version number. If we get
141  one, then modify the parent parser to handle the input.
142 
143  this is a simple_chars_only_parser with an end handler that grabs
144  the version number and tweaks the parser, if possible.
145 
146  */
147 
148 static gboolean
149 gnc_parser_configure_for_input_version (GNCParseStatus* status, gint64 version)
150 {
151 
152  status->version = version;
153  status->seen_version = TRUE;
154 
155  /* Check for a legal version here. */
156  if (version != 1)
157  {
158  status->error = GNC_PARSE_ERR_BAD_VERSION;
159  return (FALSE);
160  }
161 
162  /* Now set up the parser based on the version. */
163 
164  /* add <ledger-data> */
165  {
166  sixtp* ledger_data_pr = ledger_data_parser_new ();
167  g_return_val_if_fail (ledger_data_pr, FALSE);
168  sixtp_add_sub_parser (status->gnc_parser, "ledger-data", ledger_data_pr);
169  }
170 
171  return (TRUE);
172 }
173 
174 static gboolean
175 gnc_version_end_handler (gpointer data_for_children,
176  GSList* data_from_children, GSList* sibling_data,
177  gpointer parent_data, gpointer global_data,
178  gpointer* result, const gchar* tag)
179 {
180  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
181  gint64 version;
182  gboolean ok;
183  gchar* txt;
184 
185  g_return_val_if_fail (pstatus, FALSE);
186  if (pstatus->seen_version) return (FALSE);
187 
188  txt = concatenate_child_result_chars (data_from_children);
189  g_return_val_if_fail (txt, FALSE);
190 
191  ok = string_to_gint64 (txt, &version);
192  g_free (txt);
193  g_return_val_if_fail (ok, FALSE);
194 
195  if (!gnc_parser_configure_for_input_version (pstatus, version)) return (FALSE);
196 
197  return (TRUE);
198 }
199 
200 static sixtp*
201 gnc_version_parser_new (void)
202 {
203  return (simple_chars_only_parser_new (gnc_version_end_handler));
204 }
205 
206 /****************************************************************************/
207 /* <gnc> (lineage #f)
208 
209  Takes the results from various children and puts them in the
210  global_data result structure.
211 
212  from parent: NA
213  for children: NA
214  result: NA
215  -----------
216  start: NA
217  before-child: make sure we don't get two ledger-data's (not allowed ATM).
218  after-child: if a ledger-data child, parse_data->root_account = *result.
219  characters: allow_and_ignore_only_whitespace
220 
221  Similarly, only one query is allowed ...
222  end: NA
223 
224  cleanup-result: NA
225  cleanup-chars: NA
226  fail: NA
227  result-fail: NA
228  chars-fail: NA
229 
230  */
231 
232 static gboolean
233 gnc_parser_before_child_handler (gpointer data_for_children,
234  GSList* data_from_children,
235  GSList* sibling_data,
236  gpointer parent_data,
237  gpointer global_data,
238  gpointer* result,
239 
240  const gchar* tag,
241  const gchar* child_tag)
242 {
243  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
244 
245  g_return_val_if_fail (pstatus, FALSE);
246 
247  if (strcmp (child_tag, "ledger-data") == 0)
248  {
249  if (pstatus->root_account)
250  {
251  return (FALSE);
252  }
253  }
254 
255  return (TRUE);
256 }
257 
258 static gboolean
259 gnc_parser_after_child_handler (gpointer data_for_children,
260  GSList* data_from_children,
261  GSList* sibling_data,
262  gpointer parent_data,
263  gpointer global_data,
264  gpointer* result,
265  const gchar* tag,
266  const gchar* child_tag,
267  sixtp_child_result* child_result)
268 {
269  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
270  g_return_val_if_fail (pstatus, FALSE);
271 
272  if (strcmp (child_tag, "ledger-data") == 0)
273  {
274  g_return_val_if_fail (child_result, FALSE);
275  g_return_val_if_fail (child_result->data, FALSE);
276  pstatus->root_account = (Account*) child_result->data;
277  child_result->should_cleanup = FALSE;
278  }
279 
280  return (TRUE);
281 }
282 
283 static sixtp*
284 gnc_parser_new (void)
285 {
286  return sixtp_set_any (
287  sixtp_new (), FALSE,
288  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
289  SIXTP_BEFORE_CHILD_HANDLER_ID, gnc_parser_before_child_handler,
290  SIXTP_AFTER_CHILD_HANDLER_ID, gnc_parser_after_child_handler,
291  SIXTP_NO_MORE_HANDLERS);
292 }
293 
294 /* ================================================================== */
295 
296 static sixtp*
297 gncxml_setup_for_read (GNCParseStatus* global_parse_status)
298 {
299 
300  sixtp* top_level_pr;
301  sixtp* gnc_pr;
302  sixtp* gnc_version_pr;
303 
304  /* top-level: This is just a dummy node. It doesn't do anything.
305  For now, the result is communicated through the global_data
306  parser. */
307  top_level_pr = sixtp_new ();
308  g_return_val_if_fail (top_level_pr, FALSE);
309  sixtp_set_chars (top_level_pr, allow_and_ignore_only_whitespace);
310 
311  /* <gnc> */
312  gnc_pr = gnc_parser_new ();
313  if (!gnc_pr)
314  {
315  sixtp_destroy (top_level_pr);
316  return (NULL);
317  }
318  sixtp_add_sub_parser (top_level_pr, "gnc", gnc_pr);
319 
320  /* <version> */
321  gnc_version_pr = gnc_version_parser_new ();
322  if (!gnc_version_pr)
323  {
324  sixtp_destroy (top_level_pr);
325  return (NULL);
326  }
327  sixtp_add_sub_parser (gnc_pr, "version", gnc_version_pr);
328 
329  global_parse_status->seen_version = FALSE;
330  global_parse_status->gnc_parser = gnc_pr;
331  global_parse_status->root_account = NULL;
332  global_parse_status->pricedb = NULL;
333  // global_parse_status->query = NULL;
334  global_parse_status->error = GNC_PARSE_ERR_NONE;
335 
336  return top_level_pr;
337 }
338 
339 /* ================================================================== */
340 
341 gboolean
342 qof_session_load_from_xml_file (QofBook* book, const char* filename)
343 {
344  gboolean parse_ok;
345  gpointer parse_result = NULL;
346  sixtp* top_level_pr;
347  GNCParseStatus global_parse_status;
348  Account* root;
349 
350  global_parse_status.book = book;
351  g_return_val_if_fail (book, FALSE);
352  g_return_val_if_fail (filename, FALSE);
353 
354  xaccDisableDataScrubbing ();
355  top_level_pr = gncxml_setup_for_read (&global_parse_status);
356  g_return_val_if_fail (top_level_pr, FALSE);
357 
358  parse_ok = sixtp_parse_file (top_level_pr,
359  filename,
360  NULL,
361  &global_parse_status,
362  &parse_result);
363 
364  sixtp_destroy (top_level_pr);
365  xaccEnableDataScrubbing ();
366 
367  if (parse_ok)
368  {
369  if (!global_parse_status.root_account) return FALSE;
370 
371  root = global_parse_status.root_account;
372  gnc_book_set_root_account (book, root);
373 
374  /* Fix account and transaction commodities */
376 
377  /* Fix split amount/value */
378  xaccAccountTreeScrubSplits (root);
379 
380  return (TRUE);
381  }
382  else
383  {
384  return (FALSE);
385  }
386 }
387 
388 /* ================================================================== */
389 
390 gboolean
391 gnc_is_xml_data_file (const gchar* filename)
392 {
393  if ((gnc_is_our_xml_file (filename, NULL)) == GNC_BOOK_XML1_FILE)
394  return TRUE;
395  return FALSE;
396 }
397 
398 /* ================================================================== */
399 #include "qof.h"
400 
401 /****************************************************************************/
402 /* <kvp-frame>
403 
404  A collection of node functions intended to parse a sub-node set
405  that looks like this:
406 
407  <kvp-frame>
408  <s>
409  <k>notes</k>
410  <string>foo</string>
411  </s>
412  <s>
413  <k>temp</k>
414  <gint64>97</gint64>
415  </s>
416  </kvp-frame>
417 
418  and return a KvpFrame*. The start handler for the top allocates
419  the KvpFrame* and passes it to the children. The <s> blocks add
420  slots according to their <k> (key) and value blocks.
421 
422  FIXME: right now this is totally inappropriate for cases where we
423  want to read in a set of new values that should "merge" with the
424  existing values. This is only appropriate for wholesale
425  replacement of the slots.
426 
427 */
428 
429 /* kvp-frame [value] handlers
430 
431  Handle the possible values. Each value handler is expected to
432  parse it's subtree and return an appropriate kvp_value* in its
433  result. The <kvp-frame> <slot> handler will then cram it where it
434  belongs. */
435 
436 
437 static void
438 kvp_value_result_cleanup (sixtp_child_result* cr)
439 {
440  KvpValue* v = static_cast<KvpValue*> (cr->data);
441  if (v) delete v;
442 }
443 
444 static sixtp*
445 simple_kvp_value_parser_new (sixtp_end_handler end_handler)
446 {
447  return sixtp_set_any (sixtp_new (), FALSE,
448  SIXTP_CHARACTERS_HANDLER_ID,
449  generic_accumulate_chars,
450  SIXTP_END_HANDLER_ID, end_handler,
451  SIXTP_CLEANUP_RESULT_ID, kvp_value_result_cleanup,
452  SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
453  SIXTP_RESULT_FAIL_ID, kvp_value_result_cleanup,
454  SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
455  SIXTP_NO_MORE_HANDLERS);
456 }
457 
458 /* <gint64> - gint64 kvp_value parser.
459 
460  input: NA
461  returns: gint64 kvp_value
462 
463  start: NA
464  chars: generic_accumulate_chars.
465  end: convert chars to gint64 kvp_value* if possible and return.
466 
467  cleanup-result: kvp_value_delete.
468  cleanup-chars: g_free (for chars)
469  fail: NA
470  result-fail: kvp_value_delete
471  chars-fail: g_free (for chars)
472 
473  */
474 
475 static gboolean
476 string_to_gnc_numeric(const gchar* str, gnc_numeric *n)
477 {
478  *n = gnc_numeric_from_string (str);
479  return (!gnc_numeric_check (*n));
480 }
481 
482 /* ------------------------------------------------------------ */
483 /* generic type copnversion for kvp types */
484 #define KVP_CVT_VALUE(TYPE) \
485 { \
486  gchar *txt = NULL; \
487  TYPE val; \
488  KvpValue *kvpv; \
489  gboolean ok; \
490  \
491  txt = concatenate_child_result_chars(data_from_children); \
492  g_return_val_if_fail(txt, FALSE); \
493  \
494  ok = (gboolean) string_to_##TYPE(txt, &val); \
495  g_free(txt); \
496  g_return_val_if_fail(ok, FALSE); \
497  \
498  kvpv = new KvpValue{val}; \
499  g_return_val_if_fail(kvpv, FALSE); \
500  \
501  *result = kvpv; \
502  return(TRUE); \
503 }
504 /* ------------------------------------------------------------ */
505 
506 static gboolean
507 gint64_kvp_value_end_handler (gpointer data_for_children,
508  GSList* data_from_children,
509  GSList* sibling_data,
510  gpointer parent_data,
511  gpointer global_data,
512  gpointer* result,
513  const gchar* tag)
514 {
515  KVP_CVT_VALUE (gint64);
516 }
517 
518 
519 static sixtp*
520 gint64_kvp_value_parser_new (void)
521 {
522  return (simple_kvp_value_parser_new (gint64_kvp_value_end_handler));
523 }
524 
525 static gboolean
526 double_kvp_value_end_handler (gpointer data_for_children,
527  GSList* data_from_children,
528  GSList* sibling_data,
529  gpointer parent_data,
530  gpointer global_data,
531  gpointer* result,
532  const gchar* tag)
533 {
534  KVP_CVT_VALUE (double);
535 }
536 
537 static sixtp*
538 double_kvp_value_parser_new (void)
539 {
540  return (simple_kvp_value_parser_new (double_kvp_value_end_handler));
541 }
542 
543 static gboolean
544 gnc_numeric_kvp_value_end_handler (gpointer data_for_children,
545  GSList* data_from_children,
546  GSList* sibling_data,
547  gpointer parent_data,
548  gpointer global_data,
549  gpointer* result,
550  const gchar* tag)
551 {
552  KVP_CVT_VALUE (gnc_numeric);
553 }
554 
555 static sixtp*
556 gnc_numeric_kvp_value_parser_new (void)
557 {
558  return (simple_kvp_value_parser_new (gnc_numeric_kvp_value_end_handler));
559 }
560 
561 static gboolean
562 string_kvp_value_end_handler (gpointer data_for_children,
563  GSList* data_from_children,
564  GSList* sibling_data,
565  gpointer parent_data,
566  gpointer global_data,
567  gpointer* result,
568  const gchar* tag)
569 {
570  gchar* txt = NULL;
571  KvpValue* kvpv;
572 
573  txt = concatenate_child_result_chars (data_from_children);
574  g_return_val_if_fail (txt, FALSE);
575 
576  kvpv = new KvpValue {g_strdup (txt)};
577  g_free (txt);
578  g_return_val_if_fail (kvpv, FALSE);
579 
580  *result = kvpv;
581  return (TRUE);
582 }
583 
584 static sixtp*
585 string_kvp_value_parser_new (void)
586 {
587  return (simple_kvp_value_parser_new (string_kvp_value_end_handler));
588 }
589 
590 /* the guid handler is almost the same as above, but has
591  * inconsistent type handling */
592 static gboolean
593 guid_kvp_value_end_handler (gpointer data_for_children,
594  GSList* data_from_children,
595  GSList* sibling_data,
596  gpointer parent_data,
597  gpointer global_data,
598  gpointer* result,
599  const gchar* tag)
600 {
601  gchar* txt = NULL;
602  GncGUID val;
603  KvpValue* kvpv;
604  gboolean ok;
605 
606  txt = concatenate_child_result_chars (data_from_children);
607  g_return_val_if_fail (txt, FALSE);
608 
609  ok = string_to_guid (txt, &val);
610  g_free (txt);
611 
612  g_return_val_if_fail (ok, FALSE);
613 
614  kvpv = new KvpValue {guid_copy (&val)};
615  g_return_val_if_fail (kvpv, FALSE);
616 
617  *result = kvpv;
618  return (TRUE);
619 }
620 
621 static sixtp*
622 guid_kvp_value_parser_new (void)
623 {
624  return (simple_kvp_value_parser_new (guid_kvp_value_end_handler));
625 }
626 
627 /*********************************/
628 /* glist kvp-value handler
629  */
630 
631 /* <glist> (lineage <s> <kvp-frame>)
632  input: NA
633  returns: glist kvp_value
634 
635  start: NA
636  chars: allow_and_ignore_only_whitespace
637  end: convert the child list pointer to a glist kvp_value and return.
638 
639  cleanup-result: kvp_value_delete
640  cleanup-chars: NA
641  fail: NA
642  result-fail: kvp_value_delete
643  chars-fail: NA
644 
645  */
646 
647 
648 static gboolean
649 glist_kvp_value_end_handler (gpointer data_for_children,
650  GSList* data_from_children, GSList* sibling_data,
651  gpointer parent_data, gpointer global_data,
652  gpointer* result, const gchar* tag)
653 {
654  GSList* lp;
655  GList* result_glist;
656  KvpValue* kvp_result;
657 
658  result_glist = NULL;
659  for (lp = data_from_children; lp; lp = lp->next)
660  {
661  sixtp_child_result* cr = (sixtp_child_result*) lp->data;
662  KvpValue* kvp = (KvpValue*) cr->data;
663 
664  /* children are in reverse chron order, so this fixes it. */
665  result_glist = g_list_prepend (result_glist, kvp);
666  cr->should_cleanup = FALSE;
667  }
668 
669  kvp_result = new KvpValue {result_glist};
670  if (!kvp_result)
671  g_list_free_full (result_glist,
672  [] (void * data) { delete static_cast<KvpValue*> (data);});
673  *result = kvp_result;
674  return (TRUE);
675 }
676 
677 /* ---------------------------------------------- */
678 #define KVP_TOKEN(NAME,TOK) \
679  child_pr = NAME##_kvp_value_parser_new(); \
680  g_return_val_if_fail(child_pr, FALSE); \
681  sixtp_add_sub_parser(p, TOK, child_pr);
682 /* ---------------------------------------------- */
683 
684 
685 static gboolean
686 add_all_kvp_value_parsers_as_sub_nodes (sixtp* p,
687  sixtp* kvp_frame_parser,
688  sixtp* glist_parser)
689 {
690  sixtp* child_pr;
691 
692  g_return_val_if_fail (p, FALSE);
693  g_return_val_if_fail (kvp_frame_parser, FALSE);
694 
695  KVP_TOKEN (gint64, "gint64");
696  KVP_TOKEN (double, "double");
697  KVP_TOKEN (gnc_numeric, "numeric");
698  KVP_TOKEN (string, "string");
699  KVP_TOKEN (guid, "guid");
700 
701  sixtp_add_sub_parser (p, "glist", glist_parser);
702  sixtp_add_sub_parser (p, "frame", kvp_frame_parser);
703 
704  return (TRUE);
705 }
706 
707 static sixtp*
708 glist_kvp_value_parser_new (sixtp* kvp_frame_parser)
709 {
710  sixtp* top_level = sixtp_set_any (
711  sixtp_new (), FALSE,
712  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
713  SIXTP_END_HANDLER_ID, glist_kvp_value_end_handler,
714  SIXTP_CLEANUP_RESULT_ID, kvp_value_result_cleanup,
715  SIXTP_RESULT_FAIL_ID, kvp_value_result_cleanup,
716  SIXTP_NO_MORE_HANDLERS);
717  if (!top_level)
718  {
719  return NULL;
720  }
721 
722  if (!add_all_kvp_value_parsers_as_sub_nodes (top_level,
723  kvp_frame_parser,
724  top_level))
725  {
726  sixtp_destroy (top_level);
727  return (NULL);
728  }
729 
730  return (top_level);
731 }
732 
733 /*********************************/
734 /* kvp-frame slot handlers
735 
736  handlers for the <s><k>some key</k><[value]>data</[value]> sub-structure.
737 */
738 
739 /* <k> (lineage <s> <kvp-frame>)
740 
741  kvp-frame slot key handler - just a generic-string-parser
742 
743  */
744 
745 /* <s> (lineage <kvp-frame>)
746 
747  kvp-frame slot handler.
748 
749  input: KvpFrame*
750  returns: NA
751 
752  start: NA
753  characters: allow_and_ignore_only_whitespace
754  end: check for two children - one must be a <k> - if OK, set slot.
755 
756  cleanup-result: NA
757  cleanup-chars: NA
758  fail: NA
759  result-fail: NA
760  chars-fail: NA
761 
762  */
763 
764 static gboolean
765 kvp_frame_slot_end_handler (gpointer data_for_children,
766  GSList* data_from_children, GSList* sibling_data,
767  gpointer parent_data, gpointer global_data,
768  gpointer* result, const gchar* tag)
769 {
770  KvpFrame* f = (KvpFrame*) parent_data;
771  gchar* key = NULL;
772  KvpValue* value = NULL;
773  gboolean delete_value = FALSE;
774  sixtp_child_result *cr1 = NULL, *cr2 = NULL, *cr = NULL;
775  g_return_val_if_fail (f, FALSE);
776 
777  if (g_slist_length (data_from_children) != 2) return (FALSE);
778  cr1 = (sixtp_child_result*)data_from_children->data;
779  cr2 = (sixtp_child_result*)data_from_children->next->data;
780 
781  if (is_child_result_from_node_named(cr1, "k"))
782  {
783  key = (char*)cr1->data;
784  cr = cr2;
785  }
786  else if (is_child_result_from_node_named(cr2, "k"))
787  {
788  key = (char*)cr2->data;
789  cr = cr1;
790  }
791  else
792  return FALSE;
793 
794  if (is_child_result_from_node_named (cr, "frame"))
795  {
796  KvpFrame* frame = static_cast<KvpFrame*> (cr->data);
797  value = new KvpValue {frame};
798  delete_value = TRUE;
799  }
800  else
801  {
802  value = static_cast<KvpValue*> (cr->data);
803  delete_value = FALSE;
804  }
805 
806  f->set ({key}, value);
807  if (delete_value)
808  delete value;
809  return (TRUE);
810 }
811 
812 static sixtp*
813 kvp_frame_slot_parser_new (sixtp* kvp_frame_parser)
814 {
815  sixtp* top_level;
816  sixtp* child_pr;
817  sixtp* glist_pr;
818 
819  g_return_val_if_fail (kvp_frame_parser, NULL);
820 
821  if (! (top_level = sixtp_set_any (
822  sixtp_new (), FALSE,
823  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
824  SIXTP_END_HANDLER_ID, kvp_frame_slot_end_handler,
825  SIXTP_NO_MORE_HANDLERS)))
826  {
827  return NULL;
828  }
829 
830  child_pr = simple_chars_only_parser_new (NULL);
831  if (!child_pr)
832  {
833  sixtp_destroy (top_level);
834  return (NULL);
835  }
836  sixtp_add_sub_parser (top_level, "k", child_pr);
837 
838  glist_pr = glist_kvp_value_parser_new (kvp_frame_parser);
839  if (!glist_pr)
840  {
841  sixtp_destroy (top_level);
842  return (NULL);
843  }
844 
845  if (!add_all_kvp_value_parsers_as_sub_nodes (top_level,
846  kvp_frame_parser,
847  glist_pr))
848  {
849  sixtp_destroy (top_level);
850  return (NULL);
851  }
852 
853  return (top_level);
854 }
855 
856 
857 /* <kvp-frame> - can be used anywhere.
858 
859  input: NA
860  returns: KvpFrame*
861 
862  start: Allocates KvpFrame* and places in data_for_children.
863  characters: none (whitespace only).
864  end: put KvpFrame* into result if everything's OK.
865 
866  cleanup-result: delete KvpFrame*
867  cleanup-chars: NA
868  fail: delete KvpFrame*
869  result-fail: delete KvpFrame*
870  chars-fail: NA
871 
872  */
873 
874 static gboolean
875 kvp_frame_start_handler (GSList* sibling_data, gpointer parent_data,
876  gpointer global_data, gpointer* data_for_children,
877  gpointer* result, const gchar* tag, gchar** attrs)
878 {
879  auto f = new KvpFrame;
880  *data_for_children = f;
881  return (TRUE);
882 }
883 
884 static gboolean
885 kvp_frame_end_handler (gpointer data_for_children,
886  GSList* data_from_children, GSList* sibling_data,
887  gpointer parent_data, gpointer global_data,
888  gpointer* result, const gchar* tag)
889 {
890  g_return_val_if_fail (data_for_children != NULL, FALSE);
891  *result = data_for_children;
892  return (TRUE);
893 }
894 
895 static void
896 kvp_frame_fail_handler (gpointer data_for_children,
897  GSList* data_from_children,
898  GSList* sibling_data,
899  gpointer parent_data,
900  gpointer global_data,
901  gpointer* result,
902  const gchar* tag)
903 {
904  auto f = static_cast<KvpFrame*> (data_for_children);
905  if (f) delete f;
906 }
907 
908 static void
909 kvp_frame_result_cleanup (sixtp_child_result* cr)
910 {
911  auto f = static_cast<KvpFrame*> (cr->data);
912  if (f) delete f;
913 }
914 
915 static sixtp*
916 kvp_frame_parser_new (void)
917 {
918  sixtp* top_level;
919 
920  if (! (top_level = sixtp_set_any (
921  sixtp_new (), FALSE,
922  SIXTP_START_HANDLER_ID, kvp_frame_start_handler,
923  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
924  SIXTP_END_HANDLER_ID, kvp_frame_end_handler,
925  SIXTP_CLEANUP_RESULT_ID, kvp_frame_result_cleanup,
926  SIXTP_RESULT_FAIL_ID, kvp_frame_result_cleanup,
927  SIXTP_FAIL_HANDLER_ID, kvp_frame_fail_handler,
928  SIXTP_NO_MORE_HANDLERS)))
929  {
930  return NULL;
931  }
932 
933  if (! (sixtp_add_some_sub_parsers (
934  top_level, TRUE,
935  "s", kvp_frame_slot_parser_new (top_level),
936  NULL, NULL)))
937  {
938  return NULL;
939  }
940 
941  return (top_level);
942 }
943 
944 /****************************************************************************/
945 /****************************************************************************/
946 /****************************************************************************/
947 /* <ledger-data> (parent <gnc-data>)
948 
949  On failure or on normal cleanup, the root account will be killed,
950  so if you want it, you better set should_cleanup to false
951 
952  input: NA
953  to-children-via-*result: new root Account*
954  returns: an Account*
955  start: creates the root account and puts it into *result
956  characters: NA
957  end: finishes up the root account and leaves it in result.
958  cleanup-result: deletes the root account (use should_cleanup to avoid).
959  cleanup-chars: NA
960  fail: deletes the root account in *result.
961  result-fail: same as cleanup-result.
962  chars-fail: NA
963 
964 */
965 
966 
967 static gboolean
968 ledger_data_start_handler (GSList* sibling_data, gpointer parent_data,
969  gpointer global_data, gpointer* data_for_children,
970  gpointer* result, const gchar* tag, gchar** attrs)
971 {
972  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
973  Account* ra;
974 
975  /* disable logging during load; otherwise its just a mess */
976  xaccLogDisable ();
977  ra = xaccMallocAccount (pstatus->book);
978 
979  g_return_val_if_fail (ra, FALSE);
980 
981  *data_for_children = ra;
982  return (ra != NULL);
983 }
984 
985 static gboolean
986 ledger_data_after_child_handler (gpointer data_for_children,
987  GSList* data_from_children,
988  GSList* sibling_data,
989  gpointer parent_data,
990  gpointer global_data,
991  gpointer* result,
992  const gchar* tag,
993  const gchar* child_tag,
994  sixtp_child_result* child_result)
995 {
996  if (!child_result) return (TRUE);
997 
998  /* if we see the pricedb, deal with it */
999  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
1000  if (strcmp (child_result->tag, "pricedb") == 0)
1001  {
1002  GNCPriceDB* pdb = (GNCPriceDB*) child_result->data;
1003  GNCParseStatus* status = (GNCParseStatus*) global_data;
1004 
1005  g_return_val_if_fail (pdb, FALSE);
1006  g_return_val_if_fail (status, FALSE);
1007 
1008  if (status->pricedb)
1009  {
1010  PERR ("hit pricedb twice in data file.");
1011  return FALSE;
1012  }
1013  status->pricedb = pdb;
1014  child_result->should_cleanup = FALSE;
1015  }
1016  return (TRUE);
1017 }
1018 
1019 static gboolean
1020 ledger_data_end_handler (gpointer data_for_children,
1021  GSList* data_from_children, GSList* sibling_data,
1022  gpointer parent_data, gpointer global_data,
1023  gpointer* result, const gchar* tag)
1024 {
1025 
1026  Account* ra = (Account*) data_for_children;
1027  GList* descendants;
1028 
1029  g_return_val_if_fail (ra, FALSE);
1030 
1031  /* commit all accounts, this completes the BeginEdit started when the
1032  * account_end_handler finished reading the account.
1033  */
1034  descendants = gnc_account_get_descendants (ra);
1035  g_list_foreach (descendants, (GFunc)xaccAccountCommitEdit, NULL);
1036  g_list_free (descendants);
1037 
1038  xaccLogEnable ();
1039 
1040  *result = ra;
1041  return (TRUE);
1042 }
1043 
1044 static void
1045 ledger_data_fail_handler (gpointer data_for_children,
1046  GSList* data_from_children,
1047  GSList* sibling_data,
1048  gpointer parent_data,
1049  gpointer global_data,
1050  gpointer* result,
1051  const gchar* tag)
1052 {
1053  Account* account = (Account*) data_for_children;
1054  if (account)
1055  {
1056  xaccAccountBeginEdit (account);
1057  xaccAccountDestroy (account);
1058  }
1059 }
1060 
1061 static void
1062 ledger_data_result_cleanup (sixtp_child_result* cr)
1063 {
1064  Account* account = (Account*) cr->data;
1065  if (account)
1066  {
1067  xaccAccountBeginEdit (account);
1068  xaccAccountDestroy (account);
1069  }
1070 }
1071 
1072 
1073 static sixtp*
1074 ledger_data_parser_new (void)
1075 {
1076  sixtp* top_level;
1077 
1078  /* <ledger-data> */
1079  if (! (top_level = sixtp_set_any (
1080  sixtp_new (), FALSE,
1081  SIXTP_START_HANDLER_ID, ledger_data_start_handler,
1082  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
1083  SIXTP_AFTER_CHILD_HANDLER_ID, ledger_data_after_child_handler,
1084  SIXTP_END_HANDLER_ID, ledger_data_end_handler,
1085  SIXTP_CLEANUP_RESULT_ID, ledger_data_result_cleanup,
1086  SIXTP_FAIL_HANDLER_ID, ledger_data_fail_handler,
1087  SIXTP_RESULT_FAIL_ID, ledger_data_result_cleanup,
1088  SIXTP_NO_MORE_HANDLERS)))
1089  {
1090  return NULL;
1091  }
1092 
1093  if (!sixtp_add_some_sub_parsers (
1094  top_level, TRUE,
1095  "commodity", commodity_restore_parser_new (),
1096  "pricedb", gnc_pricedb_parser_new (),
1097  "account", gnc_account_parser_new (),
1098  "transaction", gnc_transaction_parser_new (),
1099  NULL, NULL))
1100  {
1101  return NULL;
1102  }
1103 
1104  return (top_level);
1105 }
1106 
1107 /***********************************************************************/
1108 /****************************************************************************/
1109 /* <account> (parent <ledger-data>)
1110 
1111  This block does nothing but pass the ledger-data account group down
1112  to its children. It generates no data of its own, so it doesn't
1113  need any cleanup.
1114 
1115  input: Account*
1116 
1117  to-children-via-*result: Account*
1118 
1119  returns: NA
1120 
1121  start: pass input to children.
1122 
1123  characters: NA
1124 
1125  end: NA
1126 
1127  cleanup-result: NA
1128 
1129  cleanup-chars: NA
1130 
1131  fail: NA
1132 
1133  result-fail: NA
1134 
1135  chars-fail: NA
1136 
1137  */
1138 
1139 static gboolean
1140 account_start_handler (GSList* sibling_data,
1141  gpointer parent_data,
1142  gpointer global_data,
1143  gpointer* data_for_children,
1144  gpointer* result,
1145  const gchar* tag,
1146  gchar** attrs)
1147 {
1148  /* pass the parent data down to the children */
1149  *data_for_children = parent_data;
1150  return (TRUE);
1151 }
1152 
1153 /****************************************************************************/
1154 /* <restore> (lineage <account> <ledger-data>)
1155 
1156  restores a given account. We allocate the new account in the
1157  start block, the children modify it, and in the end block, we see
1158  if the resultant account is OK, and if so, we add it to the
1159  ledger-data's account group.
1160 
1161  input: Account*
1162  to-children-via-*result: new Account*
1163  returns: NA
1164  start: create new Account*, and leave in for children.
1165  characters: NA
1166  end: clear *result
1167  cleanup-result: NA
1168  cleanup-chars: NA
1169  fail: delete Account*
1170  result-fail: NA
1171  chars-fail: NA
1172  */
1173 
1174 static gboolean
1175 account_restore_start_handler (GSList* sibling_data,
1176  gpointer parent_data,
1177  gpointer global_data,
1178  gpointer* data_for_children,
1179  gpointer* result,
1180  const gchar* tag,
1181  gchar** attrs)
1182 {
1183  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
1184  Account* acc = xaccMallocAccount (pstatus->book);
1185 
1186  g_return_val_if_fail (acc, FALSE);
1187  xaccAccountBeginEdit (acc);
1188 
1189  *data_for_children = acc;
1190  *result = acc;
1191 
1192  return (TRUE);
1193 }
1194 
1195 static gboolean
1196 account_restore_end_handler (gpointer data_for_children,
1197  GSList* data_from_children, GSList* sibling_data,
1198  gpointer parent_data, gpointer global_data,
1199  gpointer* result, const gchar* tag)
1200 {
1201  Account* parent = (Account*) parent_data;
1202  Account* acc = (Account*) * result;
1203 
1204  g_return_val_if_fail ((parent && acc), FALSE);
1205 
1206  /* CHECKME: do we need to xaccAccountRecomputeBalance(acc) here? */
1207  xaccAccountCommitEdit (acc);
1208 
1209  /* If the account doesn't have a parent yet, just cram it into the
1210  top level */
1211  if (!gnc_account_get_parent (acc))
1212  gnc_account_append_child (parent, acc);
1213 
1214  *result = NULL;
1215 
1216  /* Now return the account to the "edit" state. At the end of reading
1217  * all the transactions, we will Commit. This replaces #splits
1218  * rebalances with #accounts rebalances at the end. A BIG win!
1219  */
1220  xaccAccountBeginEdit (acc);
1221  return (TRUE);
1222 }
1223 
1224 static gboolean
1225 account_restore_after_child_handler (gpointer data_for_children,
1226  GSList* data_from_children,
1227  GSList* sibling_data,
1228  gpointer parent_data,
1229  gpointer global_data,
1230  gpointer* result,
1231  const gchar* tag,
1232  const gchar* child_tag,
1233  sixtp_child_result* child_result)
1234 {
1235  Account* a = (Account*) data_for_children;
1236  /* GNCParseStatus *pstatus = (GNCParseStatus *) global_data; */
1237 
1238  g_return_val_if_fail (a, FALSE);
1239 
1240  if (!child_result) return (TRUE);
1241  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
1242  if (strcmp (child_result->tag, "slots") == 0)
1243  {
1244  auto f = static_cast<KvpFrame*> (child_result->data);
1245  g_return_val_if_fail (f, FALSE);
1246  if (a->inst.kvp_data) delete a->inst.kvp_data;
1247  a->inst.kvp_data = f;
1248  child_result->should_cleanup = FALSE;
1249  }
1250  else if (strcmp (child_result->tag, "currency") == 0)
1251  {
1252  gnc_commodity* com = (gnc_commodity*) child_result->data;
1253  g_return_val_if_fail (com, FALSE);
1254  if (DxaccAccountGetCurrency (a)) return FALSE;
1255  DxaccAccountSetCurrency (a, com);
1256  /* let the normal child_result handler clean up com */
1257  }
1258  else if (strcmp (child_result->tag, "security") == 0)
1259  {
1260  gnc_commodity* com = (gnc_commodity*) child_result->data;
1261  g_return_val_if_fail (com, FALSE);
1262  if (xaccAccountGetCommodity (a)) return FALSE;
1263  xaccAccountSetCommodity (a, com);
1264  /* let the normal child_result handler clean up com */
1265  }
1266 
1267  return (TRUE);
1268 }
1269 
1270 static void
1271 account_restore_fail_handler (gpointer data_for_children,
1272  GSList* data_from_children,
1273  GSList* sibling_data,
1274  gpointer parent_data,
1275  gpointer global_data,
1276  gpointer* result,
1277  const gchar* tag)
1278 {
1279  Account* acc = (Account*) * result;
1280  if (acc)
1281  {
1282  xaccAccountBeginEdit (acc);
1283  xaccAccountDestroy (acc);
1284  }
1285 }
1286 
1287 /****************************************************************************/
1288 /* <name> (lineage <restore> <account>)
1289 
1290  restores a given account's name.
1291  input: Account*
1292  returns: NA
1293 
1294  start: NA
1295  characters: return string copy for accumulation in end handler.
1296  end: concatenate all chars and set as account name.
1297 
1298  cleanup-result: NA
1299  cleanup-chars: g_free the result string.
1300  fail: NA
1301  result-fail: NA
1302  chars-fail: g_free the result string.
1303 
1304  */
1305 static gboolean
1306 acc_restore_name_end_handler (gpointer data_for_children,
1307  GSList* data_from_children, GSList* sibling_data,
1308  gpointer parent_data, gpointer global_data,
1309  gpointer* result, const gchar* tag)
1310 {
1311  Account* acc = (Account*) parent_data;
1312  gchar* name = NULL;
1313 
1314  g_return_val_if_fail (acc, FALSE);
1315 
1316  name = concatenate_child_result_chars (data_from_children);
1317  g_return_val_if_fail (name, FALSE);
1318 
1319  xaccAccountSetName (acc, name);
1320  g_free (name);
1321  return (TRUE);
1322 }
1323 
1324 /****************************************************************************/
1325 /* <guid> (lineage <restore> <account>)
1326 
1327  restores a given account's guid.
1328  input: Account*
1329  returns: NA
1330 
1331  start: NA
1332  characters: return string copy for accumulation in end handler.
1333  end: concatenate all chars and set as account GncGUID if not duplicate.
1334 
1335  cleanup-result: NA
1336  cleanup-chars: g_free the result string.
1337  fail: NA
1338  result-fail: NA
1339  chars-fail: g_free the result string.
1340 
1341  */
1342 
1343 static gboolean
1344 acc_restore_guid_end_handler (gpointer data_for_children,
1345  GSList* data_from_children, GSList* sibling_data,
1346  gpointer parent_data, gpointer global_data,
1347  gpointer* result, const gchar* tag)
1348 {
1349  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
1350  Account* acc = (Account*) parent_data;
1351  gchar* txt = NULL;
1352  GncGUID gid;
1353  gboolean ok;
1354 
1355  g_return_val_if_fail (acc, FALSE);
1356 
1357  txt = concatenate_child_result_chars (data_from_children);
1358  g_return_val_if_fail (txt, FALSE);
1359 
1360  ok = string_to_guid (txt, &gid);
1361  g_free (txt);
1362 
1363  g_return_val_if_fail (ok, FALSE);
1364 
1365  if (xaccAccountLookup (&gid, pstatus->book))
1366  {
1367  return (FALSE);
1368  }
1369 
1370  xaccAccountSetGUID (acc, &gid);
1371  return (TRUE);
1372 }
1373 
1374 /****************************************************************************/
1375 /* <type> (lineage <restore> <account>)
1376 
1377  restores a given account's type.
1378  input: Account*
1379  returns: NA
1380 
1381  start: NA
1382  characters: return string copy for accumulation in end handler.
1383  end: concatenate all chars and set as account type.
1384 
1385  cleanup-result: NA
1386  cleanup-chars: g_free the result string.
1387  fail: NA
1388  result-fail: NA
1389  chars-fail: g_free the result string.
1390 
1391  */
1392 
1393 static gboolean
1394 acc_restore_type_end_handler (gpointer data_for_children,
1395  GSList* data_from_children, GSList* sibling_data,
1396  gpointer parent_data, gpointer global_data,
1397  gpointer* result, const gchar* tag)
1398 {
1399  Account* acc = (Account*) parent_data;
1400  gchar* txt = NULL;
1401  GNCAccountType type;
1402  gboolean ok;
1403 
1404  g_return_val_if_fail (acc, FALSE);
1405 
1406  txt = concatenate_child_result_chars (data_from_children);
1407  g_return_val_if_fail (txt, FALSE);
1408 
1409  ok = xaccAccountStringToType (txt, &type);
1410  g_free (txt);
1411 
1412  g_return_val_if_fail (ok, FALSE);
1413 
1414  xaccAccountSetType (acc, type);
1415  return (TRUE);
1416 }
1417 
1418 /****************************************************************************/
1419 /* <code> (lineage <restore> <account>)
1420 
1421  restores a given account's code.
1422  input: Account*
1423  returns: NA
1424 
1425  start: NA
1426  characters: return string copy for accumulation in end handler.
1427  end: concatenate all chars and set as account type.
1428 
1429  cleanup-result: NA
1430  cleanup-chars: g_free the result string.
1431  fail: NA
1432  result-fail: NA
1433  chars-fail: g_free the result string.
1434 
1435  */
1436 
1437 static gboolean
1438 acc_restore_code_end_handler (gpointer data_for_children,
1439  GSList* data_from_children, GSList* sibling_data,
1440  gpointer parent_data, gpointer global_data,
1441  gpointer* result, const gchar* tag)
1442 {
1443  Account* acc = (Account*) parent_data;
1444  gchar* txt = NULL;
1445 
1446  g_return_val_if_fail (acc, FALSE);
1447 
1448  txt = concatenate_child_result_chars (data_from_children);
1449  g_return_val_if_fail (txt, FALSE);
1450 
1451  xaccAccountSetCode (acc, txt);
1452  g_free (txt);
1453  return (TRUE);
1454 }
1455 
1456 /****************************************************************************/
1457 /* <description> (lineage <restore> <account>)
1458 
1459  restores a given account's description.
1460  input: Account*
1461  returns: NA
1462 
1463  start: NA
1464  characters: return string copy for accumulation in end handler.
1465  end: concatenate all chars and set as account description.
1466 
1467  cleanup-result: NA
1468  cleanup-chars: g_free the result string.
1469  fail: NA
1470  result-fail: NA
1471  chars-fail: g_free the result string.
1472  restores a given account's description.
1473 
1474  */
1475 
1476 static gboolean
1477 acc_restore_description_end_handler (gpointer data_for_children,
1478  GSList* data_from_children, GSList* sibling_data,
1479  gpointer parent_data, gpointer global_data,
1480  gpointer* result, const gchar* tag)
1481 {
1482  Account* acc = (Account*) parent_data;
1483  gchar* txt = NULL;
1484 
1485  g_return_val_if_fail (acc, FALSE);
1486 
1487  txt = concatenate_child_result_chars (data_from_children);
1488  g_return_val_if_fail (txt, FALSE);
1489 
1490  xaccAccountSetDescription (acc, txt);
1491  g_free (txt);
1492  return (TRUE);
1493 }
1494 
1495 /****************************************************************************/
1496 /* <notes> (lineage <restore> <account>)
1497 
1498  restores a given account's notes.
1499  input: Account*
1500  returns: NA
1501 
1502  start: NA
1503  characters: return string copy for accumulation in end handler.
1504  end: concatenate all chars and set as account notes.
1505 
1506  cleanup-result: NA
1507  cleanup-chars: g_free the result string.
1508  fail: NA
1509  result-fail: NA
1510  chars-fail: g_free the result string.
1511 
1512  */
1513 
1514 static gboolean
1515 acc_restore_notes_end_handler (gpointer data_for_children,
1516  GSList* data_from_children, GSList* sibling_data,
1517  gpointer parent_data, gpointer global_data,
1518  gpointer* result, const gchar* tag)
1519 {
1520  Account* acc = (Account*) parent_data;
1521  gchar* txt = NULL;
1522 
1523  g_return_val_if_fail (acc, FALSE);
1524 
1525  txt = concatenate_child_result_chars (data_from_children);
1526  g_return_val_if_fail (txt, FALSE);
1527 
1528  xaccAccountSetNotes (acc, txt);
1529  g_free (txt);
1530  return (TRUE);
1531 }
1532 
1533 /****************************************************************************/
1534 /* <parent> (lineage <restore> <account>)
1535 
1536  restores a given account's parent.
1537  input: Account*
1538  returns: NA
1539 
1540  start: NA
1541 
1542  characters: allow and ignore only whitespace.
1543 
1544  end: check for single <guid> child and if found, use result to set
1545  account guid.
1546 
1547  cleanup-result: NA
1548  cleanup-chars: NA
1549  fail: NA
1550  result-fail: NA
1551  chars-fail: NA
1552 
1553  */
1554 
1555 static gboolean
1556 acc_restore_parent_end_handler (gpointer data_for_children,
1557  GSList* data_from_children, GSList* sibling_data,
1558  gpointer parent_data, gpointer global_data,
1559  gpointer* result, const gchar* tag)
1560 {
1561  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
1562  Account* acc = (Account*) parent_data;
1563  Account* parent;
1564  sixtp_child_result* child_result;
1565  GncGUID gid;
1566 
1567  g_return_val_if_fail (acc, FALSE);
1568 
1569  if (g_slist_length (data_from_children) != 1)
1570  return (FALSE);
1571 
1572  child_result = (sixtp_child_result*) data_from_children->data;
1573 
1574  if (!is_child_result_from_node_named (child_result, "guid"))
1575  return (FALSE);
1576 
1577  /* otherwise this must be a good result - use it */
1578  gid = * ((GncGUID*) child_result->data);
1579 
1580  parent = xaccAccountLookup (&gid, pstatus->book);
1581 
1582  g_return_val_if_fail (parent, FALSE);
1583 
1584  gnc_account_append_child (parent, acc);
1585 
1586  return (TRUE);
1587 }
1588 
1589 static sixtp*
1590 parent_lookup_parser_new (void)
1591 {
1592  return sixtp_set_any (sixtp_new (), TRUE,
1593  SIXTP_CHARACTERS_HANDLER_ID,
1594  allow_and_ignore_only_whitespace,
1595  SIXTP_END_HANDLER_ID,
1596  acc_restore_parent_end_handler,
1597  SIXTP_NO_MORE_HANDLERS);
1598 }
1599 
1600 static sixtp*
1601 gnc_account_parser_new (void)
1602 {
1603  sixtp* restore_pr;
1604  sixtp* ret;
1605 
1606  /* <account> */
1607  if (! (ret = sixtp_set_any (
1608  sixtp_new (), FALSE,
1609  SIXTP_START_HANDLER_ID, account_start_handler,
1610  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
1611  SIXTP_NO_MORE_HANDLERS)))
1612  {
1613  return NULL;
1614  }
1615 
1616  /* <account> <restore> */
1617  if (! (restore_pr =
1618  sixtp_set_any (sixtp_new (), FALSE,
1619  SIXTP_START_HANDLER_ID, account_restore_start_handler,
1620  SIXTP_END_HANDLER_ID, account_restore_end_handler,
1621  SIXTP_FAIL_HANDLER_ID, account_restore_fail_handler,
1622  SIXTP_AFTER_CHILD_HANDLER_ID,
1623  account_restore_after_child_handler,
1624  SIXTP_NO_MORE_HANDLERS)))
1625  {
1626  sixtp_destroy (ret);
1627  return NULL;
1628  }
1629 
1630  /* <restore> (<name> | <guid> | <type> | <code> | <description> | <notes>)*/
1631  if (!sixtp_add_some_sub_parsers (
1632  restore_pr, TRUE,
1633  "name", restore_char_generator (acc_restore_name_end_handler),
1634  "guid", restore_char_generator (acc_restore_guid_end_handler),
1635  "type", restore_char_generator (acc_restore_type_end_handler),
1636  "code", restore_char_generator (acc_restore_code_end_handler),
1637  "description",
1638  restore_char_generator (acc_restore_description_end_handler),
1639  "notes", restore_char_generator (acc_restore_notes_end_handler),
1640  /* <account> <restore> <currency> */
1641  "currency", generic_gnc_commodity_lookup_parser_new (),
1642  /* <account> <restore> <security> */
1643  "security", generic_gnc_commodity_lookup_parser_new (),
1644  /* <account> <restore> <parent> */
1645  "parent", sixtp_add_some_sub_parsers (
1646  parent_lookup_parser_new (), TRUE,
1647  "guid", generic_guid_parser_new (),
1648  NULL, NULL),
1649  "slots", kvp_frame_parser_new (),
1650  NULL, NULL))
1651  {
1652  sixtp_destroy (ret);
1653  return NULL;
1654  }
1655 
1656  sixtp_add_sub_parser (ret, "restore", restore_pr);
1657 
1658  return ret;
1659 }
1660 /***********************************************************************/
1661 /****************************************************************************/
1662 /* Commodity restorer.
1663 
1664  Right now we just check to see that fields aren't duplicated. If
1665  fields don't show up, then we just use "".
1666 
1667  We also check to see that we get a <fraction>. If not, it's an
1668  error.
1669 
1670  Example:
1671  <commodity>
1672  <restore>
1673  <space>NASDAQ</space>
1674  <id>XYZZY</id>
1675  <name>Grue Enterprises</name>
1676  <xcode>XXX</xcode>
1677  <fraction>100</fraction>
1678  </restore>
1679  </commodity>
1680 
1681  */
1682 
1683 /* ==================================================================== */
1684 
1685 /*********************************/
1686 /* <restore> (lineage <commodity>)
1687 
1688  Start handler allocates a gnc_commodity. The end_handler, if
1689  everything's OK, crams the commodity into the engine, otherwise it
1690  deletes it.
1691 
1692  input: NA
1693  returns: NA
1694 
1695  start: allocate CommodityParseInfo* and put it into data_for_children.
1696  characters: allow and ignore only whitespace.
1697  after-child: handle strings from simple chars children.
1698  end: if OK create gnc_commodity and add to engine. delete CommodityParseInfo.
1699 
1700  cleanup-result: NA
1701  cleanup-chars: NA
1702  fail: delete CommodityParseInfo*.
1703  result-fail: NA
1704  chars-fail: NA
1705 
1706  */
1707 
1708 typedef struct
1709 {
1710  gchar* space;
1711  gchar* id;
1712  gchar* name;
1713  gchar* xcode;
1714  gboolean seen_fraction;
1715  int fraction;
1717 
1718 static gboolean
1719 commodity_restore_start_handler (GSList* sibling_data, gpointer parent_data,
1720  gpointer global_data,
1721  gpointer* data_for_children, gpointer* result,
1722  const gchar* tag, gchar** attrs)
1723 {
1724  CommodityParseInfo* cpi =
1725  (CommodityParseInfo*) g_new0 (CommodityParseInfo, 1);
1726 
1727  g_return_val_if_fail (cpi, FALSE);
1728 
1729  *data_for_children = cpi;
1730  return (TRUE);
1731 }
1732 
1733 /* ----------------------------------------------------*/
1734 #define COMMOD_TOKEN(NAME) \
1735  if(strcmp(child_result->tag, #NAME) == 0) { \
1736  if(cpi->NAME) return(FALSE); \
1737  cpi->NAME = (gchar *) child_result->data; \
1738  child_result->should_cleanup = FALSE; \
1739  } \
1740  else
1741 /* ----------------------------------------------------*/
1742 
1743 static gboolean
1744 commodity_restore_after_child_handler (gpointer data_for_children,
1745  GSList* data_from_children,
1746  GSList* sibling_data,
1747  gpointer parent_data,
1748  gpointer global_data,
1749  gpointer* result,
1750  const gchar* tag,
1751  const gchar* child_tag,
1752  sixtp_child_result* child_result)
1753 {
1754  CommodityParseInfo* cpi = (CommodityParseInfo*) data_for_children;
1755 
1756  g_return_val_if_fail (cpi, FALSE);
1757  g_return_val_if_fail (child_result, FALSE);
1758 
1759  COMMOD_TOKEN (space)
1760  COMMOD_TOKEN (id)
1761  COMMOD_TOKEN (name)
1762  COMMOD_TOKEN (xcode)
1763  if (strcmp (child_result->tag, "fraction") == 0)
1764  {
1765  gint64 frac;
1766 
1767  if (cpi->seen_fraction) return (FALSE);
1768  string_to_gint64 ((gchar*) child_result->data, &frac);
1769  cpi->fraction = frac;
1770  cpi->seen_fraction = TRUE;
1771  child_result->should_cleanup = TRUE;
1772  }
1773  else
1774  {
1775  /* redundant because the parser won't allow any other children */
1776  return (FALSE);
1777  }
1778 
1779  return (TRUE);
1780 }
1781 
1782 static gboolean
1783 commodity_restore_end_handler (gpointer data_for_children,
1784  GSList* data_from_children, GSList* sibling_data,
1785  gpointer parent_data, gpointer global_data,
1786  gpointer* result, const gchar* tag)
1787 {
1788  CommodityParseInfo* cpi = (CommodityParseInfo*) data_for_children;
1789  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
1790  gboolean ok = FALSE;
1791  gnc_commodity* comm = NULL;
1792 
1793  g_return_val_if_fail (cpi, FALSE);
1794 
1795  if (cpi->seen_fraction)
1796  {
1797  gnc_commodity* comm;
1798 
1799  if (!cpi->space) cpi->space = g_strdup ("");
1800  if (!cpi->id) cpi->id = g_strdup ("");
1801  if (!cpi->name) cpi->name = g_strdup ("");
1802  if (!cpi->xcode) cpi->xcode = g_strdup ("");
1803 
1804  comm = gnc_commodity_new (pstatus->book,
1805  cpi->name,
1806  cpi->space,
1807  cpi->id,
1808  cpi->xcode,
1809  cpi->fraction);
1810  if (comm)
1811  {
1812  gnc_commodity_table* ctab;
1813 
1814  ctab = gnc_commodity_table_get_table (pstatus->book);
1815 
1816  if (ctab)
1817  {
1818  gnc_commodity_table_insert (ctab, comm);
1819  ok = TRUE;
1820  }
1821  }
1822  }
1823 
1824  g_free (cpi->space);
1825  g_free (cpi->id);
1826  g_free (cpi->name);
1827  g_free (cpi->xcode);
1828  g_free (cpi);
1829 
1830  if (!ok) gnc_commodity_destroy (comm);
1831 
1832  return (ok);
1833 }
1834 
1835 
1836 static sixtp*
1837 commodity_restore_parser_new (void)
1838 {
1839  sixtp* top_level;
1840  sixtp* restore_pr;
1841 
1842  top_level = sixtp_new ();
1843  g_return_val_if_fail (top_level, NULL);
1844 
1845  if (! (restore_pr = sixtp_set_any (
1846  sixtp_new (), FALSE,
1847  SIXTP_START_HANDLER_ID, commodity_restore_start_handler,
1848  SIXTP_END_HANDLER_ID, commodity_restore_end_handler,
1849  SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
1850  SIXTP_AFTER_CHILD_HANDLER_ID, commodity_restore_after_child_handler,
1851  SIXTP_NO_MORE_HANDLERS)))
1852  {
1853  sixtp_destroy (top_level);
1854  return (NULL);
1855  }
1856  sixtp_add_sub_parser (top_level, "restore", restore_pr);
1857 
1858  if (!sixtp_add_some_sub_parsers (
1859  restore_pr, TRUE,
1860  "space", simple_chars_only_parser_new (NULL),
1861  "id", simple_chars_only_parser_new (NULL),
1862  "name", simple_chars_only_parser_new (NULL),
1863  "xcode", simple_chars_only_parser_new (NULL),
1864  "fraction", simple_chars_only_parser_new (NULL),
1865  NULL, NULL))
1866  {
1867  return NULL;
1868  }
1869 
1870  return (top_level);
1871 }
1872 
1873 /****************************************************************************/
1874 /* generic gnc_commodity lookup handler.
1875 
1876  A collection of node functions intended to parse a sub-node set
1877  that looks like this:
1878 
1879  <security>
1880  <space>NASDAQ</space>
1881  <id>ZXDDQ</id>
1882  </security>
1883 
1884  and produce a gnc_commodity* by looking up the unique combination
1885  of namespace and ID (mnemonic).
1886 
1887  The start handler for the top allocates a CommodityParseInfo* and
1888  passes it to the children. The <space> block sets the namespace
1889  and the <id> block sets the ID. The end handler performs the
1890  lookup. If all goes well, returns the gnc_commodity* as the
1891  result. */
1892 
1893 /* Top level gnc_commodity lookup node:
1894 
1895  input: NA
1896  returns: gnc_commodity*
1897 
1898  start: Allocates CommodityParseInfo* for data_for_children.
1899  characters: none (whitespace only).
1900  end: lookup commodity and place into *result, free data_for_children.
1901 
1902  fail: g_free data_for_children (CommodityParseInfo and contents).
1903  cleanup-chars: NA
1904  chars-fail: NA
1905  cleanup-result: NA (we didn't create the gnc_commodity we're returning)
1906  result-fail: NA
1907 
1908  */
1909 
1910 typedef struct
1911 {
1912  gchar* name_space;
1913  gchar* id;
1915 
1916 static gboolean
1917 generic_gnc_commodity_lookup_start_handler (
1918  GSList* sibling_data, gpointer parent_data, gpointer global_data,
1919  gpointer* data_for_children, gpointer* result, const gchar* tag,
1920  gchar** attrs)
1921 {
1923  g_return_val_if_fail (cpi, FALSE);
1924  *data_for_children = cpi;
1925  return (TRUE);
1926 }
1927 
1928 static gboolean
1929 generic_gnc_commodity_lookup_after_child_handler (gpointer data_for_children,
1930  GSList* data_from_children,
1931  GSList* sibling_data,
1932  gpointer parent_data,
1933  gpointer global_data,
1934  gpointer* result,
1935  const gchar* tag,
1936  const gchar* child_tag,
1937  sixtp_child_result* child_result)
1938 {
1940  (CommodityLookupParseInfo*) data_for_children;
1941 
1942  g_return_val_if_fail (cpi, FALSE);
1943  g_return_val_if_fail (child_result, FALSE);
1944  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (FALSE);
1945 
1946  if (strcmp (child_result->tag, "space") == 0)
1947  {
1948  if (cpi->name_space) return (FALSE);
1949  cpi->name_space = (gchar*) child_result->data;
1950  child_result->should_cleanup = FALSE;
1951  }
1952  else if (strcmp (child_result->tag, "id") == 0)
1953  {
1954  if (cpi->id) return (FALSE);
1955  cpi->id = (gchar*) child_result->data;
1956  child_result->should_cleanup = FALSE;
1957  }
1958  else
1959  {
1960  /* redundant because the parser won't allow any other children */
1961  return (FALSE);
1962  }
1963 
1964  return (TRUE);
1965 }
1966 
1967 static gboolean
1968 generic_gnc_commodity_lookup_end_handler (gpointer data_for_children,
1969  GSList* data_from_children, GSList* sibling_data,
1970  gpointer parent_data, gpointer global_data,
1971  gpointer* result, const gchar* tag)
1972 {
1974  (CommodityLookupParseInfo*) data_for_children;
1975  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
1976  gboolean ok = FALSE;
1977 
1978  g_return_val_if_fail (cpi, FALSE);
1979 
1980  if (cpi->name_space && cpi->id)
1981  {
1982  gnc_commodity_table* table;
1983  gnc_commodity* com;
1984 
1985  table = gnc_commodity_table_get_table (pstatus->book);
1986 
1987  com = gnc_commodity_table_lookup (table, cpi->name_space, cpi->id);
1988 
1989  if (com)
1990  {
1991  *result = com;
1992  ok = TRUE;
1993  }
1994  }
1995 
1996  g_free (cpi->name_space);
1997  g_free (cpi->id);
1998  g_free (cpi);
1999 
2000  return (ok);
2001 }
2002 
2003 
2004 static sixtp*
2005 generic_gnc_commodity_lookup_parser_new (void)
2006 {
2007  sixtp* top_level;
2008 
2009  if (! (top_level = sixtp_set_any (
2010  sixtp_new (), FALSE,
2011  SIXTP_START_HANDLER_ID, generic_gnc_commodity_lookup_start_handler,
2012  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
2013  SIXTP_END_HANDLER_ID, generic_gnc_commodity_lookup_end_handler,
2014  SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
2015  SIXTP_AFTER_CHILD_HANDLER_ID,
2016  generic_gnc_commodity_lookup_after_child_handler,
2017  SIXTP_NO_MORE_HANDLERS)))
2018  {
2019  return NULL;
2020  }
2021 
2022  if (!sixtp_add_some_sub_parsers (
2023  top_level, TRUE,
2024  "space", simple_chars_only_parser_new (NULL),
2025  "id", simple_chars_only_parser_new (NULL),
2026  NULL, NULL))
2027  {
2028  return NULL;
2029  }
2030 
2031  return (top_level);
2032 }
2033 
2034 /****************************************************************************/
2035 /* <transaction> (parent <ledger-data>)
2036 
2037  This block does nothing but pass the ledger-data account group down
2038  to its children. It generates no data of its own, so it doesn't
2039  need any cleanup.
2040 
2041  input: Account*
2042 
2043  to-children-via-*result: Account*
2044 
2045  returns: NA
2046 
2047  start: pass input to children.
2048 
2049  characters: ignore whitespace only
2050 
2051  end: NA
2052 
2053  cleanup-result: NA
2054 
2055  cleanup-chars: NA
2056 
2057  fail: NA
2058 
2059  result-fail: NA
2060 
2061  chars-fail: NA
2062 
2063  */
2064 
2065 static gboolean
2066 transaction_start_handler (GSList* sibling_data, gpointer parent_data,
2067  gpointer global_data, gpointer* data_for_children,
2068  gpointer* result, const gchar* tag, gchar** attrs)
2069 {
2070  /* pass the parent data down to the children */
2071  *data_for_children = parent_data;
2072  return (TRUE);
2073 }
2074 
2075 /****************************************************************************/
2076 /* <restore> (lineage <transaction> <ledger-data>)
2077 
2078  restores a given transaction. We allocate the new transaction in
2079  the start block, the children modify it, and in the end block, we
2080  see if the resultant account is OK, and if so, we add it to the
2081  ledger-data's account group.
2082 
2083  from parent: Account*
2084 
2085  for children: new Transaction*
2086 
2087  result: NA
2088 
2089  -----------
2090 
2091  start: create new Transaction*, and store in data_for_children.
2092 
2093  chars: allow and ignore only whitespace.
2094 
2095  end: commit transaction to group if appropriate.
2096 
2097  cleanup-result: NA
2098 
2099  cleanup-chars: NA
2100 
2101  fail: delete Transaction* in data_for_children
2102 
2103  result-fail: NA
2104 
2105  chars-fail: NA
2106 
2107  */
2108 
2109 static gboolean
2110 txn_restore_start_handler (GSList* sibling_data, gpointer parent_data,
2111  gpointer global_data, gpointer* data_for_children,
2112  gpointer* result, const gchar* tag, gchar** attrs)
2113 {
2114  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
2115  Transaction* trans = xaccMallocTransaction (pstatus->book);
2116 
2117  g_return_val_if_fail (trans, FALSE);
2118 
2119  xaccTransBeginEdit (trans);
2120  *data_for_children = trans;
2121 
2122  return (TRUE);
2123 }
2124 
2125 static gboolean
2126 txn_restore_end_handler (gpointer data_for_children,
2127  GSList* data_from_children, GSList* sibling_data,
2128  gpointer parent_data, gpointer global_data,
2129  gpointer* result, const gchar* tag)
2130 {
2131  Account* parent = (Account*) parent_data;
2132  Transaction* trans = (Transaction*) data_for_children;
2133 
2134  g_return_val_if_fail (trans, FALSE);
2135  if (!parent)
2136  {
2137  xaccTransDestroy (trans);
2138  xaccTransCommitEdit (trans);
2139  return (FALSE);
2140  }
2141 
2142  if (!xaccTransGetGUID (trans))
2143  {
2144  /* must at least have a GncGUID for a restore */
2145  xaccTransDestroy (trans);
2146  xaccTransCommitEdit (trans);
2147  return (FALSE);
2148  }
2149 
2150  /* FIXME: what if the trans has no splits? */
2151  xaccTransCommitEdit (trans);
2152 
2153  return (TRUE);
2154 }
2155 
2156 static gboolean
2157 txn_restore_after_child_handler (gpointer data_for_children,
2158  GSList* data_from_children,
2159  GSList* sibling_data,
2160  gpointer parent_data,
2161  gpointer global_data,
2162  gpointer* result,
2163  const gchar* tag,
2164  const gchar* child_tag,
2165  sixtp_child_result* child_result)
2166 {
2167  Transaction* trans = (Transaction*) data_for_children;
2168  g_return_val_if_fail (trans, FALSE);
2169  if (!child_result) return (TRUE);
2170  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
2171  if (strcmp (child_result->tag, "slots") == 0)
2172  {
2173  KvpFrame* f = (KvpFrame*) child_result->data;
2174  g_return_val_if_fail (f, FALSE);
2175  qof_instance_set_slots (QOF_INSTANCE (trans), f);
2176  child_result->should_cleanup = FALSE;
2177  }
2178  return (TRUE);
2179 }
2180 
2181 static void
2182 txn_restore_fail_handler (gpointer data_for_children,
2183  GSList* data_from_children,
2184  GSList* sibling_data,
2185  gpointer parent_data,
2186  gpointer global_data,
2187  gpointer* result,
2188  const gchar* tag)
2189 {
2190  Transaction* trans = (Transaction*) data_for_children;
2191  if (trans)
2192  {
2193  xaccTransDestroy (trans);
2194  xaccTransCommitEdit (trans);
2195  }
2196 }
2197 
2198 /****************************************************************************/
2199 /* <guid> (lineage <restore> <transaction>)
2200 
2201  restores a given account's guid.
2202 
2203  from parent: Transaction*
2204  for children: NA
2205  result: NA
2206  -----------
2207  start: NA
2208  characters: return string copy for accumulation in end handler.
2209  end: concatenate all chars and set as transaction GncGUID if not duplicate.
2210 
2211  cleanup-result: NA
2212  cleanup-chars: g_free the result string.
2213  fail: NA
2214  result-fail: NA
2215  chars-fail: g_free the result string.
2216 
2217  */
2218 
2219 static gboolean
2220 txn_restore_guid_end_handler (gpointer data_for_children,
2221  GSList* data_from_children, GSList* sibling_data,
2222  gpointer parent_data, gpointer global_data,
2223  gpointer* result, const gchar* tag)
2224 {
2225  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
2226  Transaction* t = (Transaction*) parent_data;
2227  gchar* txt = NULL;
2228  GncGUID gid;
2229  gboolean ok;
2230 
2231  g_return_val_if_fail (t, FALSE);
2232 
2233  txt = concatenate_child_result_chars (data_from_children);
2234  g_return_val_if_fail (txt, FALSE);
2235 
2236  ok = string_to_guid (txt, &gid);
2237  g_free (txt);
2238 
2239  g_return_val_if_fail (ok, FALSE);
2240 
2241  if (xaccTransLookup (&gid, pstatus->book))
2242  {
2243  return (FALSE);
2244  }
2245 
2246  xaccTransSetGUID (t, &gid);
2247  return (TRUE);
2248 }
2249 
2250 /****************************************************************************/
2251 /* <num> (lineage <restore> <transaction>)
2252 
2253  restores a given transaction's num.
2254 
2255  from parent: Transaction*
2256  for children: NA
2257  result: NA
2258  -----------
2259  start: NA
2260  characters: return string copy for accumulation in end handler.
2261  end: concatenate all chars and set as transaction num.
2262 
2263  cleanup-result: NA
2264  cleanup-chars: g_free the result string.
2265  fail: NA
2266  result-fail: NA
2267  chars-fail: g_free the result string.
2268 
2269  */
2270 
2271 static gboolean
2272 txn_restore_num_end_handler (gpointer data_for_children,
2273  GSList* data_from_children, GSList* sibling_data,
2274  gpointer parent_data, gpointer global_data,
2275  gpointer* result, const gchar* tag)
2276 {
2277  Transaction* t = (Transaction*) parent_data;
2278  gchar* txt = NULL;
2279 
2280  g_return_val_if_fail (t, FALSE);
2281 
2282  txt = concatenate_child_result_chars (data_from_children);
2283  g_return_val_if_fail (txt, FALSE);
2284 
2285  xaccTransSetNum (t, txt);
2286  g_free (txt);
2287  return (TRUE);
2288 }
2289 
2290 /****************************************************************************/
2291 /* <description> (lineage <restore> <transaction>)
2292 
2293  restores a given transaction's description.
2294 
2295  from parent: Transaction*
2296  for children: NA
2297  result: NA
2298  -----------
2299  start: NA
2300  characters: return string copy for accumulation in end handler.
2301  end: concatenate all chars and set as transaction description.
2302 
2303  cleanup-result: NA
2304  cleanup-chars: g_free the result string.
2305  fail: NA
2306  result-fail: NA
2307  chars-fail: g_free the result string.
2308 
2309  */
2310 
2311 static gboolean
2312 txn_restore_description_end_handler (gpointer data_for_children,
2313  GSList* data_from_children, GSList* sibling_data,
2314  gpointer parent_data, gpointer global_data,
2315  gpointer* result, const gchar* tag)
2316 {
2317  Transaction* t = (Transaction*) parent_data;
2318  gchar* txt = NULL;
2319 
2320  g_return_val_if_fail (t, FALSE);
2321 
2322  txt = concatenate_child_result_chars (data_from_children);
2323  g_return_val_if_fail (txt, FALSE);
2324 
2325  xaccTransSetDescription (t, txt);
2326  g_free (txt);
2327  return (TRUE);
2328 }
2329 
2330 /****************************************************************************/
2331 /* <date-posted> (lineage <restore> <transaction>)
2332 
2333  restores a given transaction's posted date.
2334 
2335  Just uses a generic_timespec parser, but with our own end handler.
2336 
2337  end: set date posted.
2338 
2339  */
2340 
2341 static gboolean
2342 txn_rest_date_posted_end_handler (gpointer data_for_children,
2343  GSList* data_from_children, GSList* sibling_data,
2344  gpointer parent_data, gpointer global_data,
2345  gpointer* result, const gchar* tag)
2346 {
2347  Transaction* t = (Transaction*) parent_data;
2348  Time64ParseInfo* info = (Time64ParseInfo*) data_for_children;
2349 
2350  g_return_val_if_fail (info, FALSE);
2351  if (!t || !timespec_parse_ok (info))
2352  {
2353  g_free (info);
2354  return (FALSE);
2355  }
2356 
2357  xaccTransSetDatePostedSecs (t, info->time);
2358  g_free (info);
2359  return (TRUE);
2360 }
2361 
2362 /****************************************************************************/
2363 /* <date-entered> (lineage <restore> <transaction>)
2364 
2365  restores a given transaction's entered date.
2366 
2367  Just uses a generic_timespec parser, but with our own end handler.
2368 
2369  end: set date entered.
2370 
2371  */
2372 
2373 static gboolean
2374 txn_rest_date_entered_end_handler (gpointer data_for_children,
2375  GSList* data_from_children, GSList* sibling_data,
2376  gpointer parent_data, gpointer global_data,
2377  gpointer* result, const gchar* tag)
2378 {
2379  Transaction* t = (Transaction*) parent_data;
2380  Time64ParseInfo* info = (Time64ParseInfo*) data_for_children;
2381 
2382  g_return_val_if_fail (info, FALSE);
2383  if (!t || !timespec_parse_ok (info))
2384  {
2385  g_free (info);
2386  return (FALSE);
2387  }
2388 
2389  xaccTransSetDateEnteredSecs (t, info->time);
2390  g_free (info);
2391  return (TRUE);
2392 }
2393 
2394 
2395 
2396 /****************************************************************************/
2397 
2398 /* <split> (lineage <restore> <transaction> <ledger-data>)
2399 
2400  Restores a given split. We allocate the new split in the start
2401  block, the children modify it, and in the end block, we see if the
2402  resultant split is OK, and if so, we add it to the input Transaction*
2403  account group.
2404 
2405  from parent: Transaction*
2406  for children: new Split*
2407  result: NA
2408  -----------
2409  start: create new Split*, and store in data_for_children.
2410  chars: allow and ignore only whitespace.
2411  end: commit split to transaction if appropriate.
2412  cleanup-result: NA
2413  cleanup-chars: NA
2414  fail: delete Transaction* in data_for_children
2415  result-fail: NA
2416  chars-fail: NA
2417 
2418  */
2419 
2420 static gboolean
2421 txn_restore_split_start_handler (GSList* sibling_data, gpointer parent_data,
2422  gpointer global_data,
2423  gpointer* data_for_children, gpointer* result,
2424  const gchar* tag, gchar** attrs)
2425 {
2426  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
2427  Split* s = xaccMallocSplit (pstatus->book);
2428  g_return_val_if_fail (s, FALSE);
2429  *data_for_children = s;
2430  return (TRUE);
2431 }
2432 
2433 static gboolean
2434 txn_restore_split_end_handler (gpointer data_for_children,
2435  GSList* data_from_children, GSList* sibling_data,
2436  gpointer parent_data, gpointer global_data,
2437  gpointer* result, const gchar* tag)
2438 {
2439  Transaction* t = (Transaction*) parent_data;
2440  Split* s = (Split*) data_for_children;
2441 
2442  g_return_val_if_fail (s, FALSE);
2443  if (!t)
2444  {
2445  xaccSplitDestroy (s);
2446  return (FALSE);
2447  }
2448 
2449  if (!xaccSplitGetGUID (s))
2450  {
2451  /* must at least have a GncGUID for a restore */
2452  xaccSplitDestroy (s);
2453  return (FALSE);
2454  }
2455 
2456  xaccTransAppendSplit (t, s);
2457  return (TRUE);
2458 }
2459 
2460 static gboolean
2461 txn_restore_split_after_child_handler (gpointer data_for_children,
2462  GSList* data_from_children,
2463  GSList* sibling_data,
2464  gpointer parent_data,
2465  gpointer global_data,
2466  gpointer* result,
2467  const gchar* tag,
2468  const gchar* child_tag,
2469  sixtp_child_result* child_result)
2470 {
2471  Split* s = (Split*) data_for_children;
2472  g_return_val_if_fail (s, FALSE);
2473  if (!child_result) return (TRUE);
2474  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
2475 
2476  if (strcmp (child_result->tag, "slots") == 0)
2477  {
2478  KvpFrame* f = static_cast<KvpFrame*> (child_result->data);
2479  g_return_val_if_fail (f, FALSE);
2480  if (s->inst.kvp_data) delete s->inst.kvp_data;
2481  s->inst.kvp_data = f;
2482  child_result->should_cleanup = FALSE;
2483  }
2484  else if (strcmp (child_result->tag, "quantity") == 0)
2485  {
2486  gnc_numeric* n = (gnc_numeric*) child_result->data;
2487  g_return_val_if_fail (n, FALSE);
2488  xaccSplitSetAmount (s, *n);
2489  /* let the normal child_result handler clean up n */
2490  }
2491  else if (strcmp (child_result->tag, "value") == 0)
2492  {
2493  gnc_numeric* n = (gnc_numeric*) child_result->data;
2494  g_return_val_if_fail (n, FALSE);
2495  xaccSplitSetValue (s, *n);
2496  /* let the normal child_result handler clean up n */
2497  }
2498 
2499  return (TRUE);
2500 }
2501 
2502 static void
2503 txn_restore_split_fail_handler (gpointer data_for_children,
2504  GSList* data_from_children,
2505  GSList* sibling_data,
2506  gpointer parent_data,
2507  gpointer global_data,
2508  gpointer* result,
2509  const gchar* tag)
2510 {
2511  Split* s = (Split*) data_for_children;
2512  if (s) xaccSplitDestroy (s);
2513 }
2514 
2515 /****************************************************************************/
2516 /* <guid> (lineage <split> <restore> <transaction>)
2517 
2518  restores a given split's guid.
2519 
2520  from parent: Split*
2521  for children: NA
2522  result: NA
2523  -----------
2524  start: NA
2525  characters: return string copy for accumulation in end handler.
2526  end: concatenate all chars and set as split GncGUID if not duplicate.
2527 
2528  cleanup-result: NA
2529  cleanup-chars: g_free the result string.
2530  fail: NA
2531  result-fail: NA
2532  chars-fail: g_free the result string.
2533 
2534  */
2535 
2536 static gboolean
2537 txn_restore_split_guid_end_handler (gpointer data_for_children,
2538  GSList* data_from_children, GSList* sibling_data,
2539  gpointer parent_data, gpointer global_data,
2540  gpointer* result, const gchar* tag)
2541 {
2542  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
2543  Split* s = (Split*) parent_data;
2544  gchar* txt = NULL;
2545  GncGUID gid;
2546  gboolean ok;
2547 
2548  g_return_val_if_fail (s, FALSE);
2549 
2550  txt = concatenate_child_result_chars (data_from_children);
2551  g_return_val_if_fail (txt, FALSE);
2552 
2553  ok = string_to_guid (txt, &gid);
2554  g_free (txt);
2555 
2556  g_return_val_if_fail (ok, FALSE);
2557 
2558  if (xaccSplitLookup (&gid, pstatus->book))
2559  {
2560  return (FALSE);
2561  }
2562 
2563  xaccSplitSetGUID (s, &gid);
2564  return (TRUE);
2565 }
2566 
2567 /****************************************************************************/
2568 /* <memo> (lineage <split> <restore> <transaction>)
2569 
2570  restores a given split's memo.
2571 
2572  from parent: Split*
2573  for children: NA
2574  result: NA
2575  -----------
2576  start: NA
2577  characters: return string copy for accumulation in end handler.
2578  end: concatenate all chars and set as split description.
2579 
2580  cleanup-result: NA
2581  cleanup-chars: g_free the result string.
2582  fail: NA
2583  result-fail: NA
2584  chars-fail: g_free the result string.
2585 
2586  */
2587 
2588 static gboolean
2589 txn_restore_split_memo_end_handler (gpointer data_for_children,
2590  GSList* data_from_children, GSList* sibling_data,
2591  gpointer parent_data, gpointer global_data,
2592  gpointer* result, const gchar* tag)
2593 {
2594  Split* s = (Split*) parent_data;
2595  gchar* txt = NULL;
2596 
2597  g_return_val_if_fail (s, FALSE);
2598 
2599  txt = concatenate_child_result_chars (data_from_children);
2600  g_return_val_if_fail (txt, FALSE);
2601 
2602  xaccSplitSetMemo (s, txt);
2603  g_free (txt);
2604  return (TRUE);
2605 }
2606 
2607 /****************************************************************************/
2608 /* <action> (lineage <split> <restore> <transaction>)
2609 
2610  restores a given split's action.
2611 
2612  from parent: Split*
2613  for children: NA
2614  result: NA
2615  -----------
2616  start: NA
2617  characters: return string copy for accumulation in end handler.
2618  end: concatenate all chars and set as split action.
2619 
2620  cleanup-result: NA
2621  cleanup-chars: g_free the result string.
2622  fail: NA
2623  result-fail: NA
2624  chars-fail: g_free the result string.
2625 
2626  */
2627 
2628 static gboolean
2629 txn_restore_split_action_end_handler (gpointer data_for_children,
2630  GSList* data_from_children, GSList* sibling_data,
2631  gpointer parent_data, gpointer global_data,
2632  gpointer* result, const gchar* tag)
2633 {
2634  Split* s = (Split*) parent_data;
2635  gchar* txt = NULL;
2636 
2637  g_return_val_if_fail (s, FALSE);
2638 
2639  txt = concatenate_child_result_chars (data_from_children);
2640  g_return_val_if_fail (txt, FALSE);
2641 
2642  xaccSplitSetAction (s, txt);
2643  g_free (txt);
2644  return (TRUE);
2645 }
2646 
2647 /****************************************************************************/
2648 /* <reconcile-state> (lineage <split> <restore> <transaction>)
2649 
2650  restores a given split's reconcile-state.
2651 
2652  from parent: Split*
2653  for children: NA
2654  result: NA
2655  -----------
2656  start: NA
2657  characters: return string copy for accumulation in end handler.
2658  end: concatenate all chars and set as split reconcile-state.
2659 
2660  cleanup-result: NA
2661  cleanup-chars: g_free the result string.
2662  fail: NA
2663  result-fail: NA
2664  chars-fail: g_free the result string.
2665 
2666  */
2667 
2668 static gboolean
2669 txn_restore_split_reconcile_state_end_handler (gpointer data_for_children,
2670  GSList* data_from_children, GSList* sibling_data,
2671  gpointer parent_data, gpointer global_data,
2672  gpointer* result, const gchar* tag)
2673 {
2674  Split* s = (Split*) parent_data;
2675  gchar* txt = NULL;
2676 
2677  g_return_val_if_fail (s, FALSE);
2678 
2679  txt = concatenate_child_result_chars (data_from_children);
2680  g_return_val_if_fail (txt, FALSE);
2681 
2682  if (strlen (txt) != 1)
2683  {
2684  g_free (txt);
2685  return (FALSE);
2686  }
2687 
2688  xaccSplitSetReconcile (s, txt[0]);
2689  g_free (txt);
2690  return (TRUE);
2691 }
2692 
2693 /****************************************************************************/
2694 /* <reconcile-date> (lineage <split> <restore> <transaction>)
2695 
2696  restores a given split's reconcile-date.
2697 
2698  Just uses a generic_timespec parser, but with our own end handler.
2699 
2700  end: set reconcile-date.
2701 
2702  */
2703 
2704 static gboolean
2705 txn_restore_split_reconcile_date_end_handler (gpointer data_for_children,
2706  GSList* data_from_children, GSList* sibling_data,
2707  gpointer parent_data, gpointer global_data,
2708  gpointer* result, const gchar* tag)
2709 {
2710  Split* s = (Split*) parent_data;
2711  Time64ParseInfo* info = (Time64ParseInfo*) data_for_children;
2712 
2713  g_return_val_if_fail (info, FALSE);
2714  if (!s || !timespec_parse_ok (info))
2715  {
2716  g_free (info);
2717  return (FALSE);
2718  }
2719 
2720  xaccSplitSetDateReconciledSecs (s, info->time);
2721  g_free (info);
2722  return (TRUE);
2723 }
2724 
2725 /****************************************************************************/
2726 /* <account> (lineage <split> <restore> <transaction>)
2727 
2728  restores a given split's account.
2729 
2730  from parent: Split*
2731  for children: NA
2732  result: NA
2733  -----------
2734  start: NA
2735  characters: return string copy for accumulation in end handler.
2736  end: concatenate all chars and set as split account if GncGUID OK.
2737 
2738  cleanup-result: NA
2739  cleanup-chars: g_free the result string.
2740  fail: NA
2741  result-fail: NA
2742  chars-fail: g_free the result string.
2743 
2744  */
2745 
2746 static gboolean
2747 txn_restore_split_account_end_handler (gpointer data_for_children,
2748  GSList* data_from_children, GSList* sibling_data,
2749  gpointer parent_data, gpointer global_data,
2750  gpointer* result, const gchar* tag)
2751 {
2752  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
2753  Split* s = (Split*) parent_data;
2754  Account* acct;
2755  gchar* txt = NULL;
2756  GncGUID gid;
2757  gboolean ok;
2758 
2759  g_return_val_if_fail (s, FALSE);
2760 
2761  txt = concatenate_child_result_chars (data_from_children);
2762  g_return_val_if_fail (txt, FALSE);
2763 
2764  ok = string_to_guid (txt, &gid);
2765  g_free (txt);
2766 
2767  g_return_val_if_fail (ok, FALSE);
2768 
2769  acct = xaccAccountLookup (&gid, pstatus->book);
2770  g_return_val_if_fail (acct, FALSE);
2771 
2772  xaccAccountInsertSplit (acct, s);
2773  return (TRUE);
2774 }
2775 
2776 
2777 /****************************************************************************/
2778 
2779 
2780 /****************************************************************************/
2781 
2782 static sixtp*
2783 gnc_txn_restore_split_parser_new (void)
2784 {
2785  sixtp* top_level;
2786 
2787  if (! (top_level =
2788  sixtp_set_any (sixtp_new (), FALSE,
2789  SIXTP_START_HANDLER_ID, txn_restore_split_start_handler,
2790  SIXTP_CHARACTERS_HANDLER_ID,
2791  allow_and_ignore_only_whitespace,
2792  SIXTP_END_HANDLER_ID, txn_restore_split_end_handler,
2793  SIXTP_FAIL_HANDLER_ID, txn_restore_split_fail_handler,
2794  SIXTP_AFTER_CHILD_HANDLER_ID,
2795  txn_restore_split_after_child_handler,
2796  SIXTP_NO_MORE_HANDLERS)))
2797  {
2798  return NULL;
2799  }
2800 
2801  if (!sixtp_add_some_sub_parsers (
2802  top_level, TRUE,
2803  "guid", restore_char_generator (txn_restore_split_guid_end_handler),
2804  "memo", restore_char_generator (txn_restore_split_memo_end_handler),
2805  "action",
2806  restore_char_generator (txn_restore_split_action_end_handler),
2807  "account",
2808  restore_char_generator (txn_restore_split_account_end_handler),
2809  "reconcile-state",
2810  restore_char_generator (txn_restore_split_reconcile_state_end_handler),
2811  "reconcile-date",
2812  generic_timespec_parser_new (
2813  txn_restore_split_reconcile_date_end_handler),
2814  "quantity", generic_gnc_numeric_parser_new (),
2815  "value", generic_gnc_numeric_parser_new (),
2816  "slots", kvp_frame_parser_new (),
2817  NULL, NULL))
2818  {
2819  return NULL;
2820  }
2821 
2822  return (top_level);
2823 }
2824 
2825 /***************************************************************************/
2826 
2827 static sixtp*
2828 gnc_transaction_parser_new (void)
2829 {
2830  sixtp* top_level;
2831  sixtp* restore_pr;
2832 
2833  if (! (top_level =
2834  sixtp_set_any (sixtp_new (), FALSE,
2835  SIXTP_START_HANDLER_ID, transaction_start_handler,
2836  SIXTP_CHARACTERS_HANDLER_ID,
2837  allow_and_ignore_only_whitespace,
2838  SIXTP_AFTER_CHILD_HANDLER_ID,
2839  txn_restore_after_child_handler,
2840  SIXTP_NO_MORE_HANDLERS)))
2841  {
2842  return NULL;
2843  }
2844 
2845  /* <restore> */
2846  if (! (restore_pr =
2847  sixtp_set_any (sixtp_new (), FALSE,
2848  SIXTP_START_HANDLER_ID, txn_restore_start_handler,
2849  SIXTP_END_HANDLER_ID, txn_restore_end_handler,
2850  SIXTP_FAIL_HANDLER_ID, txn_restore_fail_handler,
2851  SIXTP_AFTER_CHILD_HANDLER_ID,
2852  txn_restore_after_child_handler,
2853  SIXTP_NO_MORE_HANDLERS)))
2854  {
2855  sixtp_destroy (top_level);
2856  return (NULL);
2857  }
2858  sixtp_add_sub_parser (top_level, "restore", restore_pr);
2859 
2860  if (! (sixtp_add_some_sub_parsers (
2861  restore_pr, TRUE,
2862  "guid", restore_char_generator (txn_restore_guid_end_handler),
2863  "num", restore_char_generator (txn_restore_num_end_handler),
2864  "description",
2865  restore_char_generator (txn_restore_description_end_handler),
2866  "date-posted",
2867  generic_timespec_parser_new (txn_rest_date_posted_end_handler),
2868  "date-entered",
2869  generic_timespec_parser_new (txn_rest_date_entered_end_handler),
2870  "slots", kvp_frame_parser_new (),
2871  "split", gnc_txn_restore_split_parser_new (),
2872  NULL, NULL)))
2873  {
2874  sixtp_destroy (top_level);
2875  return NULL;
2876  }
2877 
2878  return (top_level);
2879 }
2880 
2881 /****************************************************************************/
2882 /****************************************************************************/
2883 
2884 /* Read and Write the pricedb as XML -- something like this:
2885 
2886  <pricedb>
2887  price-1
2888  price-2
2889  ...
2890  </pricedb>
2891 
2892  where each price should look roughly like this:
2893 
2894  <price>
2895  <price:id>
2896  00000000111111112222222233333333
2897  </price:id>
2898  <price:commodity>
2899  <cmdty:space>NASDAQ</cmdty:space>
2900  <cmdty:id>RHAT</cmdty:id>
2901  </price:commodity>
2902  <price:currency>
2903  <cmdty:space>ISO?</cmdty:space>
2904  <cmdty:id>USD</cmdty:id>
2905  </price:currency>
2906  <price:time><ts:date>Mon ...</ts:date><ts:ns>12</ts:ns></price:time>
2907  <price:source>Finance::Quote</price:source>
2908  <price:type>bid</price:type>
2909  <price:value>11011/100</price:value>
2910  </price>
2911 
2912 */
2913 
2914 /***********************************************************************/
2915 /* READING */
2916 /***********************************************************************/
2917 
2918 /****************************************************************************/
2919 /* <price>
2920 
2921  restores a price. Does so via a walk of the XML tree in memory.
2922  Returns a GNCPrice * in result.
2923 
2924  Right now, a price is legitimate even if all of it's fields are not
2925  set. We may need to change that later, but at the moment.
2926 
2927 */
2928 
2929 static gboolean
2930 price_parse_xml_sub_node (GNCPrice* p, xmlNodePtr sub_node, QofBook* book)
2931 {
2932  if (!p || !sub_node) return FALSE;
2933 
2934  gnc_price_begin_edit (p);
2935 
2936  if (g_strcmp0 ("price:id", (char*)sub_node->name) == 0)
2937  {
2938  GncGUID* c = dom_tree_to_guid (sub_node);
2939  if (!c) return FALSE;
2940  gnc_price_set_guid (p, c);
2941  guid_free (c);
2942  }
2943  else if (g_strcmp0 ("price:commodity", (char*)sub_node->name) == 0)
2944  {
2945  gnc_commodity* c = dom_tree_to_commodity_ref (sub_node, book);
2946  if (!c) return FALSE;
2947  gnc_price_set_commodity (p, c);
2948  }
2949  else if (g_strcmp0 ("price:currency", (char*)sub_node->name) == 0)
2950  {
2951  gnc_commodity* c = dom_tree_to_commodity_ref (sub_node, book);
2952  if (!c) return FALSE;
2953  gnc_price_set_currency (p, c);
2954  }
2955  else if (g_strcmp0 ("price:time", (char*)sub_node->name) == 0)
2956  {
2957  time64 time = dom_tree_to_time64 (sub_node);
2958  if (!dom_tree_valid_time64 (time, sub_node->name)) time = 0;
2959  gnc_price_set_time64 (p, time);
2960  }
2961  else if (g_strcmp0 ("price:source", (char*)sub_node->name) == 0)
2962  {
2963  char* text = dom_tree_to_text (sub_node);
2964  if (!text) return FALSE;
2965  gnc_price_set_source_string (p, text);
2966  g_free (text);
2967  }
2968  else if (g_strcmp0 ("price:type", (char*)sub_node->name) == 0)
2969  {
2970  char* text = dom_tree_to_text (sub_node);
2971  if (!text) return FALSE;
2972  gnc_price_set_typestr (p, text);
2973  g_free (text);
2974  }
2975  else if (g_strcmp0 ("price:value", (char*)sub_node->name) == 0)
2976  {
2977  gnc_price_set_value (p, dom_tree_to_gnc_numeric (sub_node));
2978  }
2979  gnc_price_commit_edit (p);
2980  return TRUE;
2981 }
2982 
2983 static gboolean
2984 price_parse_xml_end_handler (gpointer data_for_children,
2985  GSList* data_from_children,
2986  GSList* sibling_data,
2987  gpointer parent_data,
2988  gpointer global_data,
2989  gpointer* result,
2990  const gchar* tag)
2991 {
2992  gboolean ok = TRUE;
2993  xmlNodePtr price_xml = (xmlNodePtr) data_for_children;
2994  xmlNodePtr child;
2995  GNCPrice* p = NULL;
2996  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
2997 
2998  /* we haven't been handed the *top* level node yet... */
2999  if (parent_data) return TRUE;
3000 
3001  *result = NULL;
3002 
3003  if (!price_xml) return FALSE;
3004  if (price_xml->next)
3005  {
3006  ok = FALSE;
3007  goto cleanup_and_exit;
3008  }
3009  if (price_xml->prev)
3010  {
3011  ok = FALSE;
3012  goto cleanup_and_exit;
3013  }
3014  if (!price_xml->xmlChildrenNode)
3015  {
3016  ok = FALSE;
3017  goto cleanup_and_exit;
3018  }
3019 
3020  p = gnc_price_create (pstatus->book);
3021  if (!p)
3022  {
3023  ok = FALSE;
3024  goto cleanup_and_exit;
3025  }
3026 
3027  for (child = price_xml->xmlChildrenNode; child; child = child->next)
3028  {
3029  switch (child->type)
3030  {
3031  case XML_COMMENT_NODE:
3032  case XML_TEXT_NODE:
3033  break;
3034  case XML_ELEMENT_NODE:
3035  if (!price_parse_xml_sub_node (p, child, pstatus->book))
3036  {
3037  ok = FALSE;
3038  goto cleanup_and_exit;
3039  }
3040  break;
3041  default:
3042  PERR ("Unknown node type (%d) while parsing gnc-price xml.", child->type);
3043  child = NULL;
3044  ok = FALSE;
3045  goto cleanup_and_exit;
3046  break;
3047  }
3048  }
3049 
3050 cleanup_and_exit:
3051  if (ok)
3052  {
3053  *result = p;
3054  }
3055  else
3056  {
3057  *result = NULL;
3058  gnc_price_unref (p);
3059  }
3060  xmlFreeNode (price_xml);
3061  return ok;
3062 }
3063 
3064 static void
3065 cleanup_gnc_price (sixtp_child_result* result)
3066 {
3067  if (result->data) gnc_price_unref ((GNCPrice*) result->data);
3068 }
3069 
3070 static sixtp*
3071 gnc_price_parser_new (void)
3072 {
3073  return sixtp_dom_parser_new (price_parse_xml_end_handler,
3074  cleanup_gnc_price,
3075  cleanup_gnc_price);
3076 }
3077 
3078 
3079 /****************************************************************************/
3080 /* <pricedb> (lineage <ledger-data>)
3081 
3082  restores a pricedb. We allocate the new db in the start block, the
3083  children add to it, and it gets returned in result. Note that the
3084  cleanup handler will destroy the pricedb, so the parent needs to
3085  stop that if desired.
3086 
3087  result: GNCPriceDB*
3088 
3089  start: create new GNCPriceDB*, and leave in *data_for_children.
3090  cleanup-result: destroy GNCPriceDB*
3091  result-fail: destroy GNCPriceDB*
3092 
3093 */
3094 
3095 static gboolean
3096 pricedb_start_handler (GSList* sibling_data,
3097  gpointer parent_data,
3098  gpointer global_data,
3099  gpointer* data_for_children,
3100  gpointer* result,
3101  const gchar* tag,
3102  gchar** attrs)
3103 {
3104  GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
3105  GNCPriceDB* db = gnc_pricedb_get_db (pstatus->book);
3106  g_return_val_if_fail (db, FALSE);
3107  *result = db;
3108  return (TRUE);
3109 }
3110 
3111 static gboolean
3112 pricedb_after_child_handler (gpointer data_for_children,
3113  GSList* data_from_children,
3114  GSList* sibling_data,
3115  gpointer parent_data,
3116  gpointer global_data,
3117  gpointer* result,
3118  const gchar* tag,
3119  const gchar* child_tag,
3120  sixtp_child_result* child_result)
3121 {
3122  GNCPriceDB* db = (GNCPriceDB*) * result;
3123 
3124  g_return_val_if_fail (db, FALSE);
3125 
3126  /* right now children have to produce results :> */
3127  if (!child_result) return (FALSE);
3128  if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (FALSE);
3129 
3130  if (strcmp (child_result->tag, "price") == 0)
3131  {
3132  GNCPrice* p = (GNCPrice*) child_result->data;
3133 
3134  g_return_val_if_fail (p, FALSE);
3135  gnc_pricedb_add_price (db, p);
3136  return TRUE;
3137  }
3138  else
3139  {
3140  return FALSE;
3141  }
3142  return FALSE;
3143 }
3144 
3145 static void
3146 pricedb_cleanup_result_handler (sixtp_child_result* result)
3147 {
3148  if (result->data)
3149  {
3150  GNCPriceDB* db = (GNCPriceDB*) result->data;
3151  if (db) gnc_pricedb_destroy (db);
3152  result->data = NULL;
3153  }
3154 }
3155 
3156 static sixtp*
3157 gnc_pricedb_parser_new (void)
3158 {
3159  sixtp* top_level;
3160  sixtp* price_parser;
3161 
3162  top_level =
3163  sixtp_set_any (sixtp_new (), TRUE,
3164  SIXTP_START_HANDLER_ID, pricedb_start_handler,
3165  SIXTP_AFTER_CHILD_HANDLER_ID, pricedb_after_child_handler,
3166  SIXTP_CHARACTERS_HANDLER_ID,
3167  allow_and_ignore_only_whitespace,
3168  SIXTP_RESULT_FAIL_ID, pricedb_cleanup_result_handler,
3169  SIXTP_CLEANUP_RESULT_ID, pricedb_cleanup_result_handler,
3170  SIXTP_NO_MORE_HANDLERS);
3171 
3172  if (!top_level) return NULL;
3173 
3174  price_parser = gnc_price_parser_new ();
3175 
3176  if (!price_parser)
3177  {
3178  sixtp_destroy (top_level);
3179  return NULL;
3180  }
3181 
3182  sixtp_add_sub_parser (top_level, "price", price_parser);
3183 
3184  return top_level;
3185 }
3186 
3187 /* ======================= END OF FILE ============================== */
void xaccAccountSetType(Account *acc, GNCAccountType tip)
Set the account&#39;s type.
Definition: Account.cpp:2418
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:92
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2877
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
This is the private header for the account structure.
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:381
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
Definition: sixtp.h:129
void xaccSplitSetAction(Split *split, const char *actn)
The Action is an arbitrary user-assigned string.
Definition: Split.cpp:1748
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship, and will then add the account as a child of the new parent.
Definition: Account.cpp:2776
void xaccAccountTreeScrubCommodities(Account *acc)
The xaccAccountTreeScrubCommodities will scrub the currency/commodity of all accounts & transactions ...
Definition: Scrub.cpp:1293
gnc_commodity * DxaccAccountGetCurrency(const Account *acc)
Definition: Account.cpp:3391
void xaccAccountSetNotes(Account *acc, const char *str)
Set the account&#39;s notes.
Definition: Account.cpp:2584
a simple price database for gnucash
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.cpp:1471
STRUCTS.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you&#39;re finished with a price (i.e.
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:120
void xaccLogDisable(void)
document me
Definition: TransLog.cpp:95
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
void xaccAccountSetCode(Account *acc, const char *str)
Set the account&#39;s accounting code.
Definition: Account.cpp:2459
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
void xaccTransSetNum(Transaction *trans, const char *xnum)
Sets the transaction Number (or ID) field; rather than use this function directly, see &#39;gnc_set_num_action&#39; in engine/engine-helpers.c & .h which takes a user-set book option for selecting the source for the num-cell (the transaction-number or the split-action field) in registers/reports into account automatically.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
api for Version 1 XML-based file format
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccAccountDestroy(Account *acc)
The xaccAccountDestroy() routine can be used to get rid of an account.
Definition: Account.cpp:1593
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id...
convert single-entry accounts to clean double-entry
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id, or NULL if there is no such split.
Definition: Split.cpp:1070
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account&#39;s commodity that the split should have...
Definition: gmock-Split.cpp:77
Account handling public routines.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
void gnc_pricedb_destroy(GNCPriceDB *db)
Destroy the given pricedb and unref all of the prices it contains.
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
Create a new commodity.
gboolean xaccAccountStringToType(const char *str, GNCAccountType *type)
Conversion routines for the account types to/from strings that are used in persistent storage...
Definition: Account.cpp:4373
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
#define xaccSplitGetGUID(X)
Definition: Split.h:552
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
#define xaccTransGetGUID(X)
Definition: Transaction.h:788
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition: Account.cpp:3005
API for the transaction logger.
void xaccSplitSetDateReconciledSecs(Split *split, time64 secs)
Set the date on which this split was reconciled by specifying the time as time64. ...
void xaccTransSetDatePostedSecs(Transaction *trans, time64 secs)
The xaccTransSetDatePostedSecs() method will modify the posted date of the transaction, specified by a time64 (see ctime(3)).
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1479
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3413
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1048
gboolean qof_session_load_from_xml_file(QofBook *, const char *filename)
Read in an account group from a file.
gnc_numeric gnc_numeric_from_string(const gchar *str)
Read a gnc_numeric from str, skipping any leading whitespace.
Account * xaccMallocAccount(QofBook *book)
Constructor.
Definition: Account.cpp:1275
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
void xaccAccountSetDescription(Account *acc, const char *str)
Set the account&#39;s description.
Definition: Account.cpp:2478
void DxaccAccountSetCurrency(Account *acc, gnc_commodity *currency)
Definition: Account.cpp:2725
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
Modify the date of when the transaction was entered.
API for Transactions and Splits (journal entries)
The type used to store guids in C.
Definition: guid.h:75
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1520
void xaccAccountSetName(Account *acc, const char *str)
Set the account&#39;s name.
Definition: Account.cpp:2439
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
void xaccLogEnable(void)
document me
Definition: TransLog.cpp:99
gboolean gnc_is_xml_data_file(const gchar *name)
The is_gncxml_file() routine checks to see if the first few chars of the file look like gnc-xml data...
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account&#39;s commodity.
Definition: Account.cpp:2618
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2048