mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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:
parent
06b7c43a82
commit
5f5661e125
2
AUTHORS
2
AUTHORS
@ -95,7 +95,7 @@ Juan Manuel Garc
|
||||
Christopher Molnar <molnarc@mandrakesoft.com> build system patch
|
||||
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
|
||||
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
|
||||
Brent Neal <brent@baton.phys.lsu.edu> TIAA-CREF support.
|
||||
Stefan Nobis <stefan-ml@snobis.de> German translation patch
|
||||
|
1
debian/rules
vendored
1
debian/rules
vendored
@ -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
|
||||
|
@ -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"
|
||||
|
||||
/****************************************************************************/
|
||||
/* <account> (parent <ledger-data>)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 = \
|
||||
|
@ -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;
|
||||
|
||||
/* <query-server> (parent <gnc-data>)
|
||||
|
||||
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 <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);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 =
|
||||
"<!-- mode: xml -->\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
|
||||
gncxml_append_emacs_trailer(const gchar *filename)
|
||||
{
|
||||
|
52
src/engine/sixtp-writers.h
Normal file
52
src/engine/sixtp-writers.h
Normal 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_ */
|
483
src/engine/sixtp-xml-write-utils.c
Normal file
483
src/engine/sixtp-xml-write-utils.c
Normal 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);
|
||||
}
|
67
src/engine/sixtp-xml-write-utils.h
Normal file
67
src/engine/sixtp-xml-write-utils.h
Normal 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_ */
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user