James LewisMoss's refactoring of xml writing.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3579 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas 2001-02-03 00:41:20 +00:00
parent 06b7c43a82
commit 5f5661e125
13 changed files with 1144 additions and 974 deletions

View File

@ -95,7 +95,7 @@ Juan Manuel Garc
Christopher Molnar <molnarc@mandrakesoft.com> build system patch Christopher Molnar <molnarc@mandrakesoft.com> build system patch
Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu> port to alpha-dec-osf4.0f Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu> port to alpha-dec-osf4.0f
G. Allen Morris III <gam3@ann.softgams.com> for QIF core dump G. Allen Morris III <gam3@ann.softgams.com> for QIF core dump
James LewisMoss <dres@phoenixdsl.com> design doc patches James LewisMoss <dres@debian.org> design doc patches
Steven Murdoch <sjmurdoch@linuxfan.com> gnc-prices fix for London exchange Steven Murdoch <sjmurdoch@linuxfan.com> gnc-prices fix for London exchange
Brent Neal <brent@baton.phys.lsu.edu> TIAA-CREF support. Brent Neal <brent@baton.phys.lsu.edu> TIAA-CREF support.
Stefan Nobis <stefan-ml@snobis.de> German translation patch Stefan Nobis <stefan-ml@snobis.de> German translation patch

1
debian/rules vendored
View File

@ -77,7 +77,6 @@ binary-arch: build install
# cp src/.libs/gnucash $(id)/usr/bin/gnucash.debug # cp src/.libs/gnucash $(id)/usr/bin/gnucash.debug
dh_compress dh_compress
dh_fixperms dh_fixperms
dh_suidregister
dh_installdeb dh_installdeb
dh_shlibdeps dh_shlibdeps
dh_gencontrol dh_gencontrol

View File

@ -5,11 +5,14 @@
#include "sixtp.h" #include "sixtp.h"
#include "sixtp-utils.h" #include "sixtp-utils.h"
#include "sixtp-parsers.h" #include "sixtp-parsers.h"
#include "sixtp-xml-write-utils.h"
#include "Account.h" #include "Account.h"
#include "AccountP.h" #include "AccountP.h"
#include "Group.h" #include "Group.h"
#include "sixtp-writers.h"
/****************************************************************************/ /****************************************************************************/
/* <account> (parent <ledger-data>) /* <account> (parent <ledger-data>)
@ -552,3 +555,94 @@ gnc_account_parser_new(void)
return ret; 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);
}

View File

@ -220,11 +220,7 @@ xaccAccountEqual(Account *aa, Account *ab, gboolean check_guids) {
if(!aa) return FALSE; if(!aa) return FALSE;
if(!ab) return FALSE; if(!ab) return FALSE;
if(aa->type != ab->type) { if(aa->type != ab->type) return FALSE;
PERR ("Account types don't match (%d != %d)\n",
aa->type, ab->type);
return FALSE;
}
if(safe_strcmp(aa->accountName, ab->accountName) != 0) return FALSE; if(safe_strcmp(aa->accountName, ab->accountName) != 0) return FALSE;
if(safe_strcmp(aa->accountCode, ab->accountCode) != 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(!gnc_commodity_equiv(aa->security, ab->security)) return FALSE;
if(check_guids) { if(check_guids) {
if(!guid_equal(&aa->guid, &ab->guid)) { if(!guid_equal(&aa->guid, &ab->guid)) return FALSE;
PERR ("Account guids don't match for %s ?= %s\n",
aa->accountName, ab->accountName);
return FALSE;
}
} }
if(kvp_frame_compare(aa->kvp_data, ab->kvp_data) != 0) return FALSE; if(kvp_frame_compare(aa->kvp_data, ab->kvp_data) != 0) return FALSE;

View File

@ -6,9 +6,12 @@
#include "sixtp.h" #include "sixtp.h"
#include "sixtp-utils.h" #include "sixtp-utils.h"
#include "sixtp-parsers.h" #include "sixtp-parsers.h"
#include "sixtp-writers.h"
#include "sixtp-xml-write-utils.h"
#include "gnc-commodity.h" #include "gnc-commodity.h"
#include "gnc-engine.h" #include "gnc-engine.h"
#include "gnc-engine-util.h"
/****************************************************************************/ /****************************************************************************/
/* Commodity restorer. /* Commodity restorer.
@ -358,3 +361,143 @@ generic_gnc_commodity_lookup_parser_new(void)
return(top_level); 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);
}

View File

@ -36,6 +36,7 @@ libgncengine_la_SOURCES = \
sixtp-kvp-parser.c \ sixtp-kvp-parser.c \
sixtp-stack.c \ sixtp-stack.c \
sixtp-utils.c \ sixtp-utils.c \
sixtp-xml-write-utils.c \
sixtp.c \ sixtp.c \
Account-xml-parser-v1.c \ Account-xml-parser-v1.c \
Commodity-xml-parser-v1.c \ Commodity-xml-parser-v1.c \
@ -78,7 +79,9 @@ noinst_HEADERS = \
gnc-event-p.h \ gnc-event-p.h \
gnc-numeric.h \ gnc-numeric.h \
sixtp-parsers.h \ sixtp-parsers.h \
sixtp-writers.h \
sixtp-stack.h \ sixtp-stack.h \
sixtp-xml-write-utils.h \
sixtp.h sixtp.h
EXTRA_DIST = \ EXTRA_DIST = \

View File

@ -5,14 +5,13 @@
#include "sixtp.h" #include "sixtp.h"
#include "sixtp-utils.h" #include "sixtp-utils.h"
#include "sixtp-parsers.h" #include "sixtp-parsers.h"
#include "sixtp-xml-write-utils.h"
#include "gnc-engine-util.h"
#include "Query.h" #include "Query.h"
/****************************************************************************/ static short module = MOD_IO;
/* ================================================================= */
/* ================================================================= */
/* ================================================================= */
/* ================================================================= */
/* <query-server> (parent <gnc-data>) /* <query-server> (parent <gnc-data>)
On failure or on normal cleanup, the query will be killed, On failure or on normal cleanup, the query will be killed,
@ -532,3 +531,161 @@ query_server_parser_new (void)
return(top_level); 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 <query> <restore> */
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);
}

View File

@ -5,6 +5,7 @@
#include "sixtp.h" #include "sixtp.h"
#include "sixtp-utils.h" #include "sixtp-utils.h"
#include "sixtp-parsers.h" #include "sixtp-parsers.h"
#include "sixtp-xml-write-utils.h"
#include "Transaction.h" #include "Transaction.h"
#include "TransactionP.h" #include "TransactionP.h"
@ -834,3 +835,136 @@ gnc_transaction_parser_new(void)
return(top_level); 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));
}

View File

@ -88,6 +88,7 @@
#include "gnc-engine.h" #include "gnc-engine.h"
#include "gnc-engine-util.h" #include "gnc-engine-util.h"
#include "sixtp-writers.h"
#include "io-gncxml.h" #include "io-gncxml.h"
#include "AccountP.h" /* just for kvp_data */ #include "AccountP.h" /* just for kvp_data */
@ -107,962 +108,6 @@ static const gchar *gncxml_emacs_trailer =
"<!-- mode: xml -->\n" "<!-- mode: xml -->\n"
"<!-- End: -->\n"; "<!-- End: -->\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 <query> <restore> */
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 static gboolean
gncxml_append_emacs_trailer(const gchar *filename) gncxml_append_emacs_trailer(const gchar *filename)
{ {

View File

@ -0,0 +1,52 @@
#ifndef _SIXTP_WRITERS_H_
#define _SIXTP_WRITERS_H_
#include <config.h>
#include <glib.h>
#ifdef HAVE_XML_VERSION_HEADER
#include <libxml/xmlversion.h>
#endif
#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include <libxml/parserInternals.h>
#ifndef xmlChildrenNode
#define xmlChildrenNode children
#define xmlRootNode children
#endif
#else
#include <gnome-xml/tree.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/xmlmemory.h>
#include <gnome-xml/parserInternals.h>
#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_ */

View File

@ -0,0 +1,483 @@
#include <config.h>
#define _GNU_SOURCE
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_XML_VERSION_HEADER
#include <libxml/xmlversion.h>
#endif
#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include <libxml/parserInternals.h>
#ifndef xmlChildrenNode
#define xmlChildrenNode children
#define xmlRootNode children
#endif
#else
#include <gnome-xml/tree.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/xmlmemory.h>
#include <gnome-xml/parserInternals.h>
#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);
}

View File

@ -0,0 +1,67 @@
#ifndef _SIXTP_XML_WRITE_UTILS_H_
#define _SIXTP_XML_WRITE_UTILS_H_
#include <config.h>
#include <glib.h>
#ifdef HAVE_XML_VERSION_HEADER
#include <libxml/xmlversion.h>
#endif
#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include <libxml/parserInternals.h>
#ifndef xmlChildrenNode
#define xmlChildrenNode children
#define xmlRootNode children
#endif
#else
#include <gnome-xml/tree.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/xmlmemory.h>
#include <gnome-xml/parserInternals.h>
#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_ */

View File

@ -476,7 +476,8 @@ sixtp_sax_end_handler(void *user_data, const xmlChar *name) {
current_frame = (sixtp_stack_frame *) pdata->stack->data; current_frame = (sixtp_stack_frame *) pdata->stack->data;
parent_frame = (sixtp_stack_frame *) pdata->stack->next->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) { if(safe_strcmp(current_frame->tag, name) != 0) {
pdata->parsing_ok = FALSE; pdata->parsing_ok = FALSE;
return; return;