/******************************************************************** * gnc-schedxactions-xml-v2.c -- xml routines for transactions * * Copyright (C) 2001,2007 Joshua Sled * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * * * *******************************************************************/ extern "C" { #include #include #include #include "SX-book.h" } #include "gnc-xml-helper.h" #include "sixtp.h" #include "sixtp-utils.h" #include "sixtp-parsers.h" #include "sixtp-utils.h" #include "sixtp-dom-parsers.h" #include "sixtp-dom-generators.h" #include "gnc-xml.h" #include "io-gncxml-v2.h" #include "io-gncxml-gen.h" #include "sixtp-dom-parsers.h" #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "gnc.backend.file.sx" #define SX_ID "sx:id" #define SX_NAME "sx:name" #define SX_ENABLED "sx:enabled" #define SX_AUTOCREATE "sx:autoCreate" #define SX_AUTOCREATE_NOTIFY "sx:autoCreateNotify" #define SX_ADVANCE_CREATE_DAYS "sx:advanceCreateDays" #define SX_ADVANCE_REMIND_DAYS "sx:advanceRemindDays" #define SX_INSTANCE_COUNT "sx:instanceCount" #define SX_START "sx:start" #define SX_LAST "sx:last" #define SX_NUM_OCCUR "sx:num-occur" #define SX_REM_OCCUR "sx:rem-occur" #define SX_END "sx:end" #define SX_TEMPL_ACCT "sx:templ-acct" #define SX_FREQSPEC "sx:freqspec" #define SX_SCHEDULE "sx:schedule" #define SX_SLOTS "sx:slots" #define SX_DEFER_INSTANCE "sx:deferredInstance" /* * FIXME: These should be defined in a header somewhere */ #define GNC_ACCOUNT_TAG "gnc:account" #define GNC_TRANSACTION_TAG "gnc:transaction" #define GNC_SCHEDXACTION_TAG "gnc:schedxaction" const gchar* schedxaction_version_string = "1.0.0"; const gchar* schedxaction_version2_string = "2.0.0"; xmlNodePtr gnc_schedXaction_dom_tree_create (SchedXaction* sx) { xmlNodePtr ret; const GDate* date; gint instCount; const GncGUID* templ_acc_guid; gboolean allow_2_2_incompat = TRUE; gchar* name = g_strdup (xaccSchedXactionGetName (sx)); templ_acc_guid = xaccAccountGetGUID (sx->template_acct); /* FIXME: this should be the same as the def in io-gncxml-v2.c */ ret = xmlNewNode (NULL, BAD_CAST GNC_SCHEDXACTION_TAG); if (allow_2_2_incompat) xmlSetProp (ret, BAD_CAST "version", BAD_CAST schedxaction_version2_string); else xmlSetProp (ret, BAD_CAST "version", BAD_CAST schedxaction_version_string); xmlAddChild (ret, guid_to_dom_tree (SX_ID, xaccSchedXactionGetGUID (sx))); xmlNewTextChild (ret, NULL, BAD_CAST SX_NAME, checked_char_cast (name)); g_free (name); if (allow_2_2_incompat) { xmlNewTextChild (ret, NULL, BAD_CAST SX_ENABLED, BAD_CAST (sx->enabled ? "y" : "n")); } xmlNewTextChild (ret, NULL, BAD_CAST SX_AUTOCREATE, BAD_CAST (sx->autoCreateOption ? "y" : "n")); xmlNewTextChild (ret, NULL, BAD_CAST SX_AUTOCREATE_NOTIFY, BAD_CAST (sx->autoCreateNotify ? "y" : "n")); xmlAddChild (ret, int_to_dom_tree (SX_ADVANCE_CREATE_DAYS, sx->advanceCreateDays)); xmlAddChild (ret, int_to_dom_tree (SX_ADVANCE_REMIND_DAYS, sx->advanceRemindDays)); instCount = gnc_sx_get_instance_count (sx, NULL); xmlAddChild (ret, int_to_dom_tree (SX_INSTANCE_COUNT, instCount)); xmlAddChild (ret, gdate_to_dom_tree (SX_START, xaccSchedXactionGetStartDate (sx))); date = xaccSchedXactionGetLastOccurDate (sx); if (g_date_valid (date)) { xmlAddChild (ret, gdate_to_dom_tree (SX_LAST, date)); } if (xaccSchedXactionHasOccurDef (sx)) { xmlAddChild (ret, int_to_dom_tree (SX_NUM_OCCUR, xaccSchedXactionGetNumOccur (sx))); xmlAddChild (ret, int_to_dom_tree (SX_REM_OCCUR, xaccSchedXactionGetRemOccur (sx))); } else if (xaccSchedXactionHasEndDate (sx)) { xmlAddChild (ret, gdate_to_dom_tree (SX_END, xaccSchedXactionGetEndDate (sx))); } /* output template account GncGUID */ xmlAddChild (ret, guid_to_dom_tree (SX_TEMPL_ACCT, templ_acc_guid)); if (allow_2_2_incompat) { xmlNodePtr schedule_node = xmlNewNode (NULL, BAD_CAST "sx:schedule"); GList* schedule = gnc_sx_get_schedule (sx); for (; schedule != NULL; schedule = schedule->next) { xmlAddChild (schedule_node, recurrence_to_dom_tree ("gnc:recurrence", (Recurrence*)schedule->data)); } xmlAddChild (ret, schedule_node); } /* Output deferred-instance list. */ { xmlNodePtr instNode; SXTmpStateData* tsd; GList* l; for (l = gnc_sx_get_defer_instances (sx); l; l = l->next) { tsd = (SXTmpStateData*)l->data; instNode = xmlNewNode (NULL, BAD_CAST SX_DEFER_INSTANCE); if (g_date_valid (&tsd->last_date)) { xmlAddChild (instNode, gdate_to_dom_tree (SX_LAST, &tsd->last_date)); } xmlAddChild (instNode, int_to_dom_tree (SX_REM_OCCUR, tsd->num_occur_rem)); xmlAddChild (instNode, int_to_dom_tree (SX_INSTANCE_COUNT, tsd->num_inst)); xmlAddChild (ret, instNode); } } /* xmlAddChild won't do anything with a NULL, so tests are superfluous. */ xmlAddChild (ret, qof_instance_slots_to_dom_tree (SX_SLOTS, QOF_INSTANCE (sx))); return ret; } struct sx_pdata { SchedXaction* sx; QofBook* book; gboolean saw_freqspec; gboolean saw_recurrence; }; static gboolean sx_id_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; GncGUID* tmp = dom_tree_to_guid (node); g_return_val_if_fail (tmp, FALSE); xaccSchedXactionSetGUID (sx, tmp); g_free (tmp); return TRUE; } static gboolean sx_name_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gchar* tmp = dom_tree_to_text (node); g_debug ("sx named [%s]", tmp); g_return_val_if_fail (tmp, FALSE); xaccSchedXactionSetName (sx, tmp); g_free (tmp); return TRUE; } static gboolean sx_enabled_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gchar* tmp = dom_tree_to_text (node); sx->enabled = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE); return TRUE; } static gboolean sx_autoCreate_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gchar* tmp = dom_tree_to_text (node); sx->autoCreateOption = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE); return TRUE; } static gboolean sx_notify_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gchar* tmp = dom_tree_to_text (node); sx->autoCreateNotify = (g_strcmp0 (tmp, "y") == 0 ? TRUE : FALSE); return TRUE; } static gboolean sx_advCreate_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gint64 advCreate; if (! dom_tree_to_integer (node, &advCreate)) { return FALSE; } xaccSchedXactionSetAdvanceCreation (sx, advCreate); return TRUE; } static gboolean sx_advRemind_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gint64 advRemind; if (! dom_tree_to_integer (node, &advRemind)) { return FALSE; } xaccSchedXactionSetAdvanceReminder (sx, advRemind); return TRUE; } static gboolean sx_set_date (xmlNodePtr node, SchedXaction* sx, void (*settor) (SchedXaction* sx, const GDate* d)) { GDate* date; date = dom_tree_to_gdate (node); g_return_val_if_fail (date, FALSE); (*settor) (sx, date); g_date_free (date); return TRUE; } static gboolean sx_instcount_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gint64 instanceNum; if (! dom_tree_to_integer (node, &instanceNum)) { return FALSE; } gnc_sx_set_instance_count (sx, instanceNum); return TRUE; } static gboolean sx_start_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; return sx_set_date (node, sx, xaccSchedXactionSetStartDate); } static gboolean sx_last_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; return sx_set_date (node, sx, xaccSchedXactionSetLastOccurDate); } static gboolean sx_end_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; return sx_set_date (node, sx, xaccSchedXactionSetEndDate); } static void _fixup_recurrence_start_dates (const GDate* sx_start_date, GList* schedule) { GList* iter; for (iter = schedule; iter != NULL; iter = iter->next) { Recurrence* r; GDate start, next; r = (Recurrence*)iter->data; start = *sx_start_date; g_date_subtract_days (&start, 1); g_date_clear (&next, 1); recurrenceNextInstance (r, &start, &next); g_return_if_fail (g_date_valid (&next)); { gchar date_str[128]; gchar* sched_str; g_date_strftime (date_str, 127, "%x", &next); sched_str = recurrenceToString (r); g_debug ("setting recurrence [%s] start date to [%s]", sched_str, date_str); g_free (sched_str); } recurrenceSet (r, recurrenceGetMultiplier (r), recurrenceGetPeriodType (r), &next, recurrenceGetWeekendAdjust (r)); } if (g_list_length (schedule) == 1 && recurrenceGetPeriodType ((Recurrence*)g_list_nth_data (schedule, 0)) == PERIOD_ONCE) { char date_buf[128]; Recurrence* fixup = (Recurrence*)g_list_nth_data (schedule, 0); g_date_strftime (date_buf, 127, "%x", sx_start_date); recurrenceSet (fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE); g_debug ("fixed up period=ONCE Recurrence to date [%s]", date_buf); } } static gboolean sx_freqspec_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; GList* schedule; gchar* debug_str; g_return_val_if_fail (node, FALSE); schedule = dom_tree_freqSpec_to_recurrences (node, pdata->book); gnc_sx_set_schedule (sx, schedule); debug_str = recurrenceListToString (schedule); g_debug ("parsed from freqspec [%s]", debug_str); g_free (debug_str); _fixup_recurrence_start_dates (xaccSchedXactionGetStartDate (sx), schedule); pdata->saw_freqspec = TRUE; return TRUE; } static gboolean sx_schedule_recurrence_handler (xmlNodePtr node, gpointer parsing_data) { GList** schedule = (GList**)parsing_data; gchar* sched_str; Recurrence* r = dom_tree_to_recurrence (node); g_return_val_if_fail (r, FALSE); sched_str = recurrenceToString (r); g_debug ("parsed recurrence [%s]", sched_str); g_free (sched_str); *schedule = g_list_append (*schedule, r); return TRUE; } struct dom_tree_handler sx_recurrence_list_handlers[] = { { "gnc:recurrence", sx_schedule_recurrence_handler, 0, 0 }, { NULL, NULL, 0, 0 } }; static gboolean sx_recurrence_handler (xmlNodePtr node, gpointer _pdata) { struct sx_pdata* parsing_data = static_cast (_pdata); GList* schedule = NULL; gchar* debug_str; g_return_val_if_fail (node, FALSE); if (!dom_tree_generic_parse (node, sx_recurrence_list_handlers, &schedule)) return FALSE; // g_return_val_if_fail(schedule, FALSE); debug_str = recurrenceListToString (schedule); g_debug ("setting freshly-parsed schedule: [%s]", debug_str); g_free (debug_str); gnc_sx_set_schedule (parsing_data->sx, schedule); parsing_data->saw_recurrence = TRUE; return TRUE; } static gboolean sx_defer_last_handler (xmlNodePtr node, gpointer gpTSD) { GDate* gd; SXTmpStateData* tsd = (SXTmpStateData*)gpTSD; g_return_val_if_fail (node, FALSE); gd = dom_tree_to_gdate (node); g_return_val_if_fail (gd, FALSE); tsd->last_date = *gd; g_date_free (gd); return TRUE; } static gboolean sx_defer_rem_occur_handler (xmlNodePtr node, gpointer gpTSD) { gint64 remOccur; SXTmpStateData* tsd = (SXTmpStateData*)gpTSD; g_return_val_if_fail (node, FALSE); if (! dom_tree_to_integer (node, &remOccur)) { return FALSE; } tsd->num_occur_rem = remOccur; return TRUE; } static gboolean sx_defer_inst_count_handler (xmlNodePtr node, gpointer gpTSD) { gint64 instCount; SXTmpStateData* tsd = (SXTmpStateData*)gpTSD; g_return_val_if_fail (node, FALSE); if (! dom_tree_to_integer (node, &instCount)) { return FALSE; } tsd->num_inst = instCount; return TRUE; } struct dom_tree_handler sx_defer_dom_handlers[] = { /* tag name, handler, opt, ? */ { SX_LAST, sx_defer_last_handler, 1, 0 }, { SX_REM_OCCUR, sx_defer_rem_occur_handler, 1, 0 }, { SX_INSTANCE_COUNT, sx_defer_inst_count_handler, 1, 0 }, { NULL, NULL, 0, 0 } }; static gboolean sx_defer_inst_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; SXTmpStateData* tsd; g_return_val_if_fail (node, FALSE); tsd = g_new0 (SXTmpStateData, 1); g_assert (sx_defer_dom_handlers != NULL); if (!dom_tree_generic_parse (node, sx_defer_dom_handlers, tsd)) { xmlElemDump (stdout, NULL, node); g_free (tsd); tsd = NULL; return FALSE; } /* We assume they were serialized in sorted order, here. */ sx->deferredList = g_list_append (sx->deferredList, tsd); return TRUE; } static gboolean sx_numOccur_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gint64 numOccur; if (! dom_tree_to_integer (node, &numOccur)) { return FALSE; } xaccSchedXactionSetNumOccur (sx, numOccur); return TRUE; } static gboolean sx_templ_acct_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; GncGUID* templ_acct_guid = dom_tree_to_guid (node); Account* account; if (!templ_acct_guid) { return FALSE; } account = xaccAccountLookup (templ_acct_guid, pdata->book); sx_set_template_account (sx, account); guid_free (templ_acct_guid); return TRUE; } static gboolean sx_remOccur_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; gint64 remOccur; if (! dom_tree_to_integer (node, &remOccur)) { return FALSE; } xaccSchedXactionSetRemOccur (sx, remOccur); return TRUE; } static gboolean sx_slots_handler (xmlNodePtr node, gpointer sx_pdata) { struct sx_pdata* pdata = static_cast (sx_pdata); SchedXaction* sx = pdata->sx; return dom_tree_create_instance_slots (node, QOF_INSTANCE (sx)); } struct dom_tree_handler sx_dom_handlers[] = { { SX_ID, sx_id_handler, 1, 0 }, { SX_NAME, sx_name_handler, 1, 0 }, { SX_ENABLED, sx_enabled_handler, 0, 0 }, { SX_AUTOCREATE, sx_autoCreate_handler, 1, 0 }, { SX_AUTOCREATE_NOTIFY, sx_notify_handler, 1, 0 }, { SX_ADVANCE_CREATE_DAYS, sx_advCreate_handler, 1, 0 }, { SX_ADVANCE_REMIND_DAYS, sx_advRemind_handler, 1, 0 }, { SX_INSTANCE_COUNT, sx_instcount_handler, 0, 0 }, { SX_START, sx_start_handler, 1, 0 }, { SX_LAST, sx_last_handler, 0, 0 }, { SX_NUM_OCCUR, sx_numOccur_handler, 0, 0 }, { SX_REM_OCCUR, sx_remOccur_handler, 0, 0 }, { SX_END, sx_end_handler, 0, 0 }, { SX_TEMPL_ACCT, sx_templ_acct_handler, 0, 0 }, { SX_FREQSPEC, sx_freqspec_handler, 0, 0 }, { SX_SCHEDULE, sx_recurrence_handler, 0, 0 }, { SX_DEFER_INSTANCE, sx_defer_inst_handler, 0, 0 }, { SX_SLOTS, sx_slots_handler, 0, 0 }, { NULL, NULL, 0, 0 } }; static gboolean gnc_schedXaction_end_handler (gpointer data_for_children, GSList* data_from_children, GSList* sibling_data, gpointer parent_data, gpointer global_data, gpointer* result, const gchar* tag) { SchedXaction* sx; gboolean successful = FALSE; xmlNodePtr tree = (xmlNodePtr)data_for_children; gxpf_data* gdata = (gxpf_data*)global_data; struct sx_pdata sx_pdata; if (parent_data) { return TRUE; } if (!tag) { return TRUE; } g_return_val_if_fail (tree, FALSE); sx = xaccSchedXactionMalloc (static_cast (gdata->bookdata)); memset (&sx_pdata, 0, sizeof (sx_pdata)); sx_pdata.sx = sx; sx_pdata.book = static_cast (gdata->bookdata); g_assert (sx_dom_handlers != NULL); successful = dom_tree_generic_parse (tree, sx_dom_handlers, &sx_pdata); if (!successful) { g_critical ("failed to parse scheduled xaction"); xmlElemDump (stdout, NULL, tree); gnc_sx_begin_edit (sx); xaccSchedXactionDestroy (sx); goto done; } if (tree->properties) { gchar* sx_name = xaccSchedXactionGetName (sx); xmlAttr* attr; for (attr = tree->properties; attr != NULL; attr = attr->next) { xmlChar* attr_value = attr->children->content; g_debug ("sx attribute name[%s] value[%s]", attr->name, attr_value); if (strcmp ((const char*)attr->name, "version") != 0) { g_warning ("unknown sx attribute [%s]", attr->name); continue; } // if version == 1.0.0: ensure freqspec, no recurrence // if version == 2.0.0: ensure recurrence, no freqspec. if (strcmp ((const char*)attr_value, schedxaction_version_string) == 0) { if (!sx_pdata.saw_freqspec) g_critical ("did not see freqspec in version 1 sx [%s]", sx_name); if (sx_pdata.saw_recurrence) g_warning ("saw recurrence in supposedly version 1 sx [%s]", sx_name); } if (strcmp ((const char*)attr_value, schedxaction_version2_string) == 0) { if (sx_pdata.saw_freqspec) g_warning ("saw freqspec in version 2 sx [%s]", sx_name); if (!sx_pdata.saw_recurrence) g_critical ("did not find recurrence in version 2 sx [%s]", sx_name); } } } // generic_callback -> book_callback: insert the SX in the book gdata->cb (tag, gdata->parsedata, sx); /* FIXME: this should be removed somewhere near 1.8 release time. */ if (sx->template_acct == NULL) { Account* ra = NULL; Account* acct = NULL; sixtp_gdv2* sixdata = static_cast (gdata->parsedata); QofBook* book; gchar guidstr[GUID_ENCODING_LENGTH + 1]; book = sixdata->book; /* We're dealing with a pre-200107 rgmerk change re: storing template accounts. */ /* Fix: get account with name of our GncGUID from the template accounts. Make that our template_acct pointer. */ guid_to_string_buff (xaccSchedXactionGetGUID (sx), guidstr); ra = gnc_book_get_template_root (book); if (ra == NULL) { g_warning ("Error getting template root account from being-parsed Book."); xmlFreeNode (tree); return FALSE; } acct = gnc_account_lookup_by_name (ra, guidstr); if (acct == NULL) { g_warning ("no template account with name [%s]", guidstr); xmlFreeNode (tree); return FALSE; } g_debug ("template account name [%s] for SX with GncGUID [%s]", xaccAccountGetName (acct), guidstr); /* FIXME: free existing template account. * HUH????? We only execute this if there isn't * currently an existing template account, don't we? * */ sx->template_acct = acct; } done: xmlFreeNode (tree); return successful; } sixtp* gnc_schedXaction_sixtp_parser_create (void) { return sixtp_dom_parser_new (gnc_schedXaction_end_handler, NULL, NULL); } static gboolean tt_act_handler (xmlNodePtr node, gpointer data) { gnc_template_xaction_data* txd = static_cast (data); Account* acc; gnc_commodity* com; acc = dom_tree_to_account (node, txd->book); if (acc == NULL) { return FALSE; } else { xaccAccountBeginEdit (acc); /* Check for the lack of a commodity [signifying that the pre-7/11/2001-CIT-change SX template Account was parsed [but incorrectly]. */ if (xaccAccountGetCommodity (acc) == NULL) { #if 1 gnc_commodity_table* table; table = gnc_commodity_table_get_table (txd->book); com = gnc_commodity_table_lookup (table, GNC_COMMODITY_NS_TEMPLATE, "template"); #else /* FIXME: This should first look in the table of the book, maybe? The right thing happens [WRT file load/save] if we just _new all the time, but it doesn't seem right. This whole block should go away at some point, but the same concern still applies for SchedXaction.c:xaccSchedXactionInit... */ com = gnc_commodity_new (txd->book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1); #endif xaccAccountSetCommodity (acc, com); } txd->accts = g_list_append (txd->accts, acc); } return TRUE; } static gboolean tt_trn_handler (xmlNodePtr node, gpointer data) { gnc_template_xaction_data* txd = static_cast (data); Transaction* trn; trn = dom_tree_to_transaction (node, txd->book); if (trn == NULL) { return FALSE; } else { txd->transactions = g_list_append (txd->transactions, trn); } return TRUE; } struct dom_tree_handler tt_dom_handlers[] = { { GNC_ACCOUNT_TAG, tt_act_handler, 0, 0 }, { GNC_TRANSACTION_TAG, tt_trn_handler, 0, 0 }, { NULL, NULL, 0, 0 }, }; static gboolean gnc_template_transaction_end_handler (gpointer data_for_children, GSList* data_from_children, GSList* sibling_data, gpointer parent_data, gpointer global_data, gpointer* result, const gchar* tag) { gboolean successful = FALSE; xmlNodePtr tree = static_cast (data_for_children); gxpf_data* gdata = static_cast (global_data); QofBook* book = static_cast (gdata->bookdata); GList* n; gnc_template_xaction_data txd; txd.book = book; txd.accts = NULL; txd.transactions = NULL; /* the DOM tree will have an account tree [the template accounts] and a list of transactions [which will be members of the template account]. we want to parse through the dom trees for each, placing the null-parent account in the book's template-group slot, the others under it, and the transactions as normal. */ if (parent_data) { return TRUE; } if (!tag) { return TRUE; } g_return_val_if_fail (tree, FALSE); successful = dom_tree_generic_parse (tree, tt_dom_handlers, &txd); if (successful) { gdata->cb (tag, gdata->parsedata, &txd); } else { g_warning ("failed to parse template transaction"); xmlElemDump (stdout, NULL, tree); } /* cleanup */ for (n = txd.accts; n; n = n->next) { n->data = NULL; } for (n = txd.transactions; n; n = n->next) { n->data = NULL; } g_list_free (txd.accts); g_list_free (txd.transactions); xmlFreeNode (tree); return successful; } sixtp* gnc_template_transaction_sixtp_parser_create (void) { return sixtp_dom_parser_new (gnc_template_transaction_end_handler, NULL, NULL); }