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