mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-26 02:40:43 -06:00
3189 lines
93 KiB
C++
3189 lines
93 KiB
C++
/********************************************************************
|
|
* io-gncxml-r.c -- read XML-format gnucash data file *
|
|
* Copyright (C) 2000 Gnumatic, Inc. *
|
|
* *
|
|
* Initial code by Rob L. Browning 4Q 2000 *
|
|
* Tuneups by James LewisMoss Dec 2000 *
|
|
* Excessive hacking Linas Vepstas January 2001 *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
* *
|
|
*******************************************************************/
|
|
#include <guid.hpp>
|
|
extern "C"
|
|
{
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <gnc-xml-helper.h>
|
|
#include <Account.h>
|
|
#include <AccountP.h>
|
|
#include <Query.h>
|
|
#include <Scrub.h>
|
|
#include <Transaction.h>
|
|
#include <TransactionP.h>
|
|
#include <TransLog.h>
|
|
#include <gnc-pricedb.h>
|
|
#include <gnc-pricedb-p.h>
|
|
}
|
|
|
|
#include "io-gncxml.h"
|
|
#include "sixtp.h"
|
|
#include "sixtp-dom-parsers.h"
|
|
#include "sixtp-stack.h"
|
|
#include "sixtp-parsers.h"
|
|
#include "sixtp-utils.h"
|
|
|
|
#include <kvp-frame.hpp>
|
|
|
|
/* from Transaction-xml-parser-v1.c */
|
|
static sixtp* gnc_transaction_parser_new (void);
|
|
|
|
/* from Account-xml-parser-v1.c */
|
|
static sixtp* gnc_account_parser_new (void);
|
|
|
|
/* from Ledger-xml-parser-v1.c */
|
|
static sixtp* ledger_data_parser_new (void);
|
|
|
|
/* from Commodity-xml-parser-v1.c */
|
|
static sixtp* commodity_restore_parser_new (void);
|
|
|
|
/* from Commodity-xml-parser-v1.c */
|
|
static sixtp* generic_gnc_commodity_lookup_parser_new (void);
|
|
|
|
/* from Query-xml-parser-v1.c */
|
|
//static sixtp* query_server_parser_new (void);
|
|
|
|
/* from sixtp-kvp-parser.c */
|
|
static sixtp* kvp_frame_parser_new (void);
|
|
|
|
/* from gnc-pricedb-xml-v1.c */
|
|
static sixtp* gnc_pricedb_parser_new (void);
|
|
|
|
|
|
typedef enum
|
|
{
|
|
GNC_PARSE_ERR_NONE,
|
|
GNC_PARSE_ERR_BAD_VERSION,
|
|
} GNCParseErr;
|
|
|
|
typedef struct
|
|
{
|
|
/* have we gotten the file version yet? */
|
|
gboolean seen_version;
|
|
gint64 version;
|
|
|
|
/* top level <gnc-data> parser - we need this so we can set it up
|
|
after we see the file version. */
|
|
sixtp* gnc_parser;
|
|
|
|
/* The book */
|
|
QofBook* book;
|
|
|
|
/* The root account */
|
|
Account* root_account;
|
|
|
|
/* The pricedb */
|
|
GNCPriceDB* pricedb;
|
|
|
|
/* The query */
|
|
// Query *query;
|
|
|
|
GNCParseErr error;
|
|
} GNCParseStatus;
|
|
|
|
|
|
/* result for a characters handler is shared across all character
|
|
handlers for a given node. result for start/end pair is shared as
|
|
well.
|
|
|
|
Cleaning up the results returned by children and characters
|
|
handlers is the user's responsibility.
|
|
|
|
results have to have cleanup pointers for exceptional failures
|
|
|
|
stack frames also have to have cleanup funcs for exceptional
|
|
failures after the start tag handler has been called, but before
|
|
the end tag handler has been called. If the end tag handler is
|
|
called, but returns FALSE, it is expected that the end tag handler
|
|
has taken care of any cleanup itself.
|
|
|
|
result cleanup functions are called for a node's children just
|
|
after the end handler unless should_cleanup has been set to FALSE,
|
|
or unless there's a failure. If there's a failure, then the
|
|
cleanup is left to the failure handler.
|
|
|
|
|
|
*/
|
|
|
|
static QofLogModule log_module = GNC_MOD_IO;
|
|
|
|
/* ================================================================= */
|
|
/* <version> (lineage <gnc>)
|
|
|
|
Fancy and strange - look for an integer version number. If we get
|
|
one, then modify the parent parser to handle the input.
|
|
|
|
this is a simple_chars_only_parser with an end handler that grabs
|
|
the version number and tweaks the parser, if possible.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
gnc_parser_configure_for_input_version (GNCParseStatus* status, gint64 version)
|
|
{
|
|
|
|
status->version = version;
|
|
status->seen_version = TRUE;
|
|
|
|
/* Check for a legal version here. */
|
|
if (version != 1)
|
|
{
|
|
status->error = GNC_PARSE_ERR_BAD_VERSION;
|
|
return (FALSE);
|
|
}
|
|
|
|
/* Now set up the parser based on the version. */
|
|
|
|
/* add <ledger-data> */
|
|
{
|
|
sixtp* ledger_data_pr = ledger_data_parser_new ();
|
|
g_return_val_if_fail (ledger_data_pr, FALSE);
|
|
sixtp_add_sub_parser (status->gnc_parser, "ledger-data", ledger_data_pr);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
gnc_version_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
gint64 version;
|
|
gboolean ok;
|
|
gchar* txt;
|
|
|
|
g_return_val_if_fail (pstatus, FALSE);
|
|
if (pstatus->seen_version) return (FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = string_to_gint64 (txt, &version);
|
|
g_free (txt);
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
if (!gnc_parser_configure_for_input_version (pstatus, version)) return (FALSE);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
gnc_version_parser_new (void)
|
|
{
|
|
return (simple_chars_only_parser_new (gnc_version_end_handler));
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <gnc> (lineage #f)
|
|
|
|
Takes the results from various children and puts them in the
|
|
global_data result structure.
|
|
|
|
from parent: NA
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
before-child: make sure we don't get two ledger-data's (not allowed ATM).
|
|
after-child: if a ledger-data child, parse_data->root_account = *result.
|
|
characters: allow_and_ignore_only_whitespace
|
|
|
|
Similarly, only one query is allowed ...
|
|
end: NA
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: NA
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
gnc_parser_before_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
|
|
const gchar* tag,
|
|
const gchar* child_tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
|
|
g_return_val_if_fail (pstatus, FALSE);
|
|
|
|
if (strcmp (child_tag, "ledger-data") == 0)
|
|
{
|
|
if (pstatus->root_account)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
gnc_parser_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
g_return_val_if_fail (pstatus, FALSE);
|
|
|
|
if (strcmp (child_tag, "ledger-data") == 0)
|
|
{
|
|
g_return_val_if_fail (child_result, FALSE);
|
|
g_return_val_if_fail (child_result->data, FALSE);
|
|
pstatus->root_account = (Account*) child_result->data;
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
gnc_parser_new (void)
|
|
{
|
|
return sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_BEFORE_CHILD_HANDLER_ID, gnc_parser_before_child_handler,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID, gnc_parser_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS);
|
|
}
|
|
|
|
/* ================================================================== */
|
|
|
|
static sixtp*
|
|
gncxml_setup_for_read (GNCParseStatus* global_parse_status)
|
|
{
|
|
|
|
sixtp* top_level_pr;
|
|
sixtp* gnc_pr;
|
|
sixtp* gnc_version_pr;
|
|
|
|
/* top-level: This is just a dummy node. It doesn't do anything.
|
|
For now, the result is communicated through the global_data
|
|
parser. */
|
|
top_level_pr = sixtp_new ();
|
|
g_return_val_if_fail (top_level_pr, FALSE);
|
|
sixtp_set_chars (top_level_pr, allow_and_ignore_only_whitespace);
|
|
|
|
/* <gnc> */
|
|
gnc_pr = gnc_parser_new ();
|
|
if (!gnc_pr)
|
|
{
|
|
sixtp_destroy (top_level_pr);
|
|
return (NULL);
|
|
}
|
|
sixtp_add_sub_parser (top_level_pr, "gnc", gnc_pr);
|
|
|
|
/* <version> */
|
|
gnc_version_pr = gnc_version_parser_new ();
|
|
if (!gnc_version_pr)
|
|
{
|
|
sixtp_destroy (top_level_pr);
|
|
return (NULL);
|
|
}
|
|
sixtp_add_sub_parser (gnc_pr, "version", gnc_version_pr);
|
|
|
|
global_parse_status->seen_version = FALSE;
|
|
global_parse_status->gnc_parser = gnc_pr;
|
|
global_parse_status->root_account = NULL;
|
|
global_parse_status->pricedb = NULL;
|
|
// global_parse_status->query = NULL;
|
|
global_parse_status->error = GNC_PARSE_ERR_NONE;
|
|
|
|
return top_level_pr;
|
|
}
|
|
|
|
/* ================================================================== */
|
|
|
|
gboolean
|
|
qof_session_load_from_xml_file (QofBook* book, const char* filename)
|
|
{
|
|
gboolean parse_ok;
|
|
gpointer parse_result = NULL;
|
|
sixtp* top_level_pr;
|
|
GNCParseStatus global_parse_status;
|
|
Account* root;
|
|
|
|
global_parse_status.book = book;
|
|
g_return_val_if_fail (book, FALSE);
|
|
g_return_val_if_fail (filename, FALSE);
|
|
|
|
xaccDisableDataScrubbing ();
|
|
top_level_pr = gncxml_setup_for_read (&global_parse_status);
|
|
g_return_val_if_fail (top_level_pr, FALSE);
|
|
|
|
parse_ok = sixtp_parse_file (top_level_pr,
|
|
filename,
|
|
NULL,
|
|
&global_parse_status,
|
|
&parse_result);
|
|
|
|
sixtp_destroy (top_level_pr);
|
|
xaccEnableDataScrubbing ();
|
|
|
|
if (parse_ok)
|
|
{
|
|
if (!global_parse_status.root_account) return FALSE;
|
|
|
|
root = global_parse_status.root_account;
|
|
gnc_book_set_root_account (book, root);
|
|
|
|
/* Fix account and transaction commodities */
|
|
xaccAccountTreeScrubCommodities (root);
|
|
|
|
/* Fix split amount/value */
|
|
xaccAccountTreeScrubSplits (root);
|
|
|
|
return (TRUE);
|
|
}
|
|
else
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
/* ================================================================== */
|
|
|
|
gboolean
|
|
gnc_is_xml_data_file (const gchar* filename)
|
|
{
|
|
if ((gnc_is_our_xml_file (filename, NULL)) == GNC_BOOK_XML1_FILE)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* ================================================================== */
|
|
#include "qof.h"
|
|
|
|
/****************************************************************************/
|
|
/* <kvp-frame>
|
|
|
|
A collection of node functions intended to parse a sub-node set
|
|
that looks like this:
|
|
|
|
<kvp-frame>
|
|
<s>
|
|
<k>notes</k>
|
|
<string>foo</string>
|
|
</s>
|
|
<s>
|
|
<k>temp</k>
|
|
<gint64>97</gint64>
|
|
</s>
|
|
</kvp-frame>
|
|
|
|
and return a KvpFrame*. The start handler for the top allocates
|
|
the KvpFrame* and passes it to the children. The <s> blocks add
|
|
slots according to their <k> (key) and value blocks.
|
|
|
|
FIXME: right now this is totally inappropriate for cases where we
|
|
want to read in a set of new values that should "merge" with the
|
|
existing values. This is only appropriate for wholesale
|
|
replacement of the slots.
|
|
|
|
*/
|
|
|
|
/* kvp-frame [value] handlers
|
|
|
|
Handle the possible values. Each value handler is expected to
|
|
parse it's subtree and return an appropriate kvp_value* in its
|
|
result. The <kvp-frame> <slot> handler will then cram it where it
|
|
belongs. */
|
|
|
|
|
|
static void
|
|
kvp_value_result_cleanup (sixtp_child_result* cr)
|
|
{
|
|
KvpValue* v = static_cast<KvpValue*> (cr->data);
|
|
if (v) delete v;
|
|
}
|
|
|
|
static sixtp*
|
|
simple_kvp_value_parser_new (sixtp_end_handler end_handler)
|
|
{
|
|
return sixtp_set_any (sixtp_new (), FALSE,
|
|
SIXTP_CHARACTERS_HANDLER_ID,
|
|
generic_accumulate_chars,
|
|
SIXTP_END_HANDLER_ID, end_handler,
|
|
SIXTP_CLEANUP_RESULT_ID, kvp_value_result_cleanup,
|
|
SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
|
|
SIXTP_RESULT_FAIL_ID, kvp_value_result_cleanup,
|
|
SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
|
|
SIXTP_NO_MORE_HANDLERS);
|
|
}
|
|
|
|
/* <gint64> - gint64 kvp_value parser.
|
|
|
|
input: NA
|
|
returns: gint64 kvp_value
|
|
|
|
start: NA
|
|
chars: generic_accumulate_chars.
|
|
end: convert chars to gint64 kvp_value* if possible and return.
|
|
|
|
cleanup-result: kvp_value_delete.
|
|
cleanup-chars: g_free (for chars)
|
|
fail: NA
|
|
result-fail: kvp_value_delete
|
|
chars-fail: g_free (for chars)
|
|
|
|
*/
|
|
|
|
/* ------------------------------------------------------------ */
|
|
/* generic type copnversion for kvp types */
|
|
#define KVP_CVT_VALUE(TYPE) \
|
|
{ \
|
|
gchar *txt = NULL; \
|
|
TYPE val; \
|
|
KvpValue *kvpv; \
|
|
gboolean ok; \
|
|
\
|
|
txt = concatenate_child_result_chars(data_from_children); \
|
|
g_return_val_if_fail(txt, FALSE); \
|
|
\
|
|
ok = (gboolean) string_to_##TYPE(txt, &val); \
|
|
g_free(txt); \
|
|
g_return_val_if_fail(ok, FALSE); \
|
|
\
|
|
kvpv = new KvpValue{val}; \
|
|
g_return_val_if_fail(kvpv, FALSE); \
|
|
\
|
|
*result = kvpv; \
|
|
return(TRUE); \
|
|
}
|
|
/* ------------------------------------------------------------ */
|
|
|
|
static gboolean
|
|
gint64_kvp_value_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
KVP_CVT_VALUE (gint64);
|
|
}
|
|
|
|
|
|
static sixtp*
|
|
gint64_kvp_value_parser_new (void)
|
|
{
|
|
return (simple_kvp_value_parser_new (gint64_kvp_value_end_handler));
|
|
}
|
|
|
|
static gboolean
|
|
double_kvp_value_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
KVP_CVT_VALUE (double);
|
|
}
|
|
|
|
static sixtp*
|
|
double_kvp_value_parser_new (void)
|
|
{
|
|
return (simple_kvp_value_parser_new (double_kvp_value_end_handler));
|
|
}
|
|
|
|
static gboolean
|
|
gnc_numeric_kvp_value_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
KVP_CVT_VALUE (gnc_numeric);
|
|
}
|
|
|
|
static sixtp*
|
|
gnc_numeric_kvp_value_parser_new (void)
|
|
{
|
|
return (simple_kvp_value_parser_new (gnc_numeric_kvp_value_end_handler));
|
|
}
|
|
|
|
static gboolean
|
|
string_kvp_value_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
gchar* txt = NULL;
|
|
KvpValue* kvpv;
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
kvpv = new KvpValue {g_strdup (txt)};
|
|
g_free (txt);
|
|
g_return_val_if_fail (kvpv, FALSE);
|
|
|
|
*result = kvpv;
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
string_kvp_value_parser_new (void)
|
|
{
|
|
return (simple_kvp_value_parser_new (string_kvp_value_end_handler));
|
|
}
|
|
|
|
/* the guid handler is almost the same as above, but has
|
|
* inconsistent type handling */
|
|
static gboolean
|
|
guid_kvp_value_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
gchar* txt = NULL;
|
|
GncGUID val;
|
|
KvpValue* kvpv;
|
|
gboolean ok;
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = string_to_guid (txt, &val);
|
|
g_free (txt);
|
|
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
kvpv = new KvpValue {guid_copy (&val)};
|
|
g_return_val_if_fail (kvpv, FALSE);
|
|
|
|
*result = kvpv;
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
guid_kvp_value_parser_new (void)
|
|
{
|
|
return (simple_kvp_value_parser_new (guid_kvp_value_end_handler));
|
|
}
|
|
|
|
/*********************************/
|
|
/* glist kvp-value handler
|
|
*/
|
|
|
|
/* <glist> (lineage <s> <kvp-frame>)
|
|
input: NA
|
|
returns: glist kvp_value
|
|
|
|
start: NA
|
|
chars: allow_and_ignore_only_whitespace
|
|
end: convert the child list pointer to a glist kvp_value and return.
|
|
|
|
cleanup-result: kvp_value_delete
|
|
cleanup-chars: NA
|
|
fail: NA
|
|
result-fail: kvp_value_delete
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
|
|
static gboolean
|
|
glist_kvp_value_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GSList* lp;
|
|
GList* result_glist;
|
|
KvpValue* kvp_result;
|
|
|
|
result_glist = NULL;
|
|
for (lp = data_from_children; lp; lp = lp->next)
|
|
{
|
|
sixtp_child_result* cr = (sixtp_child_result*) lp->data;
|
|
KvpValue* kvp = (KvpValue*) cr->data;
|
|
|
|
/* children are in reverse chron order, so this fixes it. */
|
|
result_glist = g_list_prepend (result_glist, kvp);
|
|
cr->should_cleanup = FALSE;
|
|
}
|
|
|
|
kvp_result = new KvpValue {result_glist};
|
|
if (!kvp_result)
|
|
g_list_free_full (result_glist,
|
|
[] (void * data) { delete static_cast<KvpValue*> (data);});
|
|
*result = kvp_result;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* ---------------------------------------------- */
|
|
#define KVP_TOKEN(NAME,TOK) \
|
|
child_pr = NAME##_kvp_value_parser_new(); \
|
|
g_return_val_if_fail(child_pr, FALSE); \
|
|
sixtp_add_sub_parser(p, TOK, child_pr);
|
|
/* ---------------------------------------------- */
|
|
|
|
|
|
static gboolean
|
|
add_all_kvp_value_parsers_as_sub_nodes (sixtp* p,
|
|
sixtp* kvp_frame_parser,
|
|
sixtp* glist_parser)
|
|
{
|
|
sixtp* child_pr;
|
|
|
|
g_return_val_if_fail (p, FALSE);
|
|
g_return_val_if_fail (kvp_frame_parser, FALSE);
|
|
|
|
KVP_TOKEN (gint64, "gint64");
|
|
KVP_TOKEN (double, "double");
|
|
KVP_TOKEN (gnc_numeric, "numeric");
|
|
KVP_TOKEN (string, "string");
|
|
KVP_TOKEN (guid, "guid");
|
|
|
|
sixtp_add_sub_parser (p, "glist", glist_parser);
|
|
sixtp_add_sub_parser (p, "frame", kvp_frame_parser);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
glist_kvp_value_parser_new (sixtp* kvp_frame_parser)
|
|
{
|
|
sixtp* top_level = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_END_HANDLER_ID, glist_kvp_value_end_handler,
|
|
SIXTP_CLEANUP_RESULT_ID, kvp_value_result_cleanup,
|
|
SIXTP_RESULT_FAIL_ID, kvp_value_result_cleanup,
|
|
SIXTP_NO_MORE_HANDLERS);
|
|
if (!top_level)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!add_all_kvp_value_parsers_as_sub_nodes (top_level,
|
|
kvp_frame_parser,
|
|
top_level))
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return (NULL);
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/*********************************/
|
|
/* kvp-frame slot handlers
|
|
|
|
handlers for the <s><k>some key</k><[value]>data</[value]> sub-structure.
|
|
*/
|
|
|
|
/* <k> (lineage <s> <kvp-frame>)
|
|
|
|
kvp-frame slot key handler - just a generic-string-parser
|
|
|
|
*/
|
|
|
|
/* <s> (lineage <kvp-frame>)
|
|
|
|
kvp-frame slot handler.
|
|
|
|
input: KvpFrame*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: allow_and_ignore_only_whitespace
|
|
end: check for two children - one must be a <k> - if OK, set slot.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: NA
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
kvp_frame_slot_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
KvpFrame* f = (KvpFrame*) parent_data;
|
|
GSList* lp;
|
|
gboolean first = TRUE;
|
|
gchar* key = NULL;
|
|
KvpValue* value = NULL;
|
|
gboolean delete_value = FALSE;
|
|
sixtp_child_result *cr1 = NULL, *cr2 = NULL, *cr = NULL;
|
|
g_return_val_if_fail (f, FALSE);
|
|
|
|
if (g_slist_length (data_from_children) != 2) return (FALSE);
|
|
cr1 = (sixtp_child_result*)data_from_children->data;
|
|
cr2 = (sixtp_child_result*)data_from_children->next->data;
|
|
|
|
if (is_child_result_from_node_named(cr1, "k"))
|
|
{
|
|
key = (char*)cr1->data;
|
|
cr = cr2;
|
|
}
|
|
else if (is_child_result_from_node_named(cr2, "k"))
|
|
{
|
|
key = (char*)cr2->data;
|
|
cr = cr1;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
if (is_child_result_from_node_named (cr, "frame"))
|
|
{
|
|
KvpFrame* frame = static_cast<KvpFrame*> (cr->data);
|
|
value = new KvpValue {frame};
|
|
delete_value = TRUE;
|
|
}
|
|
else
|
|
{
|
|
value = static_cast<KvpValue*> (cr->data);
|
|
delete_value = FALSE;
|
|
}
|
|
|
|
f->set ({key}, value);
|
|
if (delete_value)
|
|
delete value;
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
kvp_frame_slot_parser_new (sixtp* kvp_frame_parser)
|
|
{
|
|
sixtp* top_level;
|
|
sixtp* child_pr;
|
|
sixtp* glist_pr;
|
|
|
|
g_return_val_if_fail (kvp_frame_parser, NULL);
|
|
|
|
if (! (top_level = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_END_HANDLER_ID, kvp_frame_slot_end_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
child_pr = simple_chars_only_parser_new (NULL);
|
|
if (!child_pr)
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return (NULL);
|
|
}
|
|
sixtp_add_sub_parser (top_level, "k", child_pr);
|
|
|
|
glist_pr = glist_kvp_value_parser_new (kvp_frame_parser);
|
|
if (!glist_pr)
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return (NULL);
|
|
}
|
|
|
|
if (!add_all_kvp_value_parsers_as_sub_nodes (top_level,
|
|
kvp_frame_parser,
|
|
glist_pr))
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return (NULL);
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
|
|
/* <kvp-frame> - can be used anywhere.
|
|
|
|
input: NA
|
|
returns: KvpFrame*
|
|
|
|
start: Allocates KvpFrame* and places in data_for_children.
|
|
characters: none (whitespace only).
|
|
end: put KvpFrame* into result if everything's OK.
|
|
|
|
cleanup-result: delete KvpFrame*
|
|
cleanup-chars: NA
|
|
fail: delete KvpFrame*
|
|
result-fail: delete KvpFrame*
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
kvp_frame_start_handler (GSList* sibling_data, gpointer parent_data,
|
|
gpointer global_data, gpointer* data_for_children,
|
|
gpointer* result, const gchar* tag, gchar** attrs)
|
|
{
|
|
auto f = new KvpFrame;
|
|
*data_for_children = f;
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
kvp_frame_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
g_return_val_if_fail (data_for_children != NULL, FALSE);
|
|
*result = data_for_children;
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
kvp_frame_fail_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
auto f = static_cast<KvpFrame*> (data_for_children);
|
|
if (f) delete f;
|
|
}
|
|
|
|
static void
|
|
kvp_frame_result_cleanup (sixtp_child_result* cr)
|
|
{
|
|
auto f = static_cast<KvpFrame*> (cr->data);
|
|
if (f) delete f;
|
|
}
|
|
|
|
static sixtp*
|
|
kvp_frame_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
|
|
if (! (top_level = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, kvp_frame_start_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_END_HANDLER_ID, kvp_frame_end_handler,
|
|
SIXTP_CLEANUP_RESULT_ID, kvp_frame_result_cleanup,
|
|
SIXTP_RESULT_FAIL_ID, kvp_frame_result_cleanup,
|
|
SIXTP_FAIL_HANDLER_ID, kvp_frame_fail_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (! (sixtp_add_some_sub_parsers (
|
|
top_level, TRUE,
|
|
"s", kvp_frame_slot_parser_new (top_level),
|
|
NULL, NULL)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/****************************************************************************/
|
|
/****************************************************************************/
|
|
/* <ledger-data> (parent <gnc-data>)
|
|
|
|
On failure or on normal cleanup, the root account will be killed,
|
|
so if you want it, you better set should_cleanup to false
|
|
|
|
input: NA
|
|
to-children-via-*result: new root Account*
|
|
returns: an Account*
|
|
start: creates the root account and puts it into *result
|
|
characters: NA
|
|
end: finishes up the root account and leaves it in result.
|
|
cleanup-result: deletes the root account (use should_cleanup to avoid).
|
|
cleanup-chars: NA
|
|
fail: deletes the root account in *result.
|
|
result-fail: same as cleanup-result.
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
|
|
static gboolean
|
|
ledger_data_start_handler (GSList* sibling_data, gpointer parent_data,
|
|
gpointer global_data, gpointer* data_for_children,
|
|
gpointer* result, const gchar* tag, gchar** attrs)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Account* ra;
|
|
|
|
/* disable logging during load; otherwise its just a mess */
|
|
xaccLogDisable ();
|
|
ra = xaccMallocAccount (pstatus->book);
|
|
|
|
g_return_val_if_fail (ra, FALSE);
|
|
|
|
*data_for_children = ra;
|
|
return (ra != NULL);
|
|
}
|
|
|
|
static gboolean
|
|
ledger_data_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
if (!child_result) return (TRUE);
|
|
|
|
/* if we see the pricedb, deal with it */
|
|
if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
|
|
if (strcmp (child_result->tag, "pricedb") == 0)
|
|
{
|
|
GNCPriceDB* pdb = (GNCPriceDB*) child_result->data;
|
|
GNCParseStatus* status = (GNCParseStatus*) global_data;
|
|
|
|
g_return_val_if_fail (pdb, FALSE);
|
|
g_return_val_if_fail (status, FALSE);
|
|
|
|
if (status->pricedb)
|
|
{
|
|
PERR ("hit pricedb twice in data file.");
|
|
return FALSE;
|
|
}
|
|
status->pricedb = pdb;
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
ledger_data_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
|
|
Account* ra = (Account*) data_for_children;
|
|
GList* descendants;
|
|
|
|
g_return_val_if_fail (ra, FALSE);
|
|
|
|
/* commit all accounts, this completes the BeginEdit started when the
|
|
* account_end_handler finished reading the account.
|
|
*/
|
|
descendants = gnc_account_get_descendants (ra);
|
|
g_list_foreach (descendants, (GFunc)xaccAccountCommitEdit, NULL);
|
|
g_list_free (descendants);
|
|
|
|
xaccLogEnable ();
|
|
|
|
*result = ra;
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
ledger_data_fail_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
Account* account = (Account*) data_for_children;
|
|
if (account)
|
|
{
|
|
xaccAccountBeginEdit (account);
|
|
xaccAccountDestroy (account);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ledger_data_result_cleanup (sixtp_child_result* cr)
|
|
{
|
|
Account* account = (Account*) cr->data;
|
|
if (account)
|
|
{
|
|
xaccAccountBeginEdit (account);
|
|
xaccAccountDestroy (account);
|
|
}
|
|
}
|
|
|
|
|
|
static sixtp*
|
|
ledger_data_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
|
|
/* <ledger-data> */
|
|
if (! (top_level = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, ledger_data_start_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID, ledger_data_after_child_handler,
|
|
SIXTP_END_HANDLER_ID, ledger_data_end_handler,
|
|
SIXTP_CLEANUP_RESULT_ID, ledger_data_result_cleanup,
|
|
SIXTP_FAIL_HANDLER_ID, ledger_data_fail_handler,
|
|
SIXTP_RESULT_FAIL_ID, ledger_data_result_cleanup,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!sixtp_add_some_sub_parsers (
|
|
top_level, TRUE,
|
|
"commodity", commodity_restore_parser_new (),
|
|
"pricedb", gnc_pricedb_parser_new (),
|
|
"account", gnc_account_parser_new (),
|
|
"transaction", gnc_transaction_parser_new (),
|
|
NULL, NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/****************************************************************************/
|
|
/* <account> (parent <ledger-data>)
|
|
|
|
This block does nothing but pass the ledger-data account group down
|
|
to its children. It generates no data of its own, so it doesn't
|
|
need any cleanup.
|
|
|
|
input: Account*
|
|
|
|
to-children-via-*result: Account*
|
|
|
|
returns: NA
|
|
|
|
start: pass input to children.
|
|
|
|
characters: NA
|
|
|
|
end: NA
|
|
|
|
cleanup-result: NA
|
|
|
|
cleanup-chars: NA
|
|
|
|
fail: NA
|
|
|
|
result-fail: NA
|
|
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
account_start_handler (GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* data_for_children,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
gchar** attrs)
|
|
{
|
|
/* pass the parent data down to the children */
|
|
*data_for_children = parent_data;
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <restore> (lineage <account> <ledger-data>)
|
|
|
|
restores a given account. We allocate the new account in the
|
|
start block, the children modify it, and in the end block, we see
|
|
if the resultant account is OK, and if so, we add it to the
|
|
ledger-data's account group.
|
|
|
|
input: Account*
|
|
to-children-via-*result: new Account*
|
|
returns: NA
|
|
start: create new Account*, and leave in for children.
|
|
characters: NA
|
|
end: clear *result
|
|
cleanup-result: NA
|
|
cleanup-chars: NA
|
|
fail: delete Account*
|
|
result-fail: NA
|
|
chars-fail: NA
|
|
*/
|
|
|
|
static gboolean
|
|
account_restore_start_handler (GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* data_for_children,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
gchar** attrs)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Account* acc = xaccMallocAccount (pstatus->book);
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
xaccAccountBeginEdit (acc);
|
|
|
|
*data_for_children = acc;
|
|
*result = acc;
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
account_restore_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* parent = (Account*) parent_data;
|
|
Account* acc = (Account*) * result;
|
|
|
|
g_return_val_if_fail ((parent && acc), FALSE);
|
|
|
|
/* CHECKME: do we need to xaccAccountRecomputeBalance(acc) here? */
|
|
xaccAccountCommitEdit (acc);
|
|
|
|
/* If the account doesn't have a parent yet, just cram it into the
|
|
top level */
|
|
if (!gnc_account_get_parent (acc))
|
|
gnc_account_append_child (parent, acc);
|
|
|
|
*result = NULL;
|
|
|
|
/* Now return the account to the "edit" state. At the end of reading
|
|
* all the transactions, we will Commit. This replaces #splits
|
|
* rebalances with #accounts rebalances at the end. A BIG win!
|
|
*/
|
|
xaccAccountBeginEdit (acc);
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
account_restore_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
Account* a = (Account*) data_for_children;
|
|
/* GNCParseStatus *pstatus = (GNCParseStatus *) global_data; */
|
|
|
|
g_return_val_if_fail (a, FALSE);
|
|
|
|
if (!child_result) return (TRUE);
|
|
if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
|
|
if (strcmp (child_result->tag, "slots") == 0)
|
|
{
|
|
auto f = static_cast<KvpFrame*> (child_result->data);
|
|
g_return_val_if_fail (f, FALSE);
|
|
if (a->inst.kvp_data) delete a->inst.kvp_data;
|
|
a->inst.kvp_data = f;
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
else if (strcmp (child_result->tag, "currency") == 0)
|
|
{
|
|
gnc_commodity* com = (gnc_commodity*) child_result->data;
|
|
g_return_val_if_fail (com, FALSE);
|
|
if (DxaccAccountGetCurrency (a)) return FALSE;
|
|
DxaccAccountSetCurrency (a, com);
|
|
/* let the normal child_result handler clean up com */
|
|
}
|
|
else if (strcmp (child_result->tag, "security") == 0)
|
|
{
|
|
gnc_commodity* com = (gnc_commodity*) child_result->data;
|
|
g_return_val_if_fail (com, FALSE);
|
|
if (xaccAccountGetCommodity (a)) return FALSE;
|
|
xaccAccountSetCommodity (a, com);
|
|
/* let the normal child_result handler clean up com */
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
account_restore_fail_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
Account* acc = (Account*) * result;
|
|
if (acc)
|
|
{
|
|
xaccAccountBeginEdit (acc);
|
|
xaccAccountDestroy (acc);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <name> (lineage <restore> <account>)
|
|
|
|
restores a given account's name.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as account name.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
static gboolean
|
|
acc_restore_name_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* acc = (Account*) parent_data;
|
|
gchar* name = NULL;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
name = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (name, FALSE);
|
|
|
|
xaccAccountSetName (acc, name);
|
|
g_free (name);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <guid> (lineage <restore> <account>)
|
|
|
|
restores a given account's guid.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as account GncGUID if not duplicate.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
acc_restore_guid_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Account* acc = (Account*) parent_data;
|
|
gchar* txt = NULL;
|
|
GncGUID gid;
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = string_to_guid (txt, &gid);
|
|
g_free (txt);
|
|
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
if (xaccAccountLookup (&gid, pstatus->book))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccAccountSetGUID (acc, &gid);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <type> (lineage <restore> <account>)
|
|
|
|
restores a given account's type.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as account type.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
acc_restore_type_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* acc = (Account*) parent_data;
|
|
gchar* txt = NULL;
|
|
GNCAccountType type;
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = xaccAccountStringToType (txt, &type);
|
|
g_free (txt);
|
|
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
xaccAccountSetType (acc, type);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <code> (lineage <restore> <account>)
|
|
|
|
restores a given account's code.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as account type.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
acc_restore_code_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* acc = (Account*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccAccountSetCode (acc, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <description> (lineage <restore> <account>)
|
|
|
|
restores a given account's description.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as account description.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
restores a given account's description.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
acc_restore_description_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* acc = (Account*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccAccountSetDescription (acc, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <notes> (lineage <restore> <account>)
|
|
|
|
restores a given account's notes.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as account notes.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
acc_restore_notes_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* acc = (Account*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccAccountSetNotes (acc, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <parent> (lineage <restore> <account>)
|
|
|
|
restores a given account's parent.
|
|
input: Account*
|
|
returns: NA
|
|
|
|
start: NA
|
|
|
|
characters: allow and ignore only whitespace.
|
|
|
|
end: check for single <guid> child and if found, use result to set
|
|
account guid.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: NA
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
acc_restore_parent_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Account* acc = (Account*) parent_data;
|
|
Account* parent;
|
|
sixtp_child_result* child_result;
|
|
GncGUID gid;
|
|
|
|
g_return_val_if_fail (acc, FALSE);
|
|
|
|
if (g_slist_length (data_from_children) != 1)
|
|
return (FALSE);
|
|
|
|
child_result = (sixtp_child_result*) data_from_children->data;
|
|
|
|
if (!is_child_result_from_node_named (child_result, "guid"))
|
|
return (FALSE);
|
|
|
|
/* otherwise this must be a good result - use it */
|
|
gid = * ((GncGUID*) child_result->data);
|
|
|
|
parent = xaccAccountLookup (&gid, pstatus->book);
|
|
|
|
g_return_val_if_fail (parent, FALSE);
|
|
|
|
gnc_account_append_child (parent, acc);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static sixtp*
|
|
parent_lookup_parser_new (void)
|
|
{
|
|
return sixtp_set_any (sixtp_new (), TRUE,
|
|
SIXTP_CHARACTERS_HANDLER_ID,
|
|
allow_and_ignore_only_whitespace,
|
|
SIXTP_END_HANDLER_ID,
|
|
acc_restore_parent_end_handler,
|
|
SIXTP_NO_MORE_HANDLERS);
|
|
}
|
|
|
|
static sixtp*
|
|
gnc_account_parser_new (void)
|
|
{
|
|
sixtp* restore_pr;
|
|
sixtp* ret;
|
|
|
|
/* <account> */
|
|
if (! (ret = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, account_start_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* <account> <restore> */
|
|
if (! (restore_pr =
|
|
sixtp_set_any (sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, account_restore_start_handler,
|
|
SIXTP_END_HANDLER_ID, account_restore_end_handler,
|
|
SIXTP_FAIL_HANDLER_ID, account_restore_fail_handler,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID,
|
|
account_restore_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
sixtp_destroy (ret);
|
|
return NULL;
|
|
}
|
|
|
|
/* <restore> (<name> | <guid> | <type> | <code> | <description> | <notes>)*/
|
|
if (!sixtp_add_some_sub_parsers (
|
|
restore_pr, TRUE,
|
|
"name", restore_char_generator (acc_restore_name_end_handler),
|
|
"guid", restore_char_generator (acc_restore_guid_end_handler),
|
|
"type", restore_char_generator (acc_restore_type_end_handler),
|
|
"code", restore_char_generator (acc_restore_code_end_handler),
|
|
"description",
|
|
restore_char_generator (acc_restore_description_end_handler),
|
|
"notes", restore_char_generator (acc_restore_notes_end_handler),
|
|
/* <account> <restore> <currency> */
|
|
"currency", generic_gnc_commodity_lookup_parser_new (),
|
|
/* <account> <restore> <security> */
|
|
"security", generic_gnc_commodity_lookup_parser_new (),
|
|
/* <account> <restore> <parent> */
|
|
"parent", sixtp_add_some_sub_parsers (
|
|
parent_lookup_parser_new (), TRUE,
|
|
"guid", generic_guid_parser_new (),
|
|
NULL, NULL),
|
|
"slots", kvp_frame_parser_new (),
|
|
NULL, NULL))
|
|
{
|
|
sixtp_destroy (ret);
|
|
return NULL;
|
|
}
|
|
|
|
sixtp_add_sub_parser (ret, "restore", restore_pr);
|
|
|
|
return ret;
|
|
}
|
|
/***********************************************************************/
|
|
/****************************************************************************/
|
|
/* Commodity restorer.
|
|
|
|
Right now we just check to see that fields aren't duplicated. If
|
|
fields don't show up, then we just use "".
|
|
|
|
We also check to see that we get a <fraction>. If not, it's an
|
|
error.
|
|
|
|
Example:
|
|
<commodity>
|
|
<restore>
|
|
<space>NASDAQ</space>
|
|
<id>XYZZY</id>
|
|
<name>Grue Enterprises</name>
|
|
<xcode>XXX</xcode>
|
|
<fraction>100</fraction>
|
|
</restore>
|
|
</commodity>
|
|
|
|
*/
|
|
|
|
/* ==================================================================== */
|
|
|
|
/*********************************/
|
|
/* <restore> (lineage <commodity>)
|
|
|
|
Start handler allocates a gnc_commodity. The end_handler, if
|
|
everything's OK, crams the commodity into the engine, otherwise it
|
|
deletes it.
|
|
|
|
input: NA
|
|
returns: NA
|
|
|
|
start: allocate CommodityParseInfo* and put it into data_for_children.
|
|
characters: allow and ignore only whitespace.
|
|
after-child: handle strings from simple chars children.
|
|
end: if OK create gnc_commodity and add to engine. delete CommodityParseInfo.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: NA
|
|
fail: delete CommodityParseInfo*.
|
|
result-fail: NA
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
gchar* space;
|
|
gchar* id;
|
|
gchar* name;
|
|
gchar* xcode;
|
|
gboolean seen_fraction;
|
|
int fraction;
|
|
} CommodityParseInfo;
|
|
|
|
static gboolean
|
|
commodity_restore_start_handler (GSList* sibling_data, gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* data_for_children, gpointer* result,
|
|
const gchar* tag, gchar** attrs)
|
|
{
|
|
CommodityParseInfo* cpi =
|
|
(CommodityParseInfo*) g_new0 (CommodityParseInfo, 1);
|
|
|
|
g_return_val_if_fail (cpi, FALSE);
|
|
|
|
*data_for_children = cpi;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* ----------------------------------------------------*/
|
|
#define COMMOD_TOKEN(NAME) \
|
|
if(strcmp(child_result->tag, #NAME) == 0) { \
|
|
if(cpi->NAME) return(FALSE); \
|
|
cpi->NAME = (gchar *) child_result->data; \
|
|
child_result->should_cleanup = FALSE; \
|
|
} \
|
|
else
|
|
/* ----------------------------------------------------*/
|
|
|
|
static gboolean
|
|
commodity_restore_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
CommodityParseInfo* cpi = (CommodityParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (cpi, FALSE);
|
|
g_return_val_if_fail (child_result, FALSE);
|
|
|
|
COMMOD_TOKEN (space)
|
|
COMMOD_TOKEN (id)
|
|
COMMOD_TOKEN (name)
|
|
COMMOD_TOKEN (xcode)
|
|
if (strcmp (child_result->tag, "fraction") == 0)
|
|
{
|
|
gint64 frac;
|
|
|
|
if (cpi->seen_fraction) return (FALSE);
|
|
string_to_gint64 ((gchar*) child_result->data, &frac);
|
|
cpi->fraction = frac;
|
|
cpi->seen_fraction = TRUE;
|
|
child_result->should_cleanup = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* redundant because the parser won't allow any other children */
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
commodity_restore_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
CommodityParseInfo* cpi = (CommodityParseInfo*) data_for_children;
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
gboolean ok = FALSE;
|
|
gnc_commodity* comm = NULL;
|
|
|
|
g_return_val_if_fail (cpi, FALSE);
|
|
|
|
if (cpi->seen_fraction)
|
|
{
|
|
gnc_commodity* comm;
|
|
|
|
if (!cpi->space) cpi->space = g_strdup ("");
|
|
if (!cpi->id) cpi->id = g_strdup ("");
|
|
if (!cpi->name) cpi->name = g_strdup ("");
|
|
if (!cpi->xcode) cpi->xcode = g_strdup ("");
|
|
|
|
comm = gnc_commodity_new (pstatus->book,
|
|
cpi->name,
|
|
cpi->space,
|
|
cpi->id,
|
|
cpi->xcode,
|
|
cpi->fraction);
|
|
if (comm)
|
|
{
|
|
gnc_commodity_table* ctab;
|
|
|
|
ctab = gnc_commodity_table_get_table (pstatus->book);
|
|
|
|
if (ctab)
|
|
{
|
|
gnc_commodity_table_insert (ctab, comm);
|
|
ok = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_free (cpi->space);
|
|
g_free (cpi->id);
|
|
g_free (cpi->name);
|
|
g_free (cpi->xcode);
|
|
g_free (cpi);
|
|
|
|
if (!ok) gnc_commodity_destroy (comm);
|
|
|
|
return (ok);
|
|
}
|
|
|
|
|
|
static sixtp*
|
|
commodity_restore_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
sixtp* restore_pr;
|
|
|
|
top_level = sixtp_new ();
|
|
g_return_val_if_fail (top_level, NULL);
|
|
|
|
if (! (restore_pr = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, commodity_restore_start_handler,
|
|
SIXTP_END_HANDLER_ID, commodity_restore_end_handler,
|
|
SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID, commodity_restore_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return (NULL);
|
|
}
|
|
sixtp_add_sub_parser (top_level, "restore", restore_pr);
|
|
|
|
if (!sixtp_add_some_sub_parsers (
|
|
restore_pr, TRUE,
|
|
"space", simple_chars_only_parser_new (NULL),
|
|
"id", simple_chars_only_parser_new (NULL),
|
|
"name", simple_chars_only_parser_new (NULL),
|
|
"xcode", simple_chars_only_parser_new (NULL),
|
|
"fraction", simple_chars_only_parser_new (NULL),
|
|
NULL, NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* generic gnc_commodity lookup handler.
|
|
|
|
A collection of node functions intended to parse a sub-node set
|
|
that looks like this:
|
|
|
|
<security>
|
|
<space>NASDAQ</space>
|
|
<id>ZXDDQ</id>
|
|
</security>
|
|
|
|
and produce a gnc_commodity* by looking up the unique combination
|
|
of namespace and ID (mnemonic).
|
|
|
|
The start handler for the top allocates a CommodityParseInfo* and
|
|
passes it to the children. The <space> block sets the namespace
|
|
and the <id> block sets the ID. The end handler performs the
|
|
lookup. If all goes well, returns the gnc_commodity* as the
|
|
result. */
|
|
|
|
/* Top level gnc_commodity lookup node:
|
|
|
|
input: NA
|
|
returns: gnc_commodity*
|
|
|
|
start: Allocates CommodityParseInfo* for data_for_children.
|
|
characters: none (whitespace only).
|
|
end: lookup commodity and place into *result, free data_for_children.
|
|
|
|
fail: g_free data_for_children (CommodityParseInfo and contents).
|
|
cleanup-chars: NA
|
|
chars-fail: NA
|
|
cleanup-result: NA (we didn't create the gnc_commodity we're returning)
|
|
result-fail: NA
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
gchar* name_space;
|
|
gchar* id;
|
|
} CommodityLookupParseInfo;
|
|
|
|
static gboolean
|
|
generic_gnc_commodity_lookup_start_handler (
|
|
GSList* sibling_data, gpointer parent_data, gpointer global_data,
|
|
gpointer* data_for_children, gpointer* result, const gchar* tag,
|
|
gchar** attrs)
|
|
{
|
|
CommodityLookupParseInfo* cpi = g_new0 (CommodityLookupParseInfo, 1);
|
|
g_return_val_if_fail (cpi, FALSE);
|
|
*data_for_children = cpi;
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
generic_gnc_commodity_lookup_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
CommodityLookupParseInfo* cpi =
|
|
(CommodityLookupParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (cpi, FALSE);
|
|
g_return_val_if_fail (child_result, FALSE);
|
|
if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (FALSE);
|
|
|
|
if (strcmp (child_result->tag, "space") == 0)
|
|
{
|
|
if (cpi->name_space) return (FALSE);
|
|
cpi->name_space = (gchar*) child_result->data;
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
else if (strcmp (child_result->tag, "id") == 0)
|
|
{
|
|
if (cpi->id) return (FALSE);
|
|
cpi->id = (gchar*) child_result->data;
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* redundant because the parser won't allow any other children */
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
generic_gnc_commodity_lookup_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
CommodityLookupParseInfo* cpi =
|
|
(CommodityLookupParseInfo*) data_for_children;
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
gboolean ok = FALSE;
|
|
|
|
g_return_val_if_fail (cpi, FALSE);
|
|
|
|
if (cpi->name_space && cpi->id)
|
|
{
|
|
gnc_commodity_table* table;
|
|
gnc_commodity* com;
|
|
|
|
table = gnc_commodity_table_get_table (pstatus->book);
|
|
|
|
com = gnc_commodity_table_lookup (table, cpi->name_space, cpi->id);
|
|
|
|
if (com)
|
|
{
|
|
*result = com;
|
|
ok = TRUE;
|
|
}
|
|
}
|
|
|
|
g_free (cpi->name_space);
|
|
g_free (cpi->id);
|
|
g_free (cpi);
|
|
|
|
return (ok);
|
|
}
|
|
|
|
|
|
static sixtp*
|
|
generic_gnc_commodity_lookup_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
|
|
if (! (top_level = sixtp_set_any (
|
|
sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, generic_gnc_commodity_lookup_start_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
|
|
SIXTP_END_HANDLER_ID, generic_gnc_commodity_lookup_end_handler,
|
|
SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID,
|
|
generic_gnc_commodity_lookup_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!sixtp_add_some_sub_parsers (
|
|
top_level, TRUE,
|
|
"space", simple_chars_only_parser_new (NULL),
|
|
"id", simple_chars_only_parser_new (NULL),
|
|
NULL, NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <transaction> (parent <ledger-data>)
|
|
|
|
This block does nothing but pass the ledger-data account group down
|
|
to its children. It generates no data of its own, so it doesn't
|
|
need any cleanup.
|
|
|
|
input: Account*
|
|
|
|
to-children-via-*result: Account*
|
|
|
|
returns: NA
|
|
|
|
start: pass input to children.
|
|
|
|
characters: ignore whitespace only
|
|
|
|
end: NA
|
|
|
|
cleanup-result: NA
|
|
|
|
cleanup-chars: NA
|
|
|
|
fail: NA
|
|
|
|
result-fail: NA
|
|
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
transaction_start_handler (GSList* sibling_data, gpointer parent_data,
|
|
gpointer global_data, gpointer* data_for_children,
|
|
gpointer* result, const gchar* tag, gchar** attrs)
|
|
{
|
|
/* pass the parent data down to the children */
|
|
*data_for_children = parent_data;
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <restore> (lineage <transaction> <ledger-data>)
|
|
|
|
restores a given transaction. We allocate the new transaction in
|
|
the start block, the children modify it, and in the end block, we
|
|
see if the resultant account is OK, and if so, we add it to the
|
|
ledger-data's account group.
|
|
|
|
from parent: Account*
|
|
|
|
for children: new Transaction*
|
|
|
|
result: NA
|
|
|
|
-----------
|
|
|
|
start: create new Transaction*, and store in data_for_children.
|
|
|
|
chars: allow and ignore only whitespace.
|
|
|
|
end: commit transaction to group if appropriate.
|
|
|
|
cleanup-result: NA
|
|
|
|
cleanup-chars: NA
|
|
|
|
fail: delete Transaction* in data_for_children
|
|
|
|
result-fail: NA
|
|
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_start_handler (GSList* sibling_data, gpointer parent_data,
|
|
gpointer global_data, gpointer* data_for_children,
|
|
gpointer* result, const gchar* tag, gchar** attrs)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Transaction* trans = xaccMallocTransaction (pstatus->book);
|
|
|
|
g_return_val_if_fail (trans, FALSE);
|
|
|
|
xaccTransBeginEdit (trans);
|
|
*data_for_children = trans;
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
txn_restore_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Account* parent = (Account*) parent_data;
|
|
Transaction* trans = (Transaction*) data_for_children;
|
|
|
|
g_return_val_if_fail (trans, FALSE);
|
|
if (!parent)
|
|
{
|
|
xaccTransDestroy (trans);
|
|
xaccTransCommitEdit (trans);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!xaccTransGetGUID (trans))
|
|
{
|
|
/* must at least have a GncGUID for a restore */
|
|
xaccTransDestroy (trans);
|
|
xaccTransCommitEdit (trans);
|
|
return (FALSE);
|
|
}
|
|
|
|
/* FIXME: what if the trans has no splits? */
|
|
xaccTransCommitEdit (trans);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
txn_restore_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
Transaction* trans = (Transaction*) data_for_children;
|
|
g_return_val_if_fail (trans, FALSE);
|
|
if (!child_result) return (TRUE);
|
|
if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
|
|
if (strcmp (child_result->tag, "slots") == 0)
|
|
{
|
|
KvpFrame* f = (KvpFrame*) child_result->data;
|
|
g_return_val_if_fail (f, FALSE);
|
|
qof_instance_set_slots (QOF_INSTANCE (trans), f);
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
txn_restore_fail_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
Transaction* trans = (Transaction*) data_for_children;
|
|
if (trans)
|
|
{
|
|
xaccTransDestroy (trans);
|
|
xaccTransCommitEdit (trans);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <guid> (lineage <restore> <transaction>)
|
|
|
|
restores a given account's guid.
|
|
|
|
from parent: Transaction*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as transaction GncGUID if not duplicate.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_guid_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Transaction* t = (Transaction*) parent_data;
|
|
gchar* txt = NULL;
|
|
GncGUID gid;
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail (t, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = string_to_guid (txt, &gid);
|
|
g_free (txt);
|
|
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
if (xaccTransLookup (&gid, pstatus->book))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccTransSetGUID (t, &gid);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <num> (lineage <restore> <transaction>)
|
|
|
|
restores a given transaction's num.
|
|
|
|
from parent: Transaction*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as transaction num.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_num_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Transaction* t = (Transaction*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (t, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccTransSetNum (t, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <description> (lineage <restore> <transaction>)
|
|
|
|
restores a given transaction's description.
|
|
|
|
from parent: Transaction*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as transaction description.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_description_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Transaction* t = (Transaction*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (t, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccTransSetDescription (t, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <date-posted> (lineage <restore> <transaction>)
|
|
|
|
restores a given transaction's posted date.
|
|
|
|
Just uses a generic_timespec parser, but with our own end handler.
|
|
|
|
end: set date posted.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_rest_date_posted_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Transaction* t = (Transaction*) parent_data;
|
|
Time64ParseInfo* info = (Time64ParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (info, FALSE);
|
|
if (!t || !timespec_parse_ok (info))
|
|
{
|
|
g_free (info);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccTransSetDatePostedSecs (t, info->time);
|
|
g_free (info);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <date-entered> (lineage <restore> <transaction>)
|
|
|
|
restores a given transaction's entered date.
|
|
|
|
Just uses a generic_timespec parser, but with our own end handler.
|
|
|
|
end: set date entered.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_rest_date_entered_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Transaction* t = (Transaction*) parent_data;
|
|
Time64ParseInfo* info = (Time64ParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (info, FALSE);
|
|
if (!t || !timespec_parse_ok (info))
|
|
{
|
|
g_free (info);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccTransSetDateEnteredSecs (t, info->time);
|
|
g_free (info);
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
/* <split> (lineage <restore> <transaction> <ledger-data>)
|
|
|
|
Restores a given split. We allocate the new split in the start
|
|
block, the children modify it, and in the end block, we see if the
|
|
resultant split is OK, and if so, we add it to the input Transaction*
|
|
account group.
|
|
|
|
from parent: Transaction*
|
|
for children: new Split*
|
|
result: NA
|
|
-----------
|
|
start: create new Split*, and store in data_for_children.
|
|
chars: allow and ignore only whitespace.
|
|
end: commit split to transaction if appropriate.
|
|
cleanup-result: NA
|
|
cleanup-chars: NA
|
|
fail: delete Transaction* in data_for_children
|
|
result-fail: NA
|
|
chars-fail: NA
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_start_handler (GSList* sibling_data, gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* data_for_children, gpointer* result,
|
|
const gchar* tag, gchar** attrs)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Split* s = xaccMallocSplit (pstatus->book);
|
|
g_return_val_if_fail (s, FALSE);
|
|
*data_for_children = s;
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
txn_restore_split_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Transaction* t = (Transaction*) parent_data;
|
|
Split* s = (Split*) data_for_children;
|
|
|
|
g_return_val_if_fail (s, FALSE);
|
|
if (!t)
|
|
{
|
|
xaccSplitDestroy (s);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (!xaccSplitGetGUID (s))
|
|
{
|
|
/* must at least have a GncGUID for a restore */
|
|
xaccSplitDestroy (s);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccTransAppendSplit (t, s);
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
txn_restore_split_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
Split* s = (Split*) data_for_children;
|
|
g_return_val_if_fail (s, FALSE);
|
|
if (!child_result) return (TRUE);
|
|
if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (TRUE);
|
|
|
|
if (strcmp (child_result->tag, "slots") == 0)
|
|
{
|
|
KvpFrame* f = static_cast<KvpFrame*> (child_result->data);
|
|
g_return_val_if_fail (f, FALSE);
|
|
if (s->inst.kvp_data) delete s->inst.kvp_data;
|
|
s->inst.kvp_data = f;
|
|
child_result->should_cleanup = FALSE;
|
|
}
|
|
else if (strcmp (child_result->tag, "quantity") == 0)
|
|
{
|
|
gnc_numeric* n = (gnc_numeric*) child_result->data;
|
|
g_return_val_if_fail (n, FALSE);
|
|
xaccSplitSetAmount (s, *n);
|
|
/* let the normal child_result handler clean up n */
|
|
}
|
|
else if (strcmp (child_result->tag, "value") == 0)
|
|
{
|
|
gnc_numeric* n = (gnc_numeric*) child_result->data;
|
|
g_return_val_if_fail (n, FALSE);
|
|
xaccSplitSetValue (s, *n);
|
|
/* let the normal child_result handler clean up n */
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
txn_restore_split_fail_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
Split* s = (Split*) data_for_children;
|
|
if (s) xaccSplitDestroy (s);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <guid> (lineage <split> <restore> <transaction>)
|
|
|
|
restores a given split's guid.
|
|
|
|
from parent: Split*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as split GncGUID if not duplicate.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_guid_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Split* s = (Split*) parent_data;
|
|
gchar* txt = NULL;
|
|
GncGUID gid;
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail (s, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = string_to_guid (txt, &gid);
|
|
g_free (txt);
|
|
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
if (xaccSplitLookup (&gid, pstatus->book))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccSplitSetGUID (s, &gid);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <memo> (lineage <split> <restore> <transaction>)
|
|
|
|
restores a given split's memo.
|
|
|
|
from parent: Split*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as split description.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_memo_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Split* s = (Split*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (s, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccSplitSetMemo (s, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <action> (lineage <split> <restore> <transaction>)
|
|
|
|
restores a given split's action.
|
|
|
|
from parent: Split*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as split action.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_action_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Split* s = (Split*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (s, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
xaccSplitSetAction (s, txt);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <reconcile-state> (lineage <split> <restore> <transaction>)
|
|
|
|
restores a given split's reconcile-state.
|
|
|
|
from parent: Split*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as split reconcile-state.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_reconcile_state_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Split* s = (Split*) parent_data;
|
|
gchar* txt = NULL;
|
|
|
|
g_return_val_if_fail (s, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
if (strlen (txt) != 1)
|
|
{
|
|
g_free (txt);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccSplitSetReconcile (s, txt[0]);
|
|
g_free (txt);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <reconcile-date> (lineage <split> <restore> <transaction>)
|
|
|
|
restores a given split's reconcile-date.
|
|
|
|
Just uses a generic_timespec parser, but with our own end handler.
|
|
|
|
end: set reconcile-date.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_reconcile_date_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
Split* s = (Split*) parent_data;
|
|
Time64ParseInfo* info = (Time64ParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (info, FALSE);
|
|
if (!s || !timespec_parse_ok (info))
|
|
{
|
|
g_free (info);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccSplitSetDateReconciledSecs (s, info->time);
|
|
g_free (info);
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* <account> (lineage <split> <restore> <transaction>)
|
|
|
|
restores a given split's account.
|
|
|
|
from parent: Split*
|
|
for children: NA
|
|
result: NA
|
|
-----------
|
|
start: NA
|
|
characters: return string copy for accumulation in end handler.
|
|
end: concatenate all chars and set as split account if GncGUID OK.
|
|
|
|
cleanup-result: NA
|
|
cleanup-chars: g_free the result string.
|
|
fail: NA
|
|
result-fail: NA
|
|
chars-fail: g_free the result string.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
txn_restore_split_account_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children, GSList* sibling_data,
|
|
gpointer parent_data, gpointer global_data,
|
|
gpointer* result, const gchar* tag)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
Split* s = (Split*) parent_data;
|
|
Account* acct;
|
|
gchar* txt = NULL;
|
|
GncGUID gid;
|
|
gboolean ok;
|
|
|
|
g_return_val_if_fail (s, FALSE);
|
|
|
|
txt = concatenate_child_result_chars (data_from_children);
|
|
g_return_val_if_fail (txt, FALSE);
|
|
|
|
ok = string_to_guid (txt, &gid);
|
|
g_free (txt);
|
|
|
|
g_return_val_if_fail (ok, FALSE);
|
|
|
|
acct = xaccAccountLookup (&gid, pstatus->book);
|
|
g_return_val_if_fail (acct, FALSE);
|
|
|
|
xaccAccountInsertSplit (acct, s);
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
static sixtp*
|
|
gnc_txn_restore_split_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
|
|
if (! (top_level =
|
|
sixtp_set_any (sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, txn_restore_split_start_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID,
|
|
allow_and_ignore_only_whitespace,
|
|
SIXTP_END_HANDLER_ID, txn_restore_split_end_handler,
|
|
SIXTP_FAIL_HANDLER_ID, txn_restore_split_fail_handler,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID,
|
|
txn_restore_split_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!sixtp_add_some_sub_parsers (
|
|
top_level, TRUE,
|
|
"guid", restore_char_generator (txn_restore_split_guid_end_handler),
|
|
"memo", restore_char_generator (txn_restore_split_memo_end_handler),
|
|
"action",
|
|
restore_char_generator (txn_restore_split_action_end_handler),
|
|
"account",
|
|
restore_char_generator (txn_restore_split_account_end_handler),
|
|
"reconcile-state",
|
|
restore_char_generator (txn_restore_split_reconcile_state_end_handler),
|
|
"reconcile-date",
|
|
generic_timespec_parser_new (
|
|
txn_restore_split_reconcile_date_end_handler),
|
|
"quantity", generic_gnc_numeric_parser_new (),
|
|
"value", generic_gnc_numeric_parser_new (),
|
|
"slots", kvp_frame_parser_new (),
|
|
NULL, NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
static sixtp*
|
|
gnc_transaction_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
sixtp* restore_pr;
|
|
|
|
if (! (top_level =
|
|
sixtp_set_any (sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, transaction_start_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID,
|
|
allow_and_ignore_only_whitespace,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID,
|
|
txn_restore_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* <restore> */
|
|
if (! (restore_pr =
|
|
sixtp_set_any (sixtp_new (), FALSE,
|
|
SIXTP_START_HANDLER_ID, txn_restore_start_handler,
|
|
SIXTP_END_HANDLER_ID, txn_restore_end_handler,
|
|
SIXTP_FAIL_HANDLER_ID, txn_restore_fail_handler,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID,
|
|
txn_restore_after_child_handler,
|
|
SIXTP_NO_MORE_HANDLERS)))
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return (NULL);
|
|
}
|
|
sixtp_add_sub_parser (top_level, "restore", restore_pr);
|
|
|
|
if (! (sixtp_add_some_sub_parsers (
|
|
restore_pr, TRUE,
|
|
"guid", restore_char_generator (txn_restore_guid_end_handler),
|
|
"num", restore_char_generator (txn_restore_num_end_handler),
|
|
"description",
|
|
restore_char_generator (txn_restore_description_end_handler),
|
|
"date-posted",
|
|
generic_timespec_parser_new (txn_rest_date_posted_end_handler),
|
|
"date-entered",
|
|
generic_timespec_parser_new (txn_rest_date_entered_end_handler),
|
|
"slots", kvp_frame_parser_new (),
|
|
"split", gnc_txn_restore_split_parser_new (),
|
|
NULL, NULL)))
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return NULL;
|
|
}
|
|
|
|
return (top_level);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/****************************************************************************/
|
|
|
|
/* Read and Write the pricedb as XML -- something like this:
|
|
|
|
<pricedb>
|
|
price-1
|
|
price-2
|
|
...
|
|
</pricedb>
|
|
|
|
where each price should look roughly like this:
|
|
|
|
<price>
|
|
<price:id>
|
|
00000000111111112222222233333333
|
|
</price:id>
|
|
<price:commodity>
|
|
<cmdty:space>NASDAQ</cmdty:space>
|
|
<cmdty:id>RHAT</cmdty:id>
|
|
</price:commodity>
|
|
<price:currency>
|
|
<cmdty:space>ISO?</cmdty:space>
|
|
<cmdty:id>USD</cmdty:id>
|
|
</price:currency>
|
|
<price:time><ts:date>Mon ...</ts:date><ts:ns>12</ts:ns></price:time>
|
|
<price:source>Finance::Quote</price:source>
|
|
<price:type>bid</price:type>
|
|
<price:value>11011/100</price:value>
|
|
</price>
|
|
|
|
*/
|
|
|
|
/***********************************************************************/
|
|
/* READING */
|
|
/***********************************************************************/
|
|
|
|
/****************************************************************************/
|
|
/* <price>
|
|
|
|
restores a price. Does so via a walk of the XML tree in memory.
|
|
Returns a GNCPrice * in result.
|
|
|
|
Right now, a price is legitimate even if all of it's fields are not
|
|
set. We may need to change that later, but at the moment.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
price_parse_xml_sub_node (GNCPrice* p, xmlNodePtr sub_node, QofBook* book)
|
|
{
|
|
if (!p || !sub_node) return FALSE;
|
|
|
|
gnc_price_begin_edit (p);
|
|
|
|
if (g_strcmp0 ("price:id", (char*)sub_node->name) == 0)
|
|
{
|
|
GncGUID* c = dom_tree_to_guid (sub_node);
|
|
if (!c) return FALSE;
|
|
gnc_price_set_guid (p, c);
|
|
guid_free (c);
|
|
}
|
|
else if (g_strcmp0 ("price:commodity", (char*)sub_node->name) == 0)
|
|
{
|
|
gnc_commodity* c = dom_tree_to_commodity_ref (sub_node, book);
|
|
if (!c) return FALSE;
|
|
gnc_price_set_commodity (p, c);
|
|
}
|
|
else if (g_strcmp0 ("price:currency", (char*)sub_node->name) == 0)
|
|
{
|
|
gnc_commodity* c = dom_tree_to_commodity_ref (sub_node, book);
|
|
if (!c) return FALSE;
|
|
gnc_price_set_currency (p, c);
|
|
}
|
|
else if (g_strcmp0 ("price:time", (char*)sub_node->name) == 0)
|
|
{
|
|
time64 time = dom_tree_to_time64 (sub_node);
|
|
if (!dom_tree_valid_time64 (time, sub_node->name)) time = 0;
|
|
gnc_price_set_time64 (p, time);
|
|
}
|
|
else if (g_strcmp0 ("price:source", (char*)sub_node->name) == 0)
|
|
{
|
|
char* text = dom_tree_to_text (sub_node);
|
|
if (!text) return FALSE;
|
|
gnc_price_set_source_string (p, text);
|
|
g_free (text);
|
|
}
|
|
else if (g_strcmp0 ("price:type", (char*)sub_node->name) == 0)
|
|
{
|
|
char* text = dom_tree_to_text (sub_node);
|
|
if (!text) return FALSE;
|
|
gnc_price_set_typestr (p, text);
|
|
g_free (text);
|
|
}
|
|
else if (g_strcmp0 ("price:value", (char*)sub_node->name) == 0)
|
|
{
|
|
gnc_numeric* value = dom_tree_to_gnc_numeric (sub_node);
|
|
if (!value) return FALSE;
|
|
gnc_price_set_value (p, *value);
|
|
g_free (value);
|
|
}
|
|
gnc_price_commit_edit (p);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
price_parse_xml_end_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag)
|
|
{
|
|
gboolean ok = TRUE;
|
|
xmlNodePtr price_xml = (xmlNodePtr) data_for_children;
|
|
xmlNodePtr child;
|
|
GNCPrice* p = NULL;
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
|
|
/* we haven't been handed the *top* level node yet... */
|
|
if (parent_data) return TRUE;
|
|
|
|
*result = NULL;
|
|
|
|
if (!price_xml) return FALSE;
|
|
if (price_xml->next)
|
|
{
|
|
ok = FALSE;
|
|
goto cleanup_and_exit;
|
|
}
|
|
if (price_xml->prev)
|
|
{
|
|
ok = FALSE;
|
|
goto cleanup_and_exit;
|
|
}
|
|
if (!price_xml->xmlChildrenNode)
|
|
{
|
|
ok = FALSE;
|
|
goto cleanup_and_exit;
|
|
}
|
|
|
|
p = gnc_price_create (pstatus->book);
|
|
if (!p)
|
|
{
|
|
ok = FALSE;
|
|
goto cleanup_and_exit;
|
|
}
|
|
|
|
for (child = price_xml->xmlChildrenNode; child; child = child->next)
|
|
{
|
|
switch (child->type)
|
|
{
|
|
case XML_COMMENT_NODE:
|
|
case XML_TEXT_NODE:
|
|
break;
|
|
case XML_ELEMENT_NODE:
|
|
if (!price_parse_xml_sub_node (p, child, pstatus->book))
|
|
{
|
|
ok = FALSE;
|
|
goto cleanup_and_exit;
|
|
}
|
|
break;
|
|
default:
|
|
PERR ("Unknown node type (%d) while parsing gnc-price xml.", child->type);
|
|
child = NULL;
|
|
ok = FALSE;
|
|
goto cleanup_and_exit;
|
|
break;
|
|
}
|
|
}
|
|
|
|
cleanup_and_exit:
|
|
if (ok)
|
|
{
|
|
*result = p;
|
|
}
|
|
else
|
|
{
|
|
*result = NULL;
|
|
gnc_price_unref (p);
|
|
}
|
|
xmlFreeNode (price_xml);
|
|
return ok;
|
|
}
|
|
|
|
static void
|
|
cleanup_gnc_price (sixtp_child_result* result)
|
|
{
|
|
if (result->data) gnc_price_unref ((GNCPrice*) result->data);
|
|
}
|
|
|
|
static sixtp*
|
|
gnc_price_parser_new (void)
|
|
{
|
|
return sixtp_dom_parser_new (price_parse_xml_end_handler,
|
|
cleanup_gnc_price,
|
|
cleanup_gnc_price);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* <pricedb> (lineage <ledger-data>)
|
|
|
|
restores a pricedb. We allocate the new db in the start block, the
|
|
children add to it, and it gets returned in result. Note that the
|
|
cleanup handler will destroy the pricedb, so the parent needs to
|
|
stop that if desired.
|
|
|
|
result: GNCPriceDB*
|
|
|
|
start: create new GNCPriceDB*, and leave in *data_for_children.
|
|
cleanup-result: destroy GNCPriceDB*
|
|
result-fail: destroy GNCPriceDB*
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
pricedb_start_handler (GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* data_for_children,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
gchar** attrs)
|
|
{
|
|
GNCParseStatus* pstatus = (GNCParseStatus*) global_data;
|
|
GNCPriceDB* db = gnc_pricedb_get_db (pstatus->book);
|
|
g_return_val_if_fail (db, FALSE);
|
|
*result = db;
|
|
return (TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
pricedb_after_child_handler (gpointer data_for_children,
|
|
GSList* data_from_children,
|
|
GSList* sibling_data,
|
|
gpointer parent_data,
|
|
gpointer global_data,
|
|
gpointer* result,
|
|
const gchar* tag,
|
|
const gchar* child_tag,
|
|
sixtp_child_result* child_result)
|
|
{
|
|
GNCPriceDB* db = (GNCPriceDB*) * result;
|
|
|
|
g_return_val_if_fail (db, FALSE);
|
|
|
|
/* right now children have to produce results :> */
|
|
if (!child_result) return (FALSE);
|
|
if (child_result->type != SIXTP_CHILD_RESULT_NODE) return (FALSE);
|
|
|
|
if (strcmp (child_result->tag, "price") == 0)
|
|
{
|
|
GNCPrice* p = (GNCPrice*) child_result->data;
|
|
|
|
g_return_val_if_fail (p, FALSE);
|
|
gnc_pricedb_add_price (db, p);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
pricedb_cleanup_result_handler (sixtp_child_result* result)
|
|
{
|
|
if (result->data)
|
|
{
|
|
GNCPriceDB* db = (GNCPriceDB*) result->data;
|
|
if (db) gnc_pricedb_destroy (db);
|
|
result->data = NULL;
|
|
}
|
|
}
|
|
|
|
static sixtp*
|
|
gnc_pricedb_parser_new (void)
|
|
{
|
|
sixtp* top_level;
|
|
sixtp* price_parser;
|
|
|
|
top_level =
|
|
sixtp_set_any (sixtp_new (), TRUE,
|
|
SIXTP_START_HANDLER_ID, pricedb_start_handler,
|
|
SIXTP_AFTER_CHILD_HANDLER_ID, pricedb_after_child_handler,
|
|
SIXTP_CHARACTERS_HANDLER_ID,
|
|
allow_and_ignore_only_whitespace,
|
|
SIXTP_RESULT_FAIL_ID, pricedb_cleanup_result_handler,
|
|
SIXTP_CLEANUP_RESULT_ID, pricedb_cleanup_result_handler,
|
|
SIXTP_NO_MORE_HANDLERS);
|
|
|
|
if (!top_level) return NULL;
|
|
|
|
price_parser = gnc_price_parser_new ();
|
|
|
|
if (!price_parser)
|
|
{
|
|
sixtp_destroy (top_level);
|
|
return NULL;
|
|
}
|
|
|
|
sixtp_add_sub_parser (top_level, "price", price_parser);
|
|
|
|
return top_level;
|
|
}
|
|
|
|
/* ======================= END OF FILE ============================== */
|