mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
This will avoid a ninja-build from picking up a config.h generated by the autotools build (in the root build directory). Picking up the wrong config.h may lead to all kinds of subtle issues if the autotools run was done with different options than the cmake run.
3196 lines
93 KiB
C++
3196 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;
|
|
guint64 key_node_count;
|
|
gchar* key = NULL;
|
|
sixtp_child_result* value_cr = NULL;
|
|
KvpValue* value = NULL;
|
|
gboolean delete_value = FALSE;
|
|
|
|
g_return_val_if_fail (f, FALSE);
|
|
|
|
if (g_slist_length (data_from_children) != 2) return (FALSE);
|
|
|
|
/* check to see that we got exactly one <key> node */
|
|
lp = data_from_children;
|
|
key_node_count = 0;
|
|
for (lp = data_from_children; lp; lp = lp->next)
|
|
{
|
|
sixtp_child_result* cr = (sixtp_child_result*) lp->data;
|
|
|
|
if (is_child_result_from_node_named (cr, "k"))
|
|
{
|
|
key = (char*) cr->data;
|
|
key_node_count++;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
value_cr = cr;
|
|
}
|
|
}
|
|
|
|
if (key_node_count != 1) return (FALSE);
|
|
|
|
value_cr->should_cleanup = TRUE;
|
|
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;
|
|
TimespecParseInfo* info = (TimespecParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (info, FALSE);
|
|
if (!t || !timespec_parse_ok (info))
|
|
{
|
|
g_free (info);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccTransSetDatePostedTS (t, & (info->ts));
|
|
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;
|
|
TimespecParseInfo* info = (TimespecParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (info, FALSE);
|
|
if (!t || !timespec_parse_ok (info))
|
|
{
|
|
g_free (info);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccTransSetDateEnteredTS (t, & (info->ts));
|
|
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;
|
|
TimespecParseInfo* info = (TimespecParseInfo*) data_for_children;
|
|
|
|
g_return_val_if_fail (info, FALSE);
|
|
if (!s || !timespec_parse_ok (info))
|
|
{
|
|
g_free (info);
|
|
return (FALSE);
|
|
}
|
|
|
|
xaccSplitSetDateReconciledTS (s, & (info->ts));
|
|
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);
|
|
g_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)
|
|
{
|
|
Timespec t = dom_tree_to_timespec (sub_node);
|
|
if (!dom_tree_valid_timespec (&t, sub_node->name)) return FALSE;
|
|
gnc_price_set_time (p, t);
|
|
}
|
|
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 ============================== */
|