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