From 5f5661e1252b6b74dc9be0557f10f2ae1a1a27fd Mon Sep 17 00:00:00 2001 From: Dave Peticolas Date: Sat, 3 Feb 2001 00:41:20 +0000 Subject: [PATCH] James LewisMoss's refactoring of xml writing. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3579 57a11ea4-9604-0410-9ed3-97b8803252fd --- AUTHORS | 2 +- debian/rules | 1 - src/engine/Account-xml-parser-v1.c | 94 +++ src/engine/Account.c | 12 +- src/engine/Commodity-xml-parser-v1.c | 143 ++++ src/engine/Makefile.am | 3 + src/engine/Query-xml-parser-v1.c | 167 ++++- src/engine/Transaction-xml-parser-v1.c | 134 ++++ src/engine/io-gncxml-w.c | 957 +------------------------ src/engine/sixtp-writers.h | 52 ++ src/engine/sixtp-xml-write-utils.c | 483 +++++++++++++ src/engine/sixtp-xml-write-utils.h | 67 ++ src/engine/sixtp.c | 3 +- 13 files changed, 1144 insertions(+), 974 deletions(-) create mode 100644 src/engine/sixtp-writers.h create mode 100644 src/engine/sixtp-xml-write-utils.c create mode 100644 src/engine/sixtp-xml-write-utils.h diff --git a/AUTHORS b/AUTHORS index d222d19315..0659ec9098 100644 --- a/AUTHORS +++ b/AUTHORS @@ -95,7 +95,7 @@ Juan Manuel Garc Christopher Molnar build system patch Tim Mooney port to alpha-dec-osf4.0f G. Allen Morris III for QIF core dump -James LewisMoss design doc patches +James LewisMoss design doc patches Steven Murdoch gnc-prices fix for London exchange Brent Neal TIAA-CREF support. Stefan Nobis German translation patch diff --git a/debian/rules b/debian/rules index a7069a658d..2f09934d66 100644 --- a/debian/rules +++ b/debian/rules @@ -77,7 +77,6 @@ binary-arch: build install # cp src/.libs/gnucash $(id)/usr/bin/gnucash.debug dh_compress dh_fixperms - dh_suidregister dh_installdeb dh_shlibdeps dh_gencontrol diff --git a/src/engine/Account-xml-parser-v1.c b/src/engine/Account-xml-parser-v1.c index 19169e99b4..7f022a8a75 100644 --- a/src/engine/Account-xml-parser-v1.c +++ b/src/engine/Account-xml-parser-v1.c @@ -5,11 +5,14 @@ #include "sixtp.h" #include "sixtp-utils.h" #include "sixtp-parsers.h" +#include "sixtp-xml-write-utils.h" #include "Account.h" #include "AccountP.h" #include "Group.h" +#include "sixtp-writers.h" + /****************************************************************************/ /* (parent ) @@ -552,3 +555,94 @@ gnc_account_parser_new(void) return ret; } + + +/***********************************************************************/ +/***********************************************************************/ +/* write out the xml for each of the fields in an account */ + +static gboolean +xml_add_account_restorer(xmlNodePtr p, Account* a) { + xmlNodePtr acct_xml; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(a, FALSE); + + acct_xml = xmlNewTextChild(p, NULL, "account", NULL); + g_return_val_if_fail(acct_xml, FALSE); + + acct_xml = xmlNewTextChild(acct_xml, NULL, "restore", NULL); + g_return_val_if_fail(acct_xml, FALSE); + + if(!xml_add_str(acct_xml, "name", + xaccAccountGetName(a), FALSE)) + return(FALSE); + if(!xml_add_guid(acct_xml, "guid", + xaccAccountGetGUID(a))) + return(FALSE); + if(!xml_add_str(acct_xml, "type", + xaccAccountTypeEnumAsString(xaccAccountGetType(a)), FALSE)) + return(FALSE); + if(!xml_add_str(acct_xml, "code", + xaccAccountGetCode(a), FALSE)) + return(FALSE); + if(!xml_add_str(acct_xml, "description", + xaccAccountGetDescription(a), FALSE)) + return(FALSE); + /* Notes field is now in kvp table. */ + if(!xml_add_commodity_ref(acct_xml, "currency", xaccAccountGetCurrency(a))) + return(FALSE); + if(!xml_add_commodity_ref(acct_xml, "security", xaccAccountGetSecurity(a))) + return(FALSE); + + if(a->kvp_data) { + if(!xml_add_kvp_frame(acct_xml, "slots", a->kvp_data, FALSE)) + return(FALSE); + } + + { + Account *parent = xaccAccountGetParentAccount(a); + if(parent) { + xmlNodePtr parent_xml = xmlNewTextChild(acct_xml, NULL, "parent", NULL); + g_return_val_if_fail(parent_xml, FALSE); + if(!xml_add_guid(parent_xml, "guid", xaccAccountGetGUID(parent))) + return(FALSE); + } + } + + { + AccountGroup *g = xaccAccountGetChildren(a); + if(g) { + GList *list = xaccGroupGetAccountList (g); + GList *node; + + for (node = list; node; node = node->next) { + Account *current_acc = node->data; + + if(!xml_add_account_restorer(p, current_acc)) + return(FALSE); + } + } + } + return(TRUE); +} + +/* ============================================================== */ +/* loop over all accounts in the group */ + +gboolean +xml_add_account_restorers(xmlNodePtr p, AccountGroup *g) { + GList *list; + GList *node; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(g, FALSE); + + list = xaccGroupGetAccountList (g); + + for (node = list; node; node = node->next) { + Account *current_acc = node->data; + xml_add_account_restorer(p, current_acc); + } + return(TRUE); +} diff --git a/src/engine/Account.c b/src/engine/Account.c index 19177eae25..1c08cbc0cb 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -220,11 +220,7 @@ xaccAccountEqual(Account *aa, Account *ab, gboolean check_guids) { if(!aa) return FALSE; if(!ab) return FALSE; - if(aa->type != ab->type) { - PERR ("Account types don't match (%d != %d)\n", - aa->type, ab->type); - return FALSE; - } + if(aa->type != ab->type) return FALSE; if(safe_strcmp(aa->accountName, ab->accountName) != 0) return FALSE; if(safe_strcmp(aa->accountCode, ab->accountCode) != 0) return FALSE; @@ -233,11 +229,7 @@ xaccAccountEqual(Account *aa, Account *ab, gboolean check_guids) { if(!gnc_commodity_equiv(aa->security, ab->security)) return FALSE; if(check_guids) { - if(!guid_equal(&aa->guid, &ab->guid)) { - PERR ("Account guids don't match for %s ?= %s\n", - aa->accountName, ab->accountName); - return FALSE; - } + if(!guid_equal(&aa->guid, &ab->guid)) return FALSE; } if(kvp_frame_compare(aa->kvp_data, ab->kvp_data) != 0) return FALSE; diff --git a/src/engine/Commodity-xml-parser-v1.c b/src/engine/Commodity-xml-parser-v1.c index 5a904ef068..a744552db7 100644 --- a/src/engine/Commodity-xml-parser-v1.c +++ b/src/engine/Commodity-xml-parser-v1.c @@ -6,9 +6,12 @@ #include "sixtp.h" #include "sixtp-utils.h" #include "sixtp-parsers.h" +#include "sixtp-writers.h" +#include "sixtp-xml-write-utils.h" #include "gnc-commodity.h" #include "gnc-engine.h" +#include "gnc-engine-util.h" /****************************************************************************/ /* Commodity restorer. @@ -358,3 +361,143 @@ generic_gnc_commodity_lookup_parser_new(void) return(top_level); } + +/***********************************************************************/ +/***********************************************************************/ +/* WRITING */ + +gboolean +xml_add_commodity_ref(xmlNodePtr p, const char *tag, const gnc_commodity *c) { + xmlNodePtr c_xml = NULL; + gboolean ok = FALSE; + + if(p && tag) { + if(!c) { + ok = TRUE; + } else { + c_xml= xmlNewTextChild(p, NULL, tag, NULL); + if(c_xml) { + const gchar *namestr = gnc_commodity_get_namespace(c); + if(namestr) { + xmlNodePtr namespace_xml = xmlNewTextChild(c_xml, NULL, "space", namestr); + if(namespace_xml) { + const gchar *idstr = gnc_commodity_get_mnemonic(c); + xmlNodePtr id_xml = xmlNewTextChild(c_xml, NULL, "id", idstr); + if(id_xml) ok = TRUE; + } + } + } + } + } + + if(!ok && c_xml) xmlFreeNode(c_xml); + return(TRUE); +} + +/* ============================================================== */ + +static gboolean +xml_add_commodity_restorer(xmlNodePtr p, gnc_commodity *c) { + xmlNodePtr comm_xml; + xmlNodePtr rst_xml; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(c, FALSE); + + comm_xml = xmlNewTextChild(p, NULL, "commodity", NULL); + g_return_val_if_fail(comm_xml, FALSE); + + rst_xml = xmlNewTextChild(comm_xml, NULL, "restore", NULL); + if(!rst_xml) { + xmlFreeNode(comm_xml); + return(FALSE); + } + + if(!xml_add_str(rst_xml, "space", gnc_commodity_get_namespace(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_str(rst_xml, "id", gnc_commodity_get_mnemonic(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_str(rst_xml, "name", gnc_commodity_get_fullname(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_str(rst_xml, "xcode", gnc_commodity_get_exchange_code(c), FALSE)) { + xmlFreeNode(comm_xml); + return(FALSE); + } + if(!xml_add_gint64(rst_xml, "fraction", gnc_commodity_get_fraction(c))) { + xmlFreeNode(comm_xml); + return(FALSE); + } + + return(TRUE); +} + + +static gint +compare_namespaces(gconstpointer a, gconstpointer b) { + const gchar *sa = (const gchar *) a; + const gchar *sb = (const gchar *) b; + return(safe_strcmp(sa, sb)); +} + +static gint +compare_commodity_ids(gconstpointer a, gconstpointer b) { + const gnc_commodity *ca = (const gnc_commodity *) a; + const gnc_commodity *cb = (const gnc_commodity *) b; + return(safe_strcmp(gnc_commodity_get_mnemonic(ca), + gnc_commodity_get_mnemonic(cb))); +} + +gboolean +xml_add_commodity_restorers(xmlNodePtr p) { + gnc_commodity_table *commodities; + GList *namespaces; + GList *lp; + + g_return_val_if_fail(p, FALSE); + + commodities = gnc_engine_commodities(); + g_return_val_if_fail(commodities, FALSE); + + namespaces = g_list_sort(gnc_commodity_table_get_namespaces(commodities), + compare_namespaces); + + + for(lp = namespaces; lp; lp = lp->next) { + gchar *space; + + if(!lp->data) { + g_list_free (namespaces); + return(FALSE); + } + + space = (gchar *) lp->data; + if(strcmp(GNC_COMMODITY_NS_ISO, space) != 0) { + GList *comms = gnc_commodity_table_get_commodities(commodities, space); + GList *lp2; + + comms = g_list_sort(comms, compare_commodity_ids); + + for(lp2 = comms; lp2; lp2 = lp2->next) { + gnc_commodity *com = (gnc_commodity *) lp2->data; + + if(!xml_add_commodity_restorer(p, com)) { + g_list_free (comms); + g_list_free (namespaces); + return(FALSE); + } + } + + g_list_free (comms); + } + } + + g_list_free (namespaces); + + return(TRUE); +} diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index 6075df6478..edc583ba18 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -36,6 +36,7 @@ libgncengine_la_SOURCES = \ sixtp-kvp-parser.c \ sixtp-stack.c \ sixtp-utils.c \ + sixtp-xml-write-utils.c \ sixtp.c \ Account-xml-parser-v1.c \ Commodity-xml-parser-v1.c \ @@ -78,7 +79,9 @@ noinst_HEADERS = \ gnc-event-p.h \ gnc-numeric.h \ sixtp-parsers.h \ + sixtp-writers.h \ sixtp-stack.h \ + sixtp-xml-write-utils.h \ sixtp.h EXTRA_DIST = \ diff --git a/src/engine/Query-xml-parser-v1.c b/src/engine/Query-xml-parser-v1.c index 9f56c94b6d..0e2d6422b1 100644 --- a/src/engine/Query-xml-parser-v1.c +++ b/src/engine/Query-xml-parser-v1.c @@ -5,14 +5,13 @@ #include "sixtp.h" #include "sixtp-utils.h" #include "sixtp-parsers.h" +#include "sixtp-xml-write-utils.h" +#include "gnc-engine-util.h" #include "Query.h" -/****************************************************************************/ -/* ================================================================= */ -/* ================================================================= */ -/* ================================================================= */ -/* ================================================================= */ +static short module = MOD_IO; + /* (parent ) On failure or on normal cleanup, the query will be killed, @@ -532,3 +531,161 @@ query_server_parser_new (void) return(top_level); } + + +/***********************************************************************/ +/***********************************************************************/ +/* WRITING */ +/* push query terms into xml */ +/* XXX hack alert not all predicates currently implemented */ + +static gboolean +xml_add_qterm_restorer(xmlNodePtr qxml, QueryTerm *qt) +{ + int rc; + xmlNodePtr p = NULL; + + g_return_val_if_fail(qxml, FALSE); + g_return_val_if_fail(qt, FALSE); + + /* we set the predicates names based on the info they record */ + switch (qt->data.base.term_type) { + case PR_ACCOUNT: + p = xmlNewTextChild(qxml, NULL, "account-pred", NULL); + break; + + case PR_ACTION: + p = xmlNewTextChild(qxml, NULL, "action-pred", NULL); + break; + + case PR_AMOUNT: + p = xmlNewTextChild(qxml, NULL, "amount-pred", NULL); + break; + + case PR_BALANCE: + p = xmlNewTextChild(qxml, NULL, "balance-pred", NULL); + break; + + case PR_CLEARED: + p = xmlNewTextChild(qxml, NULL, "cleared-pred", NULL); + break; + + case PR_DATE: + p = xmlNewTextChild(qxml, NULL, "date-pred", NULL); + break; + + case PR_DESC: + p = xmlNewTextChild(qxml, NULL, "description-pred", NULL); + break; + + case PR_MEMO: + p = xmlNewTextChild(qxml, NULL, "memo-pred", NULL); + break; + + case PR_NUM: + p = xmlNewTextChild(qxml, NULL, "num-pred", NULL); + break; + + case PR_PRICE: + p = xmlNewTextChild(qxml, NULL, "price-pred", NULL); + break; + + case PR_SHRS: + p = xmlNewTextChild(qxml, NULL, "shares-pred", NULL); + break; + + case PR_MISC: + PERR ("Misc terms are not transmittable"); + break; + + default: + } + if (!p) return (FALSE); + + rc = xml_add_gint32(p, "sense", qt->data.base.sense); + if (!rc) return(FALSE); + + + /* however, many of the types share a generic structure. */ + switch (qt->data.type) { + case PD_ACCOUNT: + PERR ("account query unimplemented"); + break; + + case PD_AMOUNT: + PERR ("amount query unimplemented"); + break; + + case PD_BALANCE: + PERR ("balance query unimplemented"); + break; + + case PD_CLEARED: + PERR ("cleared query unimplemented"); + break; + + case PD_DATE: + xml_add_gint32(p, "use-start", qt->data.date.use_start); + xml_add_gint32(p, "use-end", qt->data.date.use_end); + if (qt->data.date.use_start) { + xml_add_editable_timespec(p, "start-date", + &(qt->data.date.start), FALSE); + } + if (qt->data.date.use_end) { + xml_add_editable_timespec(p, "end-date", + &(qt->data.date.end), FALSE); + } + break; + + case PD_STRING: + xml_add_gint32(p, "case-sens", qt->data.str.case_sens); + xml_add_gint32(p, "use-regexp", qt->data.str.use_regexp); + xml_add_str(p, "matchstring", qt->data.str.matchstring, TRUE); + break; + + case PD_MISC: + PERR ("Must not happen"); + break; + + default: + } + + return(TRUE); +} + +/* ============================================================== */ +/* loop over all terms in the query */ +/* XXX hack alert -- need to also send max-terms, sort-order, + * and other mis query elements */ + +gboolean +xml_add_query_restorers(xmlNodePtr p, Query *q) +{ + xmlNodePtr qxml, restore_xml, and_xml; + GList *aterms, *oterms; + GList *anode, *onode; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(q, FALSE); + + oterms = xaccQueryGetTerms (q); + + /* write the nested */ + qxml = xmlNewTextChild(p, NULL, "query", NULL); + g_return_val_if_fail(qxml, FALSE); + + restore_xml = xmlNewTextChild(qxml, NULL, "restore", NULL); + g_return_val_if_fail(restore_xml, FALSE); + + for (onode = oterms; onode; onode = onode->next) { + aterms = onode->data; + and_xml = xmlNewTextChild(restore_xml, NULL, "and-terms", NULL); + g_return_val_if_fail(and_xml, FALSE); + + for (anode = aterms; anode; anode = anode->next) { + QueryTerm *qt = anode->data; + xml_add_qterm_restorer(and_xml, qt); + } + } + return(TRUE); +} diff --git a/src/engine/Transaction-xml-parser-v1.c b/src/engine/Transaction-xml-parser-v1.c index ad49281b47..08eb61e492 100644 --- a/src/engine/Transaction-xml-parser-v1.c +++ b/src/engine/Transaction-xml-parser-v1.c @@ -5,6 +5,7 @@ #include "sixtp.h" #include "sixtp-utils.h" #include "sixtp-parsers.h" +#include "sixtp-xml-write-utils.h" #include "Transaction.h" #include "TransactionP.h" @@ -834,3 +835,136 @@ gnc_transaction_parser_new(void) return(top_level); } + +/***********************************************************************/ +/***********************************************************************/ +/* WRITING */ + +static gboolean +xml_add_transaction_split(xmlNodePtr p, Split* s) { + xmlNodePtr split_xml; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(s, FALSE); + + split_xml = xmlNewTextChild(p, NULL, "split", NULL); + g_return_val_if_fail(split_xml, FALSE); + + if(!xml_add_guid(split_xml, "guid", xaccSplitGetGUID(s))) + return(FALSE); + + if(!xml_add_str(split_xml, "memo", xaccSplitGetMemo(s), FALSE)) + return(FALSE); + + if(!xml_add_str(split_xml, "action", xaccSplitGetAction(s), FALSE)) + return(FALSE); + + /* reconcile-state */ + { + char state = xaccSplitGetReconcile(s); + if(!xml_add_character(split_xml, "reconcile-state", state)) + return(FALSE); + } + + { + /* reconcile-date */ + Timespec ts; + xaccSplitGetDateReconciledTS(s, &ts); + if(!xml_add_editable_timespec(split_xml, "reconcile-date", &ts, FALSE)) + return(FALSE); + } + + /* share-amount */ + if(!xml_add_gnc_numeric(split_xml, "value", xaccSplitGetValue(s))) + return(FALSE); + + /* share-price */ + if(!xml_add_gnc_numeric(split_xml, "quantity", xaccSplitGetShareAmount(s))) + return(FALSE); + + /* account */ + { + Account *acct = xaccSplitGetAccount(s); + if(acct) { + if(!xml_add_guid(split_xml, "account", xaccAccountGetGUID(acct))) + return(FALSE); + } + } + + if(s->kvp_data) { + if(!xml_add_kvp_frame(split_xml, "slots", s->kvp_data, FALSE)) + return(FALSE); + } + + return(TRUE); +} + +/* ============================================================== */ + +static gboolean +xml_add_txn_restore(xmlNodePtr p, Transaction* t) { + + xmlNodePtr txn_xml; + xmlNodePtr restore_xml; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(t, FALSE); + + txn_xml = xmlNewTextChild(p, NULL, "transaction", NULL); + g_return_val_if_fail(txn_xml, FALSE); + + restore_xml = xmlNewTextChild(txn_xml, NULL, "restore", NULL); + g_return_val_if_fail(restore_xml, FALSE); + + if(!xml_add_guid(restore_xml, "guid", xaccTransGetGUID(t))) + return(FALSE); + if(!xml_add_str(restore_xml, "num", xaccTransGetNum(t), FALSE)) + return(FALSE); + { + Timespec ts; + xaccTransGetDatePostedTS(t, &ts); + if(!xml_add_editable_timespec(restore_xml, "date-posted", &ts, FALSE)) + return(FALSE); + } + { + Timespec ts; + xaccTransGetDateEnteredTS(t, &ts); + if(!xml_add_editable_timespec(restore_xml, "date-entered", &ts, FALSE)) + return(FALSE); + } + if(!xml_add_str(restore_xml, "description", xaccTransGetDescription(t), FALSE)) + return(FALSE); + + if(t->kvp_data) { + if(!xml_add_kvp_frame(restore_xml, "slots", t->kvp_data, FALSE)) + return(FALSE); + } + + { + guint32 n = 0; + Split *s = xaccTransGetSplit(t, n); + + while(s) { + if(!xml_add_transaction_split(restore_xml, s)) return(FALSE); + n++; + s = xaccTransGetSplit(t, n); + } + } + + return(TRUE); +} + +/* ============================================================== */ + +static gboolean +xml_add_txn_restore_adapter(Transaction *t, gpointer data) { + xmlNodePtr xml_node = (xmlNodePtr) data; + return(xml_add_txn_restore(xml_node, t)); +} + +gboolean +xml_add_txn_and_split_restorers(xmlNodePtr p, AccountGroup *g) { + return(xaccGroupForEachTransaction(g, + xml_add_txn_restore_adapter, + (gpointer) p)); +} diff --git a/src/engine/io-gncxml-w.c b/src/engine/io-gncxml-w.c index 81518e25ad..5e1297a13a 100644 --- a/src/engine/io-gncxml-w.c +++ b/src/engine/io-gncxml-w.c @@ -88,6 +88,7 @@ #include "gnc-engine.h" #include "gnc-engine-util.h" +#include "sixtp-writers.h" #include "io-gncxml.h" #include "AccountP.h" /* just for kvp_data */ @@ -107,962 +108,6 @@ static const gchar *gncxml_emacs_trailer = "\n" "\n"; -/* ============================================================== */ - -static gboolean -xml_add_str(xmlNodePtr p, const char *tag, const char *str, - gboolean include_if_empty) { - xmlNodePtr child; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - if(!str && !include_if_empty) return(TRUE); - if((strlen(str) == 0) && !include_if_empty) return(TRUE); - - child = xmlNewTextChild(p, NULL, tag, str); - g_return_val_if_fail(child, FALSE); - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_character(xmlNodePtr p, const char *tag, const char c) { - char str[2]; - str[0] = c; - str[1] = '\0'; - return(xml_add_str(p, tag, str, FALSE)); -} - -/* ============================================================== */ - -static gboolean -xml_add_gint64(xmlNodePtr p, const char *tag, const gint64 value) { - xmlNodePtr val_xml; - char num_string[22]; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - - g_snprintf(num_string, sizeof (num_string), "%lld", value); - - val_xml = xmlNewTextChild(p, NULL, tag, num_string); - g_return_val_if_fail(val_xml, FALSE); - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_gint32(xmlNodePtr p, const char *tag, const gint32 value) { - xmlNodePtr val_xml; - char num_string[22]; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - - g_snprintf(num_string, sizeof (num_string), "%d", value); - - val_xml = xmlNewTextChild(p, NULL, tag, num_string); - g_return_val_if_fail(val_xml, FALSE); - - return(TRUE); -} - - -/* ============================================================== */ -/* - RLB writes: - We have to use guile because AFAICT, libc, and C in general isn't - smart enough to actually parse it's own output, especially not - portably (big surprise). - - Linas writes: - I don't understand the claim; I'm just going to use - atof or strtod to accomplish this. - - */ - -static gboolean -xml_add_double(xmlNodePtr p, const char *tag, const double value) -{ - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - - -#ifdef USE_GUILE_FOR_DOUBLE_CONVERSION - { - /* FIXME: NOT THREAD SAFE - USES STATIC DATA */ - static SCM number_to_string; - static gboolean ready = FALSE; - const char *numstr; - - if(!ready) { - number_to_string = gh_eval_str("number->string"); - scm_protect_object(number_to_string); - ready = TRUE; - } - - numstr = gh_scm2newstr(gh_call1(number_to_string, gh_double2scm(value)), - NULL); - - if(!numstr) { - return(FALSE); - } else { - xmlNodePtr child = xmlNewTextChild(p, NULL, tag, numstr); - free((void *) numstr); - g_return_val_if_fail(child, FALSE); - } - } - -#else /* don't USE_GUILE_FOR_DOUBLE_CONVERSION */ - { - int len; - char prtbuf[80]; - xmlNodePtr child; - - /* we're just going to use plain-old libc for the double conversion. - * There was some question as to whether libc is accurate enough - * in its printf function for doubles, but I don't understand - * how it couldn't be ... - */ - len = snprintf (prtbuf, 80, "%24.18g", value); - if (80 <=len) return (FALSE); - - child = xmlNewTextChild(p, NULL, tag, prtbuf); - g_return_val_if_fail(child, FALSE); - } - -#endif /* USE_GUILE_FOR_DOUBLE_CONVERSION */ - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_gnc_numeric(xmlNodePtr p, const char *tag, const gnc_numeric n) { - char *numstr; - xmlNodePtr child; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - - /* fprintf(stderr, "WRITE GNUM S: %lld/%lld -> ", n.num, n.denom); */ - - numstr = gnc_numeric_to_string(n); - g_return_val_if_fail(numstr, FALSE); - - /* fprintf(stderr, "%s\n", numstr); */ - - child = xmlNewTextChild(p, NULL, tag, numstr); - g_free(numstr); numstr = FALSE; - g_return_val_if_fail(child, FALSE); - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_guid(xmlNodePtr p, const char *tag, const GUID *guid) { - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - g_return_val_if_fail(guid, FALSE); - - { - const char *guidstr; - xmlNodePtr child; - - if(!guid) { - guidstr = NULL; - } else { - guidstr = guid_to_string(guid); - g_return_val_if_fail(guidstr, FALSE); - } - - child = xmlNewTextChild(p, NULL, tag, guidstr); - g_return_val_if_fail(child, FALSE); - if(guidstr) free((void *) guidstr); - } - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_editable_timespec(xmlNodePtr p, - const char *tag, - const Timespec *ts, - gboolean include_if_zero) { - xmlNodePtr timespec_xml; - xmlNodePtr secs_xml; - size_t num_written; - struct tm parsed_time; - time_t tmp_timet; - char secs_str[512]; /* This should be way bigger than we need. - Still, it's bogus, we ought to have - astrftime... */ - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - g_return_val_if_fail(ts, FALSE); - if(!include_if_zero && (ts->tv_sec == 0) && (ts->tv_nsec == 0)) return TRUE; - - tmp_timet = ts->tv_sec; - if(!localtime_r(&tmp_timet, &parsed_time)) return(FALSE); - - num_written = strftime(secs_str, sizeof(secs_str), - "%Y-%m-%d %H:%M:%S %z", - &parsed_time); - if(num_written == 0) return(FALSE); - - timespec_xml= xmlNewTextChild(p, NULL, tag, NULL); - g_return_val_if_fail(timespec_xml, FALSE); - - secs_xml = xmlNewTextChild(timespec_xml, NULL, "s", secs_str); - g_return_val_if_fail(secs_xml, FALSE); - - if(ts->tv_nsec) { - xmlNodePtr nsec_xml; - char num_string[22]; - - g_snprintf(num_string, sizeof (num_string), "%ld", ts->tv_nsec); - - nsec_xml = xmlNewTextChild(timespec_xml, NULL, "ns", num_string); - g_return_val_if_fail(nsec_xml, FALSE); - } - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_commodity_ref(xmlNodePtr p, const char *tag, const gnc_commodity *c) { - xmlNodePtr c_xml = NULL; - gboolean ok = FALSE; - - if(p && tag) { - if(!c) { - ok = TRUE; - } else { - c_xml= xmlNewTextChild(p, NULL, tag, NULL); - if(c_xml) { - const gchar *namestr = gnc_commodity_get_namespace(c); - if(namestr) { - xmlNodePtr namespace_xml = xmlNewTextChild(c_xml, NULL, "space", namestr); - if(namespace_xml) { - const gchar *idstr = gnc_commodity_get_mnemonic(c); - xmlNodePtr id_xml = xmlNewTextChild(c_xml, NULL, "id", idstr); - if(id_xml) ok = TRUE; - } - } - } - } - } - - if(!ok && c_xml) xmlFreeNode(c_xml); - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_commodity_restorer(xmlNodePtr p, gnc_commodity *c) { - xmlNodePtr comm_xml; - xmlNodePtr rst_xml; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(c, FALSE); - - comm_xml = xmlNewTextChild(p, NULL, "commodity", NULL); - g_return_val_if_fail(comm_xml, FALSE); - - rst_xml = xmlNewTextChild(comm_xml, NULL, "restore", NULL); - if(!rst_xml) { - xmlFreeNode(comm_xml); - return(FALSE); - } - - if(!xml_add_str(rst_xml, "space", gnc_commodity_get_namespace(c), FALSE)) { - xmlFreeNode(comm_xml); - return(FALSE); - } - if(!xml_add_str(rst_xml, "id", gnc_commodity_get_mnemonic(c), FALSE)) { - xmlFreeNode(comm_xml); - return(FALSE); - } - if(!xml_add_str(rst_xml, "name", gnc_commodity_get_fullname(c), FALSE)) { - xmlFreeNode(comm_xml); - return(FALSE); - } - if(!xml_add_str(rst_xml, "xcode", gnc_commodity_get_exchange_code(c), FALSE)) { - xmlFreeNode(comm_xml); - return(FALSE); - } - if(!xml_add_gint64(rst_xml, "fraction", gnc_commodity_get_fraction(c))) { - xmlFreeNode(comm_xml); - return(FALSE); - } - - return(TRUE); -} - -/* ============================================================== */ - -static gint -compare_namespaces(gconstpointer a, gconstpointer b) { - const gchar *sa = (const gchar *) a; - const gchar *sb = (const gchar *) b; - return(safe_strcmp(sa, sb)); -} - -static gint -compare_commodity_ids(gconstpointer a, gconstpointer b) { - const gnc_commodity *ca = (const gnc_commodity *) a; - const gnc_commodity *cb = (const gnc_commodity *) b; - return(safe_strcmp(gnc_commodity_get_mnemonic(ca), - gnc_commodity_get_mnemonic(cb))); -} - -static gboolean -xml_add_commodity_restorers(xmlNodePtr p) { - gnc_commodity_table *commodities; - GList *namespaces; - GList *lp; - - g_return_val_if_fail(p, FALSE); - - commodities = gnc_engine_commodities(); - g_return_val_if_fail(commodities, FALSE); - - namespaces = g_list_sort(gnc_commodity_table_get_namespaces(commodities), - compare_namespaces); - - - for(lp = namespaces; lp; lp = lp->next) { - gchar *space; - - if(!lp->data) { - g_list_free (namespaces); - return(FALSE); - } - - space = (gchar *) lp->data; - if(strcmp(GNC_COMMODITY_NS_ISO, space) != 0) { - GList *comms = gnc_commodity_table_get_commodities(commodities, space); - GList *lp2; - - comms = g_list_sort(comms, compare_commodity_ids); - - for(lp2 = comms; lp2; lp2 = lp2->next) { - gnc_commodity *com = (gnc_commodity *) lp2->data; - - if(!xml_add_commodity_restorer(p, com)) { - g_list_free (comms); - g_list_free (namespaces); - return(FALSE); - } - } - - g_list_free (comms); - } - } - - g_list_free (namespaces); - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_binary(xmlNodePtr p, - const char *tag, - const gchar *format, - const void *data, - guint32 size) -{ - - xmlNodePtr value_xml; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - g_return_val_if_fail(format, FALSE); - g_return_val_if_fail(data, FALSE); - - value_xml = xmlNewTextChild(p, NULL, tag, NULL); - g_return_val_if_fail(value_xml, FALSE); - - if(size == 0) return(TRUE); - - if(0 == strcmp(format, "hex")) { - /* Write out the chars as hex, buffering them in max 64 character - lines. I was going to use xmlNewTextChild, and xmlTextConcat, - but that doesn't seem to work, and looking at the source, - xmlNewTextChild doesn't set the node type to a type that - xmlTextConcat will recognize and allow. */ - - const guint max_line_len = 64; - xmlNodePtr data_xml = NULL; - GString *output; - guint32 i; - - output = g_string_sized_new(max_line_len + 2); - - for(i = 0; i < size; i++) { - g_string_sprintfa(output, "%x", (int) (((char *) data)[i])); - if(((i + 1) % max_line_len) == 0) { - data_xml = xmlNewTextChild(value_xml, NULL, "hex", output->str); - if(!data_xml) { - return(FALSE); - g_string_free(output, TRUE); - } - g_string_truncate(output, 0); - } - } - - if(strlen(output->str) > 0) { - data_xml = xmlNewTextChild(value_xml, NULL, "hex", output->str); - if(!data_xml) { - g_string_free(output, TRUE); - return(FALSE); - } - } - g_string_free(output, TRUE); - - } else { - PERR("unknown output format %s.\n", format); - return(FALSE); - } - return(TRUE); -} - -/* ============================================================== */ - -static gboolean xml_add_kvp_value(xmlNodePtr p, kvp_value *val); - -static gboolean -xml_add_kvp_glist(xmlNodePtr p, const char *tag, GList *lst) { - xmlNodePtr list_xml; - GList *cursor; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - g_return_val_if_fail(lst, FALSE); - - list_xml = xmlNewTextChild(p, NULL, tag, NULL); - g_return_val_if_fail(list_xml, FALSE); - - for(cursor = lst; cursor; cursor = cursor->next) { - kvp_value * val = (kvp_value *) cursor->data; - if(!xml_add_kvp_value(list_xml, val)) { - return(FALSE); - } - } - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_kvp_frame(xmlNodePtr p, const char *tag, - const kvp_frame *kvpf, - gboolean add_if_empty); - -static gboolean -xml_add_kvp_value(xmlNodePtr p, kvp_value *val) { - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(val, FALSE); - - switch(kvp_value_get_type(val)) { - case KVP_TYPE_GINT64: - return(xml_add_gint64(p, "gint64", kvp_value_get_gint64(val))); - break; - case KVP_TYPE_DOUBLE: - return(xml_add_double(p, "double", kvp_value_get_double(val))); - break; - case KVP_TYPE_NUMERIC: - return(xml_add_gnc_numeric(p, "numeric", kvp_value_get_numeric(val))); - break; - case KVP_TYPE_STRING: - return(xml_add_str(p, "string", kvp_value_get_string(val), TRUE)); - break; - case KVP_TYPE_GUID: - return(xml_add_guid(p, "guid", kvp_value_get_guid(val))); - break; - case KVP_TYPE_BINARY: - { - guint64 size; - void *binary_data = kvp_value_get_binary(val, &size); - g_return_val_if_fail(binary_data, FALSE); - return(xml_add_binary(p, "binary", "hex", binary_data, size)); - } - break; - case KVP_TYPE_GLIST: - return(xml_add_kvp_glist(p, "glist", kvp_value_get_glist(val))); - break; - case KVP_TYPE_FRAME: - return(xml_add_kvp_frame(p, "frame", kvp_value_get_frame(val), TRUE)); - break; - default: - return(FALSE); - break; - }; - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_kvp_slot(xmlNodePtr p, const char *key, kvp_value *val) { - xmlNodePtr slot_xml; - xmlNodePtr key_xml; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(key, FALSE); - g_return_val_if_fail(val, FALSE); - - slot_xml = xmlNewTextChild(p, NULL, "s", NULL); - g_return_val_if_fail(slot_xml, FALSE); - - key_xml = xmlNewTextChild(slot_xml, NULL, "k", key); - g_return_val_if_fail(key_xml, FALSE); - - return(xml_add_kvp_value(slot_xml, val)); -} - -/* ============================================================== */ - -typedef struct { - xmlNodePtr node; - gint64 keycount; -} kvp_value_foreach_info; - -static void -xml_add_kvp_value_foreach_adapter(const char *key, - kvp_value *value, - gpointer data) { - kvp_value_foreach_info *info = (kvp_value_foreach_info *) data; - xml_add_kvp_slot(info->node, key, value); - info->keycount++; -} - -/* ============================================================== */ - -static gboolean -xml_add_kvp_frame(xmlNodePtr p, - const char *tag, - const kvp_frame *kvpf, - gboolean add_if_empty) { - - xmlNodePtr kvp_xml; - kvp_value_foreach_info info; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(tag, FALSE); - g_return_val_if_fail(kvpf, FALSE); - - kvp_xml = xmlNewNode(NULL, tag); - g_return_val_if_fail(kvp_xml, FALSE); - - info.node = kvp_xml; - info.keycount = 0; - kvp_frame_for_each_slot((kvp_frame *) kvpf, - xml_add_kvp_value_foreach_adapter, - &info); - if(add_if_empty || info.keycount) { - xmlAddChild(p, kvp_xml); - } else { - xmlFreeNode(kvp_xml); - } - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_transaction_split(xmlNodePtr p, Split* s) { - xmlNodePtr split_xml; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(s, FALSE); - - split_xml = xmlNewTextChild(p, NULL, "split", NULL); - g_return_val_if_fail(split_xml, FALSE); - - if(!xml_add_guid(split_xml, "guid", xaccSplitGetGUID(s))) - return(FALSE); - - if(!xml_add_str(split_xml, "memo", xaccSplitGetMemo(s), FALSE)) - return(FALSE); - - if(!xml_add_str(split_xml, "action", xaccSplitGetAction(s), FALSE)) - return(FALSE); - - /* reconcile-state */ - { - char state = xaccSplitGetReconcile(s); - if(!xml_add_character(split_xml, "reconcile-state", state)) - return(FALSE); - } - - { - /* reconcile-date */ - Timespec ts; - xaccSplitGetDateReconciledTS(s, &ts); - if(!xml_add_editable_timespec(split_xml, "reconcile-date", &ts, FALSE)) - return(FALSE); - } - - /* share-amount */ - if(!xml_add_gnc_numeric(split_xml, "value", xaccSplitGetValue(s))) - return(FALSE); - - /* share-price */ - if(!xml_add_gnc_numeric(split_xml, "quantity", xaccSplitGetShareAmount(s))) - return(FALSE); - - /* account */ - { - Account *acct = xaccSplitGetAccount(s); - if(acct) { - if(!xml_add_guid(split_xml, "account", xaccAccountGetGUID(acct))) - return(FALSE); - } - } - - if(s->kvp_data) { - if(!xml_add_kvp_frame(split_xml, "slots", s->kvp_data, FALSE)) - return(FALSE); - } - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_txn_restore(xmlNodePtr p, Transaction* t) { - - xmlNodePtr txn_xml; - xmlNodePtr restore_xml; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(t, FALSE); - - txn_xml = xmlNewTextChild(p, NULL, "transaction", NULL); - g_return_val_if_fail(txn_xml, FALSE); - - restore_xml = xmlNewTextChild(txn_xml, NULL, "restore", NULL); - g_return_val_if_fail(restore_xml, FALSE); - - if(!xml_add_guid(restore_xml, "guid", xaccTransGetGUID(t))) - return(FALSE); - if(!xml_add_str(restore_xml, "num", xaccTransGetNum(t), FALSE)) - return(FALSE); - { - Timespec ts; - xaccTransGetDatePostedTS(t, &ts); - if(!xml_add_editable_timespec(restore_xml, "date-posted", &ts, FALSE)) - return(FALSE); - } - { - Timespec ts; - xaccTransGetDateEnteredTS(t, &ts); - if(!xml_add_editable_timespec(restore_xml, "date-entered", &ts, FALSE)) - return(FALSE); - } - if(!xml_add_str(restore_xml, "description", xaccTransGetDescription(t), FALSE)) - return(FALSE); - - if(t->kvp_data) { - if(!xml_add_kvp_frame(restore_xml, "slots", t->kvp_data, FALSE)) - return(FALSE); - } - - { - guint32 n = 0; - Split *s = xaccTransGetSplit(t, n); - - while(s) { - if(!xml_add_transaction_split(restore_xml, s)) return(FALSE); - n++; - s = xaccTransGetSplit(t, n); - } - } - - return(TRUE); -} - -/* ============================================================== */ - -static gboolean -xml_add_txn_restore_adapter(Transaction *t, gpointer data) { - xmlNodePtr xml_node = (xmlNodePtr) data; - return(xml_add_txn_restore(xml_node, t)); -} - -static gboolean -xml_add_txn_and_split_restorers(xmlNodePtr p, AccountGroup *g) { - return(xaccGroupForEachTransaction(g, - xml_add_txn_restore_adapter, - (gpointer) p)); -} - -/* ============================================================== */ -/* write out the xml for each of the fields in an account */ - -static gboolean -xml_add_account_restorer(xmlNodePtr p, Account* a) { - xmlNodePtr acct_xml; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(a, FALSE); - - acct_xml = xmlNewTextChild(p, NULL, "account", NULL); - g_return_val_if_fail(acct_xml, FALSE); - - acct_xml = xmlNewTextChild(acct_xml, NULL, "restore", NULL); - g_return_val_if_fail(acct_xml, FALSE); - - if(!xml_add_str(acct_xml, "name", - xaccAccountGetName(a), FALSE)) - return(FALSE); - if(!xml_add_guid(acct_xml, "guid", - xaccAccountGetGUID(a))) - return(FALSE); - if(!xml_add_str(acct_xml, "type", - xaccAccountTypeEnumAsString(xaccAccountGetType(a)), FALSE)) - return(FALSE); - if(!xml_add_str(acct_xml, "code", - xaccAccountGetCode(a), FALSE)) - return(FALSE); - if(!xml_add_str(acct_xml, "description", - xaccAccountGetDescription(a), FALSE)) - return(FALSE); - /* Notes field is now in kvp table. */ - if(!xml_add_commodity_ref(acct_xml, "currency", xaccAccountGetCurrency(a))) - return(FALSE); - if(!xml_add_commodity_ref(acct_xml, "security", xaccAccountGetSecurity(a))) - return(FALSE); - - if(a->kvp_data) { - if(!xml_add_kvp_frame(acct_xml, "slots", a->kvp_data, FALSE)) - return(FALSE); - } - - { - Account *parent = xaccAccountGetParentAccount(a); - if(parent) { - xmlNodePtr parent_xml = xmlNewTextChild(acct_xml, NULL, "parent", NULL); - g_return_val_if_fail(parent_xml, FALSE); - if(!xml_add_guid(parent_xml, "guid", xaccAccountGetGUID(parent))) - return(FALSE); - } - } - - { - AccountGroup *g = xaccAccountGetChildren(a); - if(g) { - GList *list = xaccGroupGetAccountList (g); - GList *node; - - for (node = list; node; node = node->next) { - Account *current_acc = node->data; - - if(!xml_add_account_restorer(p, current_acc)) - return(FALSE); - } - } - } - return(TRUE); -} - -/* ============================================================== */ -/* loop over all accounts in the group */ - -static gboolean -xml_add_account_restorers(xmlNodePtr p, AccountGroup *g) { - GList *list; - GList *node; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(g, FALSE); - - list = xaccGroupGetAccountList (g); - - for (node = list; node; node = node->next) { - Account *current_acc = node->data; - xml_add_account_restorer(p, current_acc); - } - return(TRUE); -} - -/* ============================================================== */ -/* push query terms into xml */ -/* XXX hack alert not all predicates currently implemented */ - -static gboolean -xml_add_qterm_restorer(xmlNodePtr qxml, QueryTerm *qt) -{ - int rc; - xmlNodePtr p = NULL; - - g_return_val_if_fail(qxml, FALSE); - g_return_val_if_fail(qt, FALSE); - - /* we set the predicates names based on the info they record */ - switch (qt->data.base.term_type) { - case PR_ACCOUNT: - p = xmlNewTextChild(qxml, NULL, "account-pred", NULL); - break; - - case PR_ACTION: - p = xmlNewTextChild(qxml, NULL, "action-pred", NULL); - break; - - case PR_AMOUNT: - p = xmlNewTextChild(qxml, NULL, "amount-pred", NULL); - break; - - case PR_BALANCE: - p = xmlNewTextChild(qxml, NULL, "balance-pred", NULL); - break; - - case PR_CLEARED: - p = xmlNewTextChild(qxml, NULL, "cleared-pred", NULL); - break; - - case PR_DATE: - p = xmlNewTextChild(qxml, NULL, "date-pred", NULL); - break; - - case PR_DESC: - p = xmlNewTextChild(qxml, NULL, "description-pred", NULL); - break; - - case PR_MEMO: - p = xmlNewTextChild(qxml, NULL, "memo-pred", NULL); - break; - - case PR_NUM: - p = xmlNewTextChild(qxml, NULL, "num-pred", NULL); - break; - - case PR_PRICE: - p = xmlNewTextChild(qxml, NULL, "price-pred", NULL); - break; - - case PR_SHRS: - p = xmlNewTextChild(qxml, NULL, "shares-pred", NULL); - break; - - case PR_MISC: - PERR ("Misc terms are not transmittable"); - break; - - default: - } - if (!p) return (FALSE); - - rc = xml_add_gint32(p, "sense", qt->data.base.sense); - if (!rc) return(FALSE); - - - /* however, many of the types share a generic structure. */ - switch (qt->data.type) { - case PD_ACCOUNT: - PERR ("account query unimplemented"); - break; - - case PD_AMOUNT: - PERR ("amount query unimplemented"); - break; - - case PD_BALANCE: - PERR ("balance query unimplemented"); - break; - - case PD_CLEARED: - PERR ("cleared query unimplemented"); - break; - - case PD_DATE: - xml_add_gint32(p, "use-start", qt->data.date.use_start); - xml_add_gint32(p, "use-end", qt->data.date.use_end); - if (qt->data.date.use_start) { - xml_add_editable_timespec(p, "start-date", - &(qt->data.date.start), FALSE); - } - if (qt->data.date.use_end) { - xml_add_editable_timespec(p, "end-date", - &(qt->data.date.end), FALSE); - } - break; - - case PD_STRING: - xml_add_gint32(p, "case-sens", qt->data.str.case_sens); - xml_add_gint32(p, "use-regexp", qt->data.str.use_regexp); - xml_add_str(p, "matchstring", qt->data.str.matchstring, TRUE); - break; - - case PD_MISC: - PERR ("Must not happen"); - break; - - default: - } - - return(TRUE); -} - -/* ============================================================== */ -/* loop over all terms in the query */ -/* XXX hack alert -- need to also send max-terms, sort-order, - * and other mis query elements */ - -static gboolean -xml_add_query_restorers(xmlNodePtr p, Query *q) -{ - xmlNodePtr qxml, restore_xml, and_xml; - GList *aterms, *oterms; - GList *anode, *onode; - - g_return_val_if_fail(p, FALSE); - g_return_val_if_fail(q, FALSE); - - oterms = xaccQueryGetTerms (q); - - /* write the nested */ - qxml = xmlNewTextChild(p, NULL, "query", NULL); - g_return_val_if_fail(qxml, FALSE); - - restore_xml = xmlNewTextChild(qxml, NULL, "restore", NULL); - g_return_val_if_fail(restore_xml, FALSE); - - for (onode = oterms; onode; onode = onode->next) { - aterms = onode->data; - and_xml = xmlNewTextChild(restore_xml, NULL, "and-terms", NULL); - g_return_val_if_fail(and_xml, FALSE); - - for (anode = aterms; anode; anode = anode->next) { - QueryTerm *qt = anode->data; - xml_add_qterm_restorer(and_xml, qt); - } - } - return(TRUE); -} - -/* ============================================================== */ - static gboolean gncxml_append_emacs_trailer(const gchar *filename) { diff --git a/src/engine/sixtp-writers.h b/src/engine/sixtp-writers.h new file mode 100644 index 0000000000..bf42126675 --- /dev/null +++ b/src/engine/sixtp-writers.h @@ -0,0 +1,52 @@ + +#ifndef _SIXTP_WRITERS_H_ +#define _SIXTP_WRITERS_H_ + +#include + +#include + +#ifdef HAVE_XML_VERSION_HEADER +#include +#endif + +#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000 + +#include +#include +#include +#include +#ifndef xmlChildrenNode +#define xmlChildrenNode children +#define xmlRootNode children +#endif + +#else + +#include +#include +#include +#include +#ifndef xmlChildrenNode +#define xmlChildrenNode childs +#define xmlRootNode root +#endif + +#endif + +#include "Query.h" + +gboolean xml_add_account_restorers(xmlNodePtr p, AccountGroup *g); + +gboolean xml_add_commodity_restorers(xmlNodePtr p); + +gboolean xml_add_commodity_ref(xmlNodePtr p, const char *tag, + const gnc_commodity *c); + +gboolean xml_add_query_restorers(xmlNodePtr p, Query *q); + +gboolean xml_add_txn_and_split_restorers(xmlNodePtr p, AccountGroup *g); + + + +#endif /* _SIXTP_WRITERS_H_ */ diff --git a/src/engine/sixtp-xml-write-utils.c b/src/engine/sixtp-xml-write-utils.c new file mode 100644 index 0000000000..967a4e6e9c --- /dev/null +++ b/src/engine/sixtp-xml-write-utils.c @@ -0,0 +1,483 @@ + +#include + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#ifdef HAVE_XML_VERSION_HEADER +#include +#endif + +#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000 + +#include +#include +#include +#include +#ifndef xmlChildrenNode +#define xmlChildrenNode children +#define xmlRootNode children +#endif + +#else + +#include +#include +#include +#include +#ifndef xmlChildrenNode +#define xmlChildrenNode childs +#define xmlRootNode root +#endif + +#endif + +#include "sixtp-xml-write-utils.h" + +#include "gnc-numeric.h" +#include "gnc-engine-util.h" + +static short module = MOD_IO; + +/* ============================================================== */ + +gboolean +xml_add_str(xmlNodePtr p, const char *tag, const char *str, + gboolean include_if_empty) { + xmlNodePtr child; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + if(!str && !include_if_empty) return(TRUE); + if((strlen(str) == 0) && !include_if_empty) return(TRUE); + + child = xmlNewTextChild(p, NULL, tag, str); + g_return_val_if_fail(child, FALSE); + + return(TRUE); +} + +/* ============================================================== */ + +gboolean +xml_add_character(xmlNodePtr p, const char *tag, const char c) { + char str[2]; + str[0] = c; + str[1] = '\0'; + return(xml_add_str(p, tag, str, FALSE)); +} + +/* ============================================================== */ + +gboolean +xml_add_gint64(xmlNodePtr p, const char *tag, const gint64 value) { + xmlNodePtr val_xml; + char num_string[22]; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + + g_snprintf(num_string, sizeof (num_string), "%lld", value); + + val_xml = xmlNewTextChild(p, NULL, tag, num_string); + g_return_val_if_fail(val_xml, FALSE); + + return(TRUE); +} + +/* ============================================================== */ + +gboolean +xml_add_gint32(xmlNodePtr p, const char *tag, const gint32 value) { + xmlNodePtr val_xml; + char num_string[22]; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + + g_snprintf(num_string, sizeof (num_string), "%d", value); + + val_xml = xmlNewTextChild(p, NULL, tag, num_string); + g_return_val_if_fail(val_xml, FALSE); + + return(TRUE); +} + + +/* ============================================================== */ +/* + RLB writes: + We have to use guile because AFAICT, libc, and C in general isn't + smart enough to actually parse it's own output, especially not + portably (big surprise). + + Linas writes: + I don't understand the claim; I'm just going to use + atof or strtod to accomplish this. + + */ + +gboolean +xml_add_double(xmlNodePtr p, const char *tag, const double value) +{ + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + + +#ifdef USE_GUILE_FOR_DOUBLE_CONVERSION + { + /* FIXME: NOT THREAD SAFE - USES STATIC DATA */ + static SCM number_to_string; + static gboolean ready = FALSE; + const char *numstr; + + if(!ready) { + number_to_string = gh_eval_str("number->string"); + scm_protect_object(number_to_string); + ready = TRUE; + } + + numstr = gh_scm2newstr(gh_call1(number_to_string, gh_double2scm(value)), + NULL); + + if(!numstr) { + return(FALSE); + } else { + xmlNodePtr child = xmlNewTextChild(p, NULL, tag, numstr); + free((void *) numstr); + g_return_val_if_fail(child, FALSE); + } + } + +#else /* don't USE_GUILE_FOR_DOUBLE_CONVERSION */ + { + int len; + char prtbuf[80]; + xmlNodePtr child; + + /* we're just going to use plain-old libc for the double conversion. + * There was some question as to whether libc is accurate enough + * in its printf function for doubles, but I don't understand + * how it couldn't be ... + */ + len = snprintf (prtbuf, 80, "%24.18g", value); + if (80 <=len) return (FALSE); + + child = xmlNewTextChild(p, NULL, tag, prtbuf); + g_return_val_if_fail(child, FALSE); + } + +#endif /* USE_GUILE_FOR_DOUBLE_CONVERSION */ + + return(TRUE); +} + +/* ============================================================== */ + +gboolean +xml_add_gnc_numeric(xmlNodePtr p, const char *tag, const gnc_numeric n) { + char *numstr; + xmlNodePtr child; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + + /* fprintf(stderr, "WRITE GNUM S: %lld/%lld -> ", n.num, n.denom); */ + + numstr = gnc_numeric_to_string(n); + g_return_val_if_fail(numstr, FALSE); + + /* fprintf(stderr, "%s\n", numstr); */ + + child = xmlNewTextChild(p, NULL, tag, numstr); + g_free(numstr); numstr = FALSE; + g_return_val_if_fail(child, FALSE); + + return(TRUE); +} + +/* ============================================================== */ + +gboolean +xml_add_guid(xmlNodePtr p, const char *tag, const GUID *guid) { + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + g_return_val_if_fail(guid, FALSE); + + { + const char *guidstr; + xmlNodePtr child; + + if(!guid) { + guidstr = NULL; + } else { + guidstr = guid_to_string(guid); + g_return_val_if_fail(guidstr, FALSE); + } + + child = xmlNewTextChild(p, NULL, tag, guidstr); + g_return_val_if_fail(child, FALSE); + if(guidstr) free((void *) guidstr); + } + return(TRUE); +} + +/* ============================================================== */ + +gboolean +xml_add_editable_timespec(xmlNodePtr p, + const char *tag, + const Timespec *ts, + gboolean include_if_zero) { + xmlNodePtr timespec_xml; + xmlNodePtr secs_xml; + size_t num_written; + struct tm parsed_time; + time_t tmp_timet; + char secs_str[512]; /* This should be way bigger than we need. + Still, it's bogus, we ought to have + astrftime... */ + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + g_return_val_if_fail(ts, FALSE); + if(!include_if_zero && (ts->tv_sec == 0) && (ts->tv_nsec == 0)) return TRUE; + + tmp_timet = ts->tv_sec; + if(!localtime_r(&tmp_timet, &parsed_time)) return(FALSE); + + num_written = strftime(secs_str, sizeof(secs_str), + "%Y-%m-%d %H:%M:%S %z", + &parsed_time); + if(num_written == 0) return(FALSE); + + timespec_xml= xmlNewTextChild(p, NULL, tag, NULL); + g_return_val_if_fail(timespec_xml, FALSE); + + secs_xml = xmlNewTextChild(timespec_xml, NULL, "s", secs_str); + g_return_val_if_fail(secs_xml, FALSE); + + if(ts->tv_nsec) { + xmlNodePtr nsec_xml; + char num_string[22]; + + g_snprintf(num_string, sizeof (num_string), "%ld", ts->tv_nsec); + + nsec_xml = xmlNewTextChild(timespec_xml, NULL, "ns", num_string); + g_return_val_if_fail(nsec_xml, FALSE); + } + + return(TRUE); +} + + +static gboolean +xml_add_binary(xmlNodePtr p, + const char *tag, + const gchar *format, + const void *data, + guint32 size) +{ + + xmlNodePtr value_xml; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + g_return_val_if_fail(format, FALSE); + g_return_val_if_fail(data, FALSE); + + value_xml = xmlNewTextChild(p, NULL, tag, NULL); + g_return_val_if_fail(value_xml, FALSE); + + if(size == 0) return(TRUE); + + if(0 == strcmp(format, "hex")) { + /* Write out the chars as hex, buffering them in max 64 character + lines. I was going to use xmlNewTextChild, and xmlTextConcat, + but that doesn't seem to work, and looking at the source, + xmlNewTextChild doesn't set the node type to a type that + xmlTextConcat will recognize and allow. */ + + const guint max_line_len = 64; + xmlNodePtr data_xml = NULL; + GString *output; + guint32 i; + + output = g_string_sized_new(max_line_len + 2); + + for(i = 0; i < size; i++) { + g_string_sprintfa(output, "%x", (int) (((char *) data)[i])); + if(((i + 1) % max_line_len) == 0) { + data_xml = xmlNewTextChild(value_xml, NULL, "hex", output->str); + if(!data_xml) { + return(FALSE); + g_string_free(output, TRUE); + } + g_string_truncate(output, 0); + } + } + + if(strlen(output->str) > 0) { + data_xml = xmlNewTextChild(value_xml, NULL, "hex", output->str); + if(!data_xml) { + g_string_free(output, TRUE); + return(FALSE); + } + } + g_string_free(output, TRUE); + + } else { + PERR("unknown output format %s.\n", format); + return(FALSE); + } + return(TRUE); +} + +/* ============================================================== */ + +static gboolean xml_add_kvp_value(xmlNodePtr p, kvp_value *val); + +static gboolean +xml_add_kvp_glist(xmlNodePtr p, const char *tag, GList *lst) { + xmlNodePtr list_xml; + GList *cursor; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + g_return_val_if_fail(lst, FALSE); + + list_xml = xmlNewTextChild(p, NULL, tag, NULL); + g_return_val_if_fail(list_xml, FALSE); + + for(cursor = lst; cursor; cursor = cursor->next) { + kvp_value * val = (kvp_value *) cursor->data; + if(!xml_add_kvp_value(list_xml, val)) { + return(FALSE); + } + } + return(TRUE); +} + +/* ============================================================== */ + +gboolean +xml_add_kvp_frame(xmlNodePtr p, const char *tag, + const kvp_frame *kvpf, + gboolean add_if_empty); + +static gboolean +xml_add_kvp_value(xmlNodePtr p, kvp_value *val) { + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(val, FALSE); + + switch(kvp_value_get_type(val)) { + case KVP_TYPE_GINT64: + return(xml_add_gint64(p, "gint64", kvp_value_get_gint64(val))); + break; + case KVP_TYPE_DOUBLE: + return(xml_add_double(p, "double", kvp_value_get_double(val))); + break; + case KVP_TYPE_NUMERIC: + return(xml_add_gnc_numeric(p, "numeric", kvp_value_get_numeric(val))); + break; + case KVP_TYPE_STRING: + return(xml_add_str(p, "string", kvp_value_get_string(val), TRUE)); + break; + case KVP_TYPE_GUID: + return(xml_add_guid(p, "guid", kvp_value_get_guid(val))); + break; + case KVP_TYPE_BINARY: + { + guint64 size; + void *binary_data = kvp_value_get_binary(val, &size); + g_return_val_if_fail(binary_data, FALSE); + return(xml_add_binary(p, "binary", "hex", binary_data, size)); + } + break; + case KVP_TYPE_GLIST: + return(xml_add_kvp_glist(p, "glist", kvp_value_get_glist(val))); + break; + case KVP_TYPE_FRAME: + return(xml_add_kvp_frame(p, "frame", kvp_value_get_frame(val), TRUE)); + break; + default: + return(FALSE); + break; + }; + + return(TRUE); +} + +/* ============================================================== */ + +static gboolean +xml_add_kvp_slot(xmlNodePtr p, const char *key, kvp_value *val) { + xmlNodePtr slot_xml; + xmlNodePtr key_xml; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(key, FALSE); + g_return_val_if_fail(val, FALSE); + + slot_xml = xmlNewTextChild(p, NULL, "s", NULL); + g_return_val_if_fail(slot_xml, FALSE); + + key_xml = xmlNewTextChild(slot_xml, NULL, "k", key); + g_return_val_if_fail(key_xml, FALSE); + + return(xml_add_kvp_value(slot_xml, val)); +} + +/* ============================================================== */ + +typedef struct { + xmlNodePtr node; + gint64 keycount; +} kvp_value_foreach_info; + +static void +xml_add_kvp_value_foreach_adapter(const char *key, + kvp_value *value, + gpointer data) { + kvp_value_foreach_info *info = (kvp_value_foreach_info *) data; + xml_add_kvp_slot(info->node, key, value); + info->keycount++; +} + +/* ============================================================== */ + +gboolean +xml_add_kvp_frame(xmlNodePtr p, + const char *tag, + const kvp_frame *kvpf, + gboolean add_if_empty) { + + xmlNodePtr kvp_xml; + kvp_value_foreach_info info; + + g_return_val_if_fail(p, FALSE); + g_return_val_if_fail(tag, FALSE); + g_return_val_if_fail(kvpf, FALSE); + + kvp_xml = xmlNewNode(NULL, tag); + g_return_val_if_fail(kvp_xml, FALSE); + + info.node = kvp_xml; + info.keycount = 0; + kvp_frame_for_each_slot((kvp_frame *) kvpf, + xml_add_kvp_value_foreach_adapter, + &info); + if(add_if_empty || info.keycount) { + xmlAddChild(p, kvp_xml); + } else { + xmlFreeNode(kvp_xml); + } + + return(TRUE); +} diff --git a/src/engine/sixtp-xml-write-utils.h b/src/engine/sixtp-xml-write-utils.h new file mode 100644 index 0000000000..e2391d262c --- /dev/null +++ b/src/engine/sixtp-xml-write-utils.h @@ -0,0 +1,67 @@ + +#ifndef _SIXTP_XML_WRITE_UTILS_H_ +#define _SIXTP_XML_WRITE_UTILS_H_ + +#include + +#include + +#ifdef HAVE_XML_VERSION_HEADER +#include +#endif + +#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000 + +#include +#include +#include +#include +#ifndef xmlChildrenNode +#define xmlChildrenNode children +#define xmlRootNode children +#endif + +#else + +#include +#include +#include +#include +#ifndef xmlChildrenNode +#define xmlChildrenNode childs +#define xmlRootNode root +#endif + +#endif + +#include "gnc-numeric.h" +#include "GNCId.h" +#include "date.h" +#include "kvp_frame.h" + +gboolean xml_add_str(xmlNodePtr p, const char *tag, const char *str, + gboolean include_if_empty); + +gboolean xml_add_character(xmlNodePtr p, const char *tag, const char c); + +gboolean xml_add_gint64(xmlNodePtr p, const char *tag, const gint64 value); + +gboolean xml_add_gint32(xmlNodePtr p, const char *tag, const gint32 value); + +gboolean xml_add_double(xmlNodePtr p, const char *tag, const double value); + +gboolean xml_add_gnc_numeric(xmlNodePtr p, const char *tag, + const gnc_numeric n); + +gboolean xml_add_guid(xmlNodePtr p, const char *tag, const GUID *guid); + +gboolean xml_add_editable_timespec(xmlNodePtr p, const char *tag, + const Timespec *ts, + gboolean include_if_zero); + +gboolean xml_add_kvp_frame(xmlNodePtr p, const char *tag, + const kvp_frame *kvpf, gboolean add_if_empty); + + + +#endif /* _SIXTP_XML_WRITE_UTILS_H_ */ diff --git a/src/engine/sixtp.c b/src/engine/sixtp.c index 20aec93f8f..20c787bfd0 100644 --- a/src/engine/sixtp.c +++ b/src/engine/sixtp.c @@ -476,7 +476,8 @@ sixtp_sax_end_handler(void *user_data, const xmlChar *name) { current_frame = (sixtp_stack_frame *) pdata->stack->data; parent_frame = (sixtp_stack_frame *) pdata->stack->next->data; - /* time to make sure we got the right closing tag */ + /* time to make sure we got the right closing tag. Is this really + necessary? */ if(safe_strcmp(current_frame->tag, name) != 0) { pdata->parsing_ok = FALSE; return;