Rob Browning's patch with price db & miscellany.

* first iteration of pricedb: we should preserve quotes from old
    binfiles now (rather than throwing it away during the conversion),
    and we can read/write the pricedb as XML.

  * added configure --enable-error-on-warning -- developers, please
    use this.

  * add fancier TAGS handling -- we now track file additions/deletions
    dynamically.

  * add g_hash_table_key_value_pairs: returns a GSList of all the key
    value pairs in a given hash table so you can manipulate them.

    add g_hash_table_kv_pair_free_gfunc: g_slist_foreach helper for
    deleting key value hash pairs when you're finished with the
    results from g_hash_table_key_value_pairs.  You'll still need to
    call g_slist_free as well to delete the spine of the list.

  * continue migration to using backend for all IO and switching from
    top-level Group to top level GNCBook.

  * switch from const gnc_commodity to gnc_commodity in many places,
    after consultation with Bill.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3684 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas 2001-02-25 00:42:44 +00:00
parent ce532a2a86
commit e30a8bc509
77 changed files with 3215 additions and 892 deletions

238
ChangeLog
View File

@ -1,3 +1,241 @@
2001-02-24 Rob Browning <rlb@cs.utexas.edu>
* configure.in (AC_ARG_ENABLE): add --enable-error-on-warning.
Enables -Werror and whatever else is needed for super-strict
checks. I'm compiling with this now, and I urge everyone else to
as well. We do have a few aggravating things we still ignore,
like unused variables, but as of today, the whole codebase
compiles successfully with this turned on.
(AC_CHECK_PROG): Add a check for etags.
(AC_ARG_WITH): --with-perl-includes - don't die if CORE not found.
I install modules in a local directory and use --perl-includes to
enable gnucash to find Finance::Quote at runtime, but there won't
be a CORE file.
(AC_CHECK_LIB): popt - note required Debian -dev package.
* macros/autogen.sh (conf_flags): omit --enable-compile-warnings.
This was adding flags that overrode our configure.in settings;
specifically, this kept --enable-error-on-warning from working.
* Makefile.am (noinst_DATA): add optional TAGS target.
(DISTCLEANFILES): add tags cleanup bits.
(TAGS): Add fancy TAGS handling. TAGS file is only built if you
have etags available, and now it takes into account
additions/deletions of files in addition to modifications.
* src/engine/gnc-engine-util.c
(g_hash_table_key_value_pairs): new - returns a GSList of all the
key value pairs in a given hash table so you can manipulate them.
(g_hash_table_kv_pair_free_gfunc): new - g_slist_foreach helper
for deleting key value hash pairs when you're finished with the
results from g_hash_table_key_value_pairs. You'll still need to
call g_slist_free as well to delete the spine of the list.
* src/engine/NetIO.c: comment out various bits of the code that's
incompatible with the recent gnc-book changes. Linas said he'd
fix it later.
* src/scm/price-quotes.scm: new file - not used yet.
* src/scm/price-quotes.scm: new file - launch a sub-process you
can talk to bidirectionally from scheme read/write.
* src/scm/command-line.scm: (disabled) support for getting prices.
* src/scm/command-line.scm
(gnc:group-map-accounts): implementation simplified dramatically,
probably should be dropped entirely, in favor of just calling (map
thunk (gnc:group-get-subaccounts group)) -- which is what's in the
replacement code.
* src/quotes/price-quote-helper.in: new file.
* src/Backend.h: Delete ERR_FILEIO_MISC (use ERR_BACKEND_MISC).
Replace ERR_FILEIO_ALLOC with ERR_BACKEND_ALLOC.
* src/engine/gnc-book.c: add support for GNCPriceDB element. Move
still relevant FileIO bits here. Add support for gnc-book
clean/dirty checking instead of just Group checking.
* src/engine/gnc-book.h: fixes for gnc-book marking, private
header, and addition of pricedb.
* src/engine/gnc-book-p.h: new file - private book functions.
* src/engine/Ledger-xml-parser-v1.c
(ledger_data_after_child_handler): new function to handle grabbing
the pricedb when we hit it.
* src/FileDialog.c: Various fixes to replace uses of the top level
AccountGroup with the parent GNCBook. Also add support for
reading/writing the GNCPriceDB. This file still needs a lot of
work. Much of it should become a proper file IO Backend.
* src/guile/gnc.gwp: Add <gnc:Book*>. Add gnc:get-current-book.
Add gnc:book-get-group. Add gnc:account-get-price-src.
* src/gnome/druid-commodity.c (finish_helper): add support for
GNCPriceDB.
* src/engine/sixtp-writers.h: add support for GNCPriceDB.
* src/engine/sixtp-to-dom-parser.c
(sixtp_dom_parser_new): change signature to add fail and result
cleanup functions. These can be NULL, in which case they're
ignored. Also modify overall scheme to clean internal garbage up
properly in case of failure.
* src/engine/sixtp-parsers.h
(sixtp_dom_parser_new): change signature.
* src/engine/sixtp-dom-parsers.c (dom_tree_to_text): minor fixes.
* src/engine/sixtp-dom-generators.c
(text_to_dom_tree): new function.
* src/engine/sixtp-dom-generators.h
(text_to_dom_tree): new prototype.
* src/engine/io-gncxml-w.c: add support for pricedb and using
GNCBook at top level rather than Group.
* src/engine/io-gncxml.h: support migration from top level Group
to top level GNCBook.
* src/engine/io-gncbin-r.c: add support for detecting legacy
prices and migrating them to the new pricedb. Replace top level
uses of a Group with a GNCBook.
* src/engine/io-gncbin.h: support pricedb and gnc-book changes.
* src/engine/io-gncxml-p.h: new file - private header.
* src/engine/io-gncxml-r.c: move bits to private header. Make
changes to support move from top level to Group to top level
GNCBook.
* src/gnome/druid-qif-import.c: -Werror fixes.
* src/engine/gnc-pricedb.h: new file.
* src/engine/gnc-pricedb-p.h: new file.
* src/engine/gnc-pricedb.c: new file.
* src/engine/gnc-pricedb-xml-v1.c: new file.
* src/engine/gnc-engine-util.h: accomodate new functions.
* src/engine/gnc-commodity-xml-v2.c
(gnc_commodity_sixtp_parser_create): fix sixtp_dom_parser_new call.
* src/engine/gnc-account-xml-v2.c
(gnc_account_end_handler): -Werror fixes.
(gnc_account_sixtp_parser_create): fix sixtp_dom_parser_new call.
* src/engine/date.h (timespec_equal): add back.
* src/engine/date.c (timespec_equal): add back.
* src/engine/Commodity-xml-parser-v1.c
(xml_add_commodity_ref): cleanup.
* src/FileIO.h: deleted.
* src/FileIOP.h: deleted.
* src/FileIO.c: deleted.
* src/test/test-dom-parser1.c: fixes for new sixtp_dom_parser_new.
* src/test/test-xml-account.c: fixes for new sixtp_dom_parser_new.
* src/test/test-xml-commodity.c: fixes for new sixtp_dom_parser_new.
* src/guile/option-util.h: remove consts from
gnc_commoditities (after consultation with Bill).
* src/guile/option-util.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/guile/guile-util.h: remove consts from
gnc_commoditities (after consultation with Bill).
* src/guile/guile-util.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/guile/global-options.h: remove consts from
gnc_commoditities (after consultation with Bill).
* src/guile/global-options.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/window-register.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/window-main.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/gnc-currency-edit.h: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/gnc-currency-edit.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/gnc-commodity-edit.h: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/gnc-commodity-edit.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/druid-commodity.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/dialog-account.c: remove consts from gnc_commoditities (after
consultation with Bill).
* src/gnome/dialog-options.c: remove consts from gnc_commoditities
(after consultation with Bill).
* src/gnome/dialog-commodity.h: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnome/dialog-commodity.c: remove consts from
gnc_commoditities (after consultation with Bill).
* src/gnc-ui-util.h: remove consts from gnc_commoditities (after
consultation with Bill).
* src/gnc-ui-util.c: remove consts from gnc_commoditities (after
consultation with Bill).
* src/engine/Transaction.h: remove consts from gnc_commoditities (after
consultation with Bill).
* src/engine/Transaction.c: remove consts from gnc_commoditities (after
consultation with Bill).
* src/engine/Scrub.c: remove consts from gnc_commoditities (after
consultation with Bill).
* src/AccountP.h: remove consts from gnc_commoditities (after
consultation with Bill).
* src/Account.h: remove consts from gnc_commoditities (after
consultation with Bill).
* src/Account.c: remove consts from gnc_commoditities (after
consultation with Bill).
* src/SplitLedger.c: remove consts from gnc_commoditities (after
consultation with Bill).
* src/EuroUtils.h: remove consts from gnc_commoditities (after
consultation with Bill).
* src/EuroUtils.c: remove consts from gnc_commoditities (after
consultation with Bill).
2001-02-23 Bill Gribble <grib@billgribble.com>
* src/scm/qif-import/qif-dialog-utils.scm: Be more flexible

View File

@ -3,7 +3,7 @@ SUBDIRS = macros debian doc-tools doc intl lib src po rpm accounts
docdir = ${GNC_DOC_INSTALL_DIR}
noinst_DATA = make-gnucash-patch
noinst_DATA = make-gnucash-patch @GNC_TAGS_FILE@
doc_DATA = \
AUTHORS \
@ -43,7 +43,8 @@ make-gnucash-patch: make-gnucash-patch.in
chmod +x $@.tmp
mv $@.tmp $@
DISTCLEANFILES += cscope.files cscope.out etags.files make-gnucash-patch
DISTCLEANFILES += \
TAGS.stamp cscope.files cscope.out etags.files make-gnucash-patch
cscope.files:
find . -name '*.[ch]' > cscope.files
@ -51,8 +52,24 @@ cscope.files:
cscope.out: cscope.files
cscope -b
TAGS: etags.files
etags `cat etags.files`
if GNC_TAGS_FILE
etags.files:
find . -name '*.[ch]' -o -name '*.scm' > etags.files
TAGS.stamp: etags.files $(shell cat etags.files)
etags `cat etags.files`
touch TAGS.stamp
TAGS:
find . -name '*.[ch]' -o -name '*.scm' | sort > etags.files.tmp
@if cmp --quiet etags.files etags.files.tmp; \
then \
echo "TAGS file list hasn't changed."; \
rm -f etags.files.tmp; \
else \
echo "TAGS file list has changed."; \
mv etags.files.tmp etags.files; \
fi
${MAKE} TAGS.stamp
.PHONY: TAGS
endif

View File

@ -126,9 +126,18 @@ AC_SUBST(GNC_ACCOUNTS_DIR)
# We should always see these errors...
CFLAGS="${CFLAGS} -Wall"
AC_ARG_ENABLE(error-on-warning,
[ --enable-error-on-warning treat compile warnings as errors],
[case "${enableval}" in
yes) CFLAGS="${CFLAGS} -Werror"; enable_compile_warnings=no ;;
no) ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-error-on-warning) ;;
esac])
# This has to come after AC_PROG_CC
if test ${GCC}x = yesx
then
CFLAGS="${CFLAGS} -Wno-unused"
CFLAGS="${CFLAGS} -Werror-implicit-function-declaration"
fi
@ -173,11 +182,15 @@ AC_ARG_WITH( help-prefix,
AC_SUBST(GNC_HELPDIR)
### --------------------------------------------------------------------------
### Check for etags
AC_CHECK_PROG(GNC_TAGS_FILE, etags, TAGS)
AM_CONDITIONAL(GNC_TAGS_FILE, test x${GNC_TAGS_FILE} = xTAGS)
### --------------------------------------------------------------------------
### Check for glade
# Check for glade
AC_ARG_WITH(glade,
[ --with-glade=FILE which glade executable to use ],
GLADE="${with_glade}")
@ -233,10 +246,6 @@ PERLINCL=`$PERL -MConfig -e 'print $Config{"archlibexp"}'`
AC_ARG_WITH( perl-includes,
[ --with-perl-includes=DIR specify where to look for perl includes],
PERLINCL="$with_perl_includes" )
if test ! -d ${PERLINCL}/CORE; then
AC_MSG_ERROR([Missing directory ${PERLINCL}/CORE in the perl include directory])
fi
AC_SUBST(PERLINCL)
@ -390,11 +399,12 @@ AC_SUBST(GTK_XIM_FLAGS)
### --------------------------------------------------------------------------
### popt
AC_CHECK_LIB(popt, poptStrippedArgv,, AC_MSG_ERROR([
AC_CHECK_LIB(popt, poptStrippedArgv,, [AC_MSG_ERROR([
popt 1.5 or newer is required to build gnucash. You can download
the latest version from ftp://people.redhat.com/sopwith/popt/
]))
the latest version from ftp://people.redhat.com/sopwith/popt/, or if
you're running Debian, install the libpopt-dev pacakge.
])])
### --------------------------------------------------------------------------
## For now, we just presume you're using the GNOME version. The other

View File

@ -168,7 +168,7 @@ do
fi
done
conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
conf_flags="--enable-maintainer-mode" # --enable-compile-warnings --enable-iso-c
if test x$NOCONFIGURE = x; then
echo Running $srcdir/configure $conf_flags "$@" ...

View File

@ -67,8 +67,7 @@ static gnc_euro_rate_struct _gnc_euro_rate_[] =
};
static int
_gnc_euro_rate_compare_(const void * key,
const void * value)
_gnc_euro_rate_compare_(const void * key, const void * value)
{
const gnc_commodity * curr = key;
const gnc_euro_rate_struct * euro = value;
@ -83,7 +82,8 @@ _gnc_euro_rate_compare_(const void * key,
/* ------------------------------------------------------ */
gboolean
gnc_is_euro_currency(const gnc_commodity * currency) {
gnc_is_euro_currency(gnc_commodity * currency)
{
gnc_euro_rate_struct *result;
const char *namespace;
@ -113,7 +113,7 @@ gnc_is_euro_currency(const gnc_commodity * currency) {
/* ------------------------------------------------------ */
gnc_numeric
gnc_convert_to_euro(const gnc_commodity * currency, gnc_numeric value) {
gnc_convert_to_euro(gnc_commodity * currency, gnc_numeric value) {
gnc_euro_rate_struct *result;
const char *namespace;
@ -150,7 +150,7 @@ gnc_convert_to_euro(const gnc_commodity * currency, gnc_numeric value) {
/* ------------------------------------------------------ */
gnc_numeric
gnc_convert_from_euro(const gnc_commodity * currency, gnc_numeric value) {
gnc_convert_from_euro(gnc_commodity * currency, gnc_numeric value) {
gnc_euro_rate_struct * result;
const char *namespace;
@ -186,7 +186,7 @@ gnc_convert_from_euro(const gnc_commodity * currency, gnc_numeric value) {
/* ------------------------------------------------------ */
const gnc_commodity *
gnc_commodity *
gnc_get_euro (void)
{
return gnc_commodity_table_lookup (gnc_engine_commodities (),

View File

@ -27,13 +27,13 @@
#include "gnc-commodity.h"
#include "gnc-numeric.h"
gboolean gnc_is_euro_currency (const gnc_commodity * currency);
gnc_numeric gnc_convert_to_euro (const gnc_commodity * currency,
gboolean gnc_is_euro_currency (gnc_commodity * currency);
gnc_numeric gnc_convert_to_euro (gnc_commodity * currency,
gnc_numeric value);
gnc_numeric gnc_convert_from_euro (const gnc_commodity * currency,
gnc_numeric gnc_convert_from_euro (gnc_commodity * currency,
gnc_numeric value);
const gnc_commodity * gnc_get_euro (void);
gnc_commodity * gnc_get_euro (void);
#endif /* __EURO_UTILS_H__ */

View File

@ -28,7 +28,6 @@
#include "Backend.h"
#include "FileBox.h"
#include "FileDialog.h"
#include "FileIO.h"
#include "Group.h"
#include "file-history.h"
#include "gnc-component-manager.h"
@ -37,6 +36,8 @@
#include "gnc-ui.h"
#include "messages.h"
/* FIXME: this is wrong. This file should not need this include. */
#include "gnc-book-p.h"
/** GLOBALS *********************************************************/
/* This static indicates the debugging module that this .o belongs to. */
@ -113,11 +114,6 @@ show_book_error (GNCBackendError io_error, const char *newfile)
if (gnc_verify_dialog (fmt, TRUE)) { uh_oh = FALSE; }
break;
case ERR_FILEIO_MISC:
fmt = _("There was an error during file I/O.");
gnc_error_dialog (fmt);
break;
case ERR_SQL_BAD_LOCATION:
fmt = _("Can't parse the database URL\n %s\n");
buf = g_strdup_printf (fmt, newfile);
@ -192,7 +188,6 @@ void
gncFileNew (void)
{
GNCBook *book;
AccountGroup *group;
/* If user attempts to start a new session before saving results of
* the last one, prompt them to clean up their act. */
@ -200,7 +195,6 @@ gncFileNew (void)
return;
book = gncGetCurrentBook ();
group = gnc_book_get_group (book);
/* close any ongoing file sessions, and free the accounts.
* disable events so we don't get spammed by redraws. */
@ -221,14 +215,8 @@ gncFileNew (void)
gboolean
gncFileQuerySave (void)
{
GNCBook *book;
AccountGroup *group;
gncUIWidget app;
book = gncGetCurrentBook ();
group = gnc_book_get_group (book);
app = gnc_get_ui_data ();
GNCBook *book = gncGetCurrentBook();
gncUIWidget app = gnc_get_ui_data();
/* If user wants to mess around before finishing business with
* the old file, give em a chance to figure out what's up.
@ -236,8 +224,7 @@ gncFileQuerySave (void)
* up the file-selection dialog, we don't blow em out of the water;
* instead, give them another chance to say "no" to the verify box.
*/
while (xaccGroupNotSaved (group))
{
while (gnc_book_not_saved(book)) {
GNCVerifyResult result;
const char *message = _("Changes have been made since the last "
"Save. Save the data to file?");
@ -386,7 +373,6 @@ gncPostFileOpen (const char * filename)
gh_eval_str("gnc:*file-opened-hook*"),
gh_str02scm(newfile));
}
g_free (newfile);
gnc_engine_resume_events ();
@ -469,7 +455,7 @@ gncFileSave (void)
gnc_history_add_file (newfile);
xaccGroupMarkSaved (gnc_book_get_group (book));
gnc_book_mark_saved(book);
LEAVE (" ");
}
@ -479,6 +465,7 @@ void
gncFileSaveAs (void)
{
AccountGroup *group;
GNCPriceDB *pdb;
GNCBook *new_book;
GNCBook *book;
const char *filename;
@ -510,7 +497,8 @@ gncFileSaveAs (void)
}
/* -- this session code is NOT identical in FileOpen and FileSaveAs -- */
group = gnc_book_get_group (book);
group = gnc_book_get_group(book);
pdb = gnc_book_get_pricedb(book);
new_book = gnc_book_new ();
gnc_book_begin (new_book, newfile, FALSE, FALSE);
@ -552,7 +540,8 @@ gncFileSaveAs (void)
/* if we got to here, then we've successfully gotten a new session */
/* close up the old file session (if any) */
gnc_book_set_group (book, NULL);
gnc_book_set_group(book, NULL);
gnc_book_set_pricedb(book, NULL);
gnc_book_destroy (book);
current_book = new_book;
@ -581,7 +570,8 @@ gncFileSaveAs (void)
}
/* OK, save the data to the file ... */
gnc_book_set_group (new_book, group);
gnc_book_set_group(new_book, group);
gnc_book_set_pricedb(new_book, pdb);
gncFileSave ();
g_free (newfile);

View File

@ -426,7 +426,7 @@ gnc_find_split_in_trans_by_memo (Transaction *trans, const char *memo,
if (safe_strcmp(memo, xaccSplitGetMemo(split)) == 0)
{
Account *account = xaccSplitGetAccount(split);
const gnc_commodity *currency, *security;
gnc_commodity *currency, *security;
if (account == NULL)
return split;
@ -611,7 +611,7 @@ gnc_split_get_value_denom (Split *split)
denom = xaccAccountGetCurrencySCU (xaccSplitGetAccount (split));
if (denom == 0)
{
const gnc_commodity *commodity = gnc_locale_default_currency ();
gnc_commodity *commodity = gnc_locale_default_currency ();
denom = gnc_commodity_get_fraction (commodity);
if (denom == 0)
denom = 100;
@ -628,7 +628,7 @@ gnc_split_get_quantity_denom (Split *split)
denom = xaccAccountGetSecuritySCU (xaccSplitGetAccount (split));
if (denom == 0)
{
const gnc_commodity *commodity = gnc_locale_default_currency ();
gnc_commodity *commodity = gnc_locale_default_currency ();
denom = gnc_commodity_get_fraction (commodity);
if (denom == 0)
denom = 100;
@ -660,7 +660,7 @@ sr_set_cell_fractions (SplitRegister *reg, Split *split)
}
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
int fraction;
xaccSetPriceCellFraction (reg->sharesCell, 10000);
@ -3245,8 +3245,8 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
if ((new_acc != NULL) && (old_acc != new_acc))
{
const gnc_commodity * currency = NULL;
const gnc_commodity * security = NULL;
gnc_commodity * currency = NULL;
gnc_commodity * security = NULL;
currency = xaccAccountGetCurrency(new_acc);
currency = xaccTransIsCommonExclSCurrency(trans, currency, split);
@ -3319,8 +3319,8 @@ xaccSRSaveChangedCells (SplitRegister *reg, Transaction *trans, Split *split)
if ((new_acc != NULL) && (old_acc != new_acc))
{
const gnc_commodity * currency = NULL;
const gnc_commodity * security = NULL;
gnc_commodity * currency = NULL;
gnc_commodity * security = NULL;
currency = xaccAccountGetCurrency(new_acc);
currency = xaccTransIsCommonExclSCurrency(trans,
@ -4831,8 +4831,8 @@ xaccSRLoadRegister (SplitRegister *reg, GList * slist,
static void
LoadXferCell (ComboCell * cell,
AccountGroup * grp,
const gnc_commodity * base_currency,
const gnc_commodity * base_security)
gnc_commodity * base_currency,
gnc_commodity * base_security)
{
gboolean load_everything;
GList *list;
@ -4853,8 +4853,8 @@ LoadXferCell (ComboCell * cell,
for (node = list; node; node = node->next)
{
Account *account = node->data;
const gnc_commodity * curr;
const gnc_commodity * secu;
gnc_commodity * curr;
gnc_commodity * secu;
char *name;
curr = xaccAccountGetCurrency (account);
@ -4891,7 +4891,7 @@ xaccLoadXferCell (ComboCell *cell,
AccountGroup *grp,
Account *base_account)
{
const gnc_commodity * curr, * secu;
gnc_commodity * curr, * secu;
curr = xaccAccountGetCurrency (base_account);
secu = xaccAccountGetSecurity (base_account);

View File

@ -132,14 +132,14 @@ account_restore_end_handler(gpointer data_for_children,
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)
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;
g_return_val_if_fail(a, FALSE);
@ -172,12 +172,12 @@ account_restore_after_child_handler(gpointer data_for_children,
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)
GSList* data_from_children,
GSList* sibling_data,
gpointer parent_data,
gpointer global_data,
gpointer *result,
const gchar *tag)
{
Account *acc = (Account *) *result;
if(acc) xaccFreeAccount(acc);

View File

@ -1147,7 +1147,7 @@ update_split_currency(Account * acc)
*/
void
xaccAccountSetCommodity (Account * acc, const gnc_commodity * com)
xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
{
if ((!acc) || (!com)) return;
@ -1180,7 +1180,7 @@ xaccAccountSetCommodity (Account * acc, const gnc_commodity * com)
/* below follow the old, deprecated currency/security routines. */
void
xaccAccountSetCurrency (Account * acc, const gnc_commodity * currency) {
xaccAccountSetCurrency (Account * acc, gnc_commodity * currency) {
if ((!acc) || (!currency)) return;
@ -1200,7 +1200,7 @@ xaccAccountSetCurrency (Account * acc, const gnc_commodity * currency) {
}
void
xaccAccountSetSecurity (Account *acc, const gnc_commodity * security) {
xaccAccountSetSecurity (Account *acc, gnc_commodity * security) {
if ((!acc) || (!security)) return;
@ -1383,14 +1383,14 @@ xaccAccountGetNotes (Account *acc)
return(NULL);
}
const gnc_commodity *
gnc_commodity *
xaccAccountGetCurrency (Account *acc)
{
if (!acc) return NULL;
return (acc->currency);
}
const gnc_commodity *
gnc_commodity *
xaccAccountGetEffectiveSecurity (Account *acc)
{
if (!acc) return NULL;
@ -1401,7 +1401,7 @@ xaccAccountGetEffectiveSecurity (Account *acc)
return (acc->security);
}
const gnc_commodity *
gnc_commodity *
xaccAccountGetSecurity (Account *account)
{
if (!account) return NULL;

View File

@ -215,23 +215,23 @@ const char * xaccAccountGetNotes (Account *account);
* amount of the Account's commodity involved) into the Transaction's
* balancing currency. */
#define xaccAccountGetCommodity xaccAccountGetEffectiveSecurity
void xaccAccountSetCommodity (Account *account, const gnc_commodity *comm);
void xaccAccountSetCommodity (Account *account, gnc_commodity *comm);
/* Soon-to-be-deprecated currency/security access routines.
* The future API will associate only one thing with an account:
* the 'commodity'. Use xaccAccountGetCommodity() to fetch it.
*/
/* these two funcs take control of thier gnc_commodity args. Don't free */
void xaccAccountSetCurrency (Account *account, const gnc_commodity *currency);
void xaccAccountSetSecurity (Account *account, const gnc_commodity *security);
void xaccAccountSetCurrency (Account *account, gnc_commodity *currency);
void xaccAccountSetSecurity (Account *account, gnc_commodity *security);
void xaccAccountSetCurrencySCU (Account *account, int frac);
void xaccAccountSetSecuritySCU (Account *account, int frac);
int xaccAccountGetCurrencySCU (Account *account);
int xaccAccountGetSecuritySCU (Account *account);
const gnc_commodity * xaccAccountGetCurrency (Account *account);
const gnc_commodity * xaccAccountGetSecurity (Account *account);
const gnc_commodity * xaccAccountGetEffectiveSecurity (Account *account);
gnc_commodity * xaccAccountGetCurrency (Account *account);
gnc_commodity * xaccAccountGetSecurity (Account *account);
gnc_commodity * xaccAccountGetEffectiveSecurity (Account *account);
AccountGroup * xaccAccountGetChildren (Account *account);
AccountGroup * xaccAccountGetParent (Account *account);

View File

@ -111,8 +111,8 @@ struct _account {
* messages will print to the screen if things don't go well.
*/
const gnc_commodity * currency;
const gnc_commodity * security;
gnc_commodity * currency;
gnc_commodity * security;
int currency_scu;
int security_scu;

View File

@ -25,6 +25,7 @@ typedef enum {
/* or no backend handler (ENOSYS) */
ERR_BACKEND_LOCKED, /* in use by another user (ETXTBSY) */
ERR_BACKEND_NO_SUCH_DB, /* the named database doesn't exist */
ERR_BACKEND_ALLOC, /* internal memory allocation failure */
ERR_BACKEND_MISC, /* undetermined error */
/* fileio errors */
@ -34,8 +35,6 @@ typedef enum {
ERR_FILEIO_FILE_NOT_FOUND, /* not found / no such file */
ERR_FILEIO_FILE_TOO_NEW, /* file version newer than what we can read */
ERR_FILEIO_FILE_TOO_OLD, /* file version so old we can't read it */
ERR_FILEIO_ALLOC, /* ?? */
ERR_FILEIO_MISC, /* unknown weird error */
/* network errors */
ERR_NETIO_NO_CONNECTION, /* network failure, can't connect to server */

View File

@ -95,14 +95,14 @@ commodity_restore_start_handler(GSList* sibling_data, gpointer parent_data,
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)
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;
@ -367,31 +367,37 @@ generic_gnc_commodity_lookup_parser_new(void)
/* WRITING */
gboolean
xml_add_commodity_ref(xmlNodePtr p, const char *tag, const gnc_commodity *c) {
xml_add_commodity_ref(xmlNodePtr p, const char *tag, const gnc_commodity *c)
{
xmlNodePtr c_xml = NULL;
gboolean ok = FALSE;
xmlNodePtr tmp_xml = NULL;
const gchar *namestr;
const gchar *idstr;
if(p && tag) {
if(!c) {
ok = TRUE;
} else {
c_xml= xmlNewTextChild(p, NULL, tag, NULL);
if(c_xml) {
const gchar *namestr = gnc_commodity_get_namespace(c);
if(namestr) {
xmlNodePtr namespace_xml = xmlNewTextChild(c_xml, NULL, "space", namestr);
if(namespace_xml) {
const gchar *idstr = gnc_commodity_get_mnemonic(c);
xmlNodePtr id_xml = xmlNewTextChild(c_xml, NULL, "id", idstr);
if(id_xml) ok = TRUE;
}
}
}
}
}
if (!(p && tag)) return FALSE;
if (!c) return TRUE;
namestr = gnc_commodity_get_namespace(c);
idstr = gnc_commodity_get_mnemonic(c);
if(!ok && c_xml) xmlFreeNode(c_xml);
return(TRUE);
if(!(namestr && idstr)) return FALSE;
c_xml= xmlNewNode(NULL, tag);
if(!c_xml) return FALSE;
tmp_xml = xmlNewTextChild(c_xml, NULL, "space", namestr);
if(!tmp_xml) {
xmlFreeNode(c_xml);
return FALSE;
}
tmp_xml = xmlNewTextChild(c_xml, NULL, "id", idstr);
if(!tmp_xml) {
xmlFreeNode(c_xml);
return FALSE;
}
xmlAddChild(p, c_xml);
return TRUE;
}
/* ============================================================== */

View File

@ -1,115 +0,0 @@
/********************************************************************\
* FileIO.c -- read and write file wrappers (old and new format) *
* Copyright (C) 1997-2000 Linas Vepstas <linas@linas.org> *
* Copyright (C) 1999-2000 Rob Browning *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#include <stdlib.h>
#include <string.h>
#include "DateUtils.h"
#include "FileIO.h"
#include "io-gncxml.h"
#include "io-gncbin.h"
AccountGroup *
xaccReadAccountGroupFile(const gchar *name, GNCBackendError *error_result)
{
AccountGroup *result_grp;
if(is_gncxml_file(name)) {
if(gncxml_read(name, &result_grp)) {
if(error_result) *error_result = ERR_BACKEND_NO_ERR;
return result_grp;
} else {
if(error_result) *error_result = ERR_FILEIO_MISC;
return NULL;
}
} else {
/* presume it's an old-style binary file */
result_grp = xaccReadGncBinAccountGroupFile(name);
if(result_grp) {
if(error_result) *error_result = ERR_BACKEND_NO_ERR;
return result_grp;
} else {
if(error_result) *error_result = xaccGetGncBinFileIOError();
return NULL;
}
}
/* Should never get here */
if(error_result) *error_result = ERR_FILEIO_MISC;
return NULL;
}
gboolean
xaccWriteAccountGroupFile(const char *datafile,
AccountGroup *grp,
gboolean make_backup,
GNCBackendError *error_result)
{
if(!datafile) {
if(error_result) *error_result = ERR_FILEIO_FILE_NOT_FOUND;
return FALSE;
}
if(!gncxml_write(grp, datafile)) {
if(error_result) *error_result = ERR_FILEIO_MISC;
return FALSE;
}
if(!make_backup) {
if(error_result) *error_result = ERR_BACKEND_NO_ERR;
return TRUE;
} else {
char * timestamp;
int filenamelen;
char * backup;
/* also, write a time-stamped backup file */
/* tag each filename with a timestamp */
timestamp = xaccDateUtilGetStampNow ();
filenamelen = strlen (datafile) + strlen (timestamp) + 6;
backup = (char *) malloc (filenamelen);
strcpy (backup, datafile);
strcat (backup, ".");
strcat (backup, timestamp);
strcat (backup, ".xac");
free (timestamp);
if(gncxml_write(grp, backup)) {
if(error_result) *error_result = ERR_BACKEND_NO_ERR;
free (backup);
return TRUE;
} else {
if(error_result) *error_result = ERR_FILEIO_MISC;
free (backup);
return FALSE;
}
}
/* Should never get here */
if(error_result) *error_result = ERR_FILEIO_MISC;
return FALSE;
}

View File

@ -1,67 +0,0 @@
/********************************************************************\
* FileIO.h -- read and write binary format file for gnucash *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998, 1999 Linas Vepstas *
* Copyright (C) 1999, 2000 Rob Browning *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
* Author: Rob Clark *
* Internet: rclark@cs.hmc.edu *
* Address: 609 8th Street *
* Huntington Beach, CA 92648-4632 *
\********************************************************************/
#ifndef __FILE_IO_H__
#define __FILE_IO_H__
#include "Backend.h"
#include "Group.h"
/*
These function read/write the data in an AccountGroup to a file.
The read functions will automatically detect the format of the file
if possible. The write functions write the file in the "current"
format. These days, that means XML.
The read functions return NULL on error, and set the error parameter
(if it's not NULL) to indicate what went wrong.
The write functions return FALSE on error and set the error
parameter similarly.
In most cases, these functions should not be used directly. They
are not "safe" against file-locking errors. Use the Session object
instead.
*/
AccountGroup *xaccReadAccountGroupFile(const char *datafile,
GNCBackendError *error);
gboolean xaccWriteAccountGroupFile(const char *datafile,
AccountGroup *grp,
gboolean make_backup,
GNCBackendError *error);
/* If make_backup is true, write out a time-stamped copy of the file
into the same directory as the indicated file, with a filename of
"file.YYYYMMDDHHMMSS.xac" where YYYYMMDDHHMMSS is replaced with the
current year/month/day/hour/minute/second. */
#endif /* __XACC_FILEIO_H__ */

View File

@ -70,6 +70,7 @@ void xaccGroupMergeAccounts (AccountGroup *grp);
gboolean xaccGroupNotSaved (AccountGroup *grp);
void xaccGroupMarkSaved (AccountGroup *grp);
void xaccGroupMarkNotSaved (AccountGroup *grp);
void xaccGroupMarkDoFree (AccountGroup *grp);
/*

View File

@ -1,12 +1,21 @@
#include "config.h"
#include <string.h>
#include "gnc-engine-util.h"
#include "gnc-pricedb.h"
#include "io-gncxml-p.h"
#include "sixtp.h"
#include "sixtp-parsers.h"
#include "sixtp-utils.h"
#include "Group.h"
#include "TransLog.h"
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_IO;
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
@ -47,6 +56,38 @@ ledger_data_start_handler(GSList* sibling_data, gpointer parent_data,
return(ag != 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,
@ -111,6 +152,7 @@ ledger_data_parser_new(void)
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,
@ -123,6 +165,7 @@ ledger_data_parser_new(void)
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(),
0))

View File

@ -11,7 +11,6 @@ libgncengine_la_SOURCES = \
Account.c \
Backend.c \
DateUtils.c \
FileIO.c \
Group.c \
NetIO.c \
Query.c \
@ -35,6 +34,8 @@ libgncengine_la_SOURCES = \
gnc-engine-util.c \
gnc-event.c \
gnc-numeric.c \
gnc-pricedb.c \
gnc-pricedb-xml-v1.c \
sixtp-dom-generators.c \
sixtp-dom-parsers.c \
sixtp-kvp-parser.c \
@ -56,8 +57,6 @@ noinst_HEADERS = \
AccountP.h \
BackendP.h \
DateUtils.h \
FileIO.h \
FileIOP.h \
GNCId.h \
GNCIdP.h \
Group.h \
@ -83,6 +82,7 @@ noinst_HEADERS = \
gnc-event.h \
gnc-event-p.h \
gnc-numeric.h \
gnc-pricedb.h \
gnc-xml-helper.h \
gnc-xml.h \
sixtp-dom-generators.h \

View File

@ -59,8 +59,10 @@ struct _xmlend {
Backend *xmlendNew (void);
#if 0
/* ==================================================================== */
/* Perform vaious validty checks on the reply:
/* Perform various validty checks on the reply:
* -- was the content type text/gnc-xml ?
* -- was there a reply body, of positive length?
* -- did the body appear to contain gnc xml data?
@ -202,7 +204,8 @@ xmlbeBookLoad (Backend *bend)
if (0 >= len) return NULL;
bufp = ghttp_get_body(request);
grp = gncxml_read_from_buf ((char *)bufp, len);
grp = gnc_book_load_from_buf ((char *)bufp, len);
LEAVE(" ");
return grp;
@ -272,9 +275,12 @@ xmlbeBookEnd (Backend *b)
/* ==================================================================== */
#endif
Backend *
xmlendNew (void)
{
#if 0
XMLBackend *be;
be = (XMLBackend *) malloc (sizeof (XMLBackend));
@ -300,7 +306,8 @@ xmlendNew (void)
be->query_url = NULL;
return (Backend *) be;
#endif
return NULL;
}
/* ============================== END OF FILE ======================== */

View File

@ -375,7 +375,7 @@ static Account *
GetOrMakeAccount (AccountGroup *root, Transaction *trans,
const char *name_root)
{
const gnc_commodity * currency;
gnc_commodity * currency;
char * accname;
Account * acc;

View File

@ -1049,9 +1049,10 @@ xaccIsCommonCurrency(const gnc_commodity * currency_1,
return (c1c2 == 1) || (c1s2 == 1) || (s1c2 == 1) || (s1s2 == 1);
}
static const gnc_commodity *
FindCommonExclSCurrency (GList *splits, const gnc_commodity * ra,
const gnc_commodity * rb, Split *excl_split)
static gnc_commodity *
FindCommonExclSCurrency (GList *splits,
gnc_commodity * ra, gnc_commodity * rb,
Split *excl_split)
{
GList *node;
@ -1060,7 +1061,7 @@ FindCommonExclSCurrency (GList *splits, const gnc_commodity * ra,
for (node = splits; node; node = node->next)
{
Split *s = node->data;
const gnc_commodity * sa, * sb;
gnc_commodity * sa, * sb;
if (s == excl_split)
continue;
@ -1113,17 +1114,16 @@ FindCommonExclSCurrency (GList *splits, const gnc_commodity * ra,
* don't exclude one split from the splitlist when looking for a
* common currency.
*/
static const gnc_commodity *
FindCommonCurrency (GList *splits,
const gnc_commodity * ra, const gnc_commodity * rb)
static gnc_commodity *
FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb)
{
return FindCommonExclSCurrency(splits, ra, rb, NULL);
}
const gnc_commodity *
gnc_commodity *
xaccTransFindCommonCurrency (Transaction *trans)
{
const gnc_commodity *ra, *rb, *retval;
gnc_commodity *ra, *rb, *retval;
Split *split;
if (!trans) return NULL;
@ -1161,16 +1161,16 @@ xaccTransFindCommonCurrency (Transaction *trans)
return retval;
}
const gnc_commodity *
xaccTransIsCommonCurrency (Transaction *trans, const gnc_commodity * ra)
gnc_commodity *
xaccTransIsCommonCurrency (Transaction *trans, gnc_commodity * ra)
{
if (!trans) return NULL;
return FindCommonCurrency (trans->splits, ra, NULL);
}
const gnc_commodity *
gnc_commodity *
xaccTransIsCommonExclSCurrency (Transaction *trans,
const gnc_commodity * ra,
gnc_commodity * ra,
Split *excl_split)
{
if (!trans) return NULL;
@ -1182,7 +1182,7 @@ xaccTransIsCommonExclSCurrency (Transaction *trans,
/* The new routine for setting the common currency */
void
xaccTransSetCurrency (Transaction *trans, const gnc_commodity *curr)
xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr)
{
if (!trans || !curr) return;

View File

@ -253,8 +253,7 @@ int xaccTransCountSplits (Transaction *trans);
* */
#define xaccTransGetCurrency xaccTransFindCommonCurrency
void xaccTransSetCurrency (Transaction *trans,
const gnc_commodity *curr);
void xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr);
/* ---------
* and now for the 'old' routines
@ -281,7 +280,7 @@ gboolean xaccIsCommonCurrency(const gnc_commodity * currency_1,
* If all of the splits share both a common security and a common currency,
* then the string name for the currency is returned.
*/
const gnc_commodity * xaccTransFindCommonCurrency (Transaction *trans);
gnc_commodity * xaccTransFindCommonCurrency (Transaction *trans);
/* The xaccTransIsCommonCurrency () method compares the input commodity
* to the currency/security denominations of all splits in the
@ -299,8 +298,8 @@ const gnc_commodity * xaccTransFindCommonCurrency (Transaction *trans);
* common. This routine is useful in dealing with securities of
* differing types as they are moved across accounts.
*/
const gnc_commodity * xaccTransIsCommonCurrency(Transaction *trans,
const gnc_commodity * curr);
gnc_commodity * xaccTransIsCommonCurrency(Transaction *trans,
gnc_commodity *curr);
/* The xaccTransIsCommonExclSCurrency () method compares the input
* string to the currency/security denominations of all splits in
@ -312,9 +311,9 @@ const gnc_commodity * xaccTransIsCommonCurrency(Transaction *trans,
* that split is of no relevance when determining whether the new
* entry has a common currency with the other splits.
*/
const gnc_commodity *
gnc_commodity *
xaccTransIsCommonExclSCurrency (Transaction *trans,
const gnc_commodity * currency,
gnc_commodity * currency,
Split *excl_split);
/* The xaccTransGetImbalance() method returns the total value of the

View File

@ -51,13 +51,23 @@ static short module = MOD_ENGINE;
/********************************************************************\
\********************************************************************/
int
timespec_cmp (const Timespec *ta, const Timespec *tb)
gboolean
timespec_equal (const Timespec *ta, const Timespec *tb)
{
if(ta == tb) return TRUE;
if(ta->tv_sec != tb->tv_sec) return FALSE;
if(ta->tv_nsec != tb->tv_nsec) return FALSE;
return TRUE;
}
gint
timespec_cmp(const Timespec *ta, const Timespec *tb)
{
if(ta == tb) return 0;
if(ta->tv_sec < tb->tv_sec) return -1;
if(ta->tv_sec > tb->tv_sec) return +1;
if(ta->tv_sec > tb->tv_sec) return 1;
if(ta->tv_nsec < tb->tv_nsec) return -1;
if(ta->tv_nsec > tb->tv_nsec) return +1;
if(ta->tv_nsec > tb->tv_nsec) return 1;
return 0;
}

View File

@ -77,10 +77,10 @@ typedef struct timespec64 Timespec;
/** Prototypes ******************************************************/
/* The timespec_cmp() routine returns 0 if ta equals tb,
* -1 if ta<tb, and +1 if ta>tb
*/
int timespec_cmp (const Timespec *ta, const Timespec *tb);
/* strict equality */
gboolean timespec_equal(const Timespec *ta, const Timespec *tb);
/* comparison: if (ta < tb) -1; else if (ta > tb) 1; else 0; */
int timespec_cmp(const Timespec *ta, const Timespec *tb);
void setDateFormat(DateFormat df);

View File

@ -272,7 +272,7 @@ gnc_account_end_handler(gpointer data_for_children,
}
}
xmlFreeNode(result);
xmlFreeNode((xmlNodePtr) result);
return successful;
}
@ -280,5 +280,5 @@ gnc_account_end_handler(gpointer data_for_children,
sixtp*
gnc_account_sixtp_parser_create(void)
{
return sixtp_dom_parser_new(gnc_account_end_handler);
return sixtp_dom_parser_new(gnc_account_end_handler, NULL, NULL);
}

View File

@ -1,8 +1,5 @@
/********************************************************************\
* FileIOP.h -- private header for binary file i/o *
* datafile for gnucash (X-Accountant) *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998, 1999 Linas Vepstas *
* gnc-book-p.h -- private functions for gnc books. *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
@ -20,11 +17,25 @@
* Free Software Foundation Voice: +1-617-542-5942 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
\********************************************************************/
#ifndef __XACC_FILEIO_P_H__
#define __XACC_FILEIO_P_H__
/*
* HISTORY:
* Created 2001 by Rob Browning
* Copyright (c) 2001 Rob Browning
*/
#ifndef __GNC_BOOK_P_H__
#define __GNC_BOOK_P_H__
#include "gnc-book.h"
#include "gnc-pricedb.h"
#include "Group.h"
#endif /* __XACC_FILEIO_P_H__ */
void gnc_book_set_group(GNCBook *book, AccountGroup *grp);
void gnc_book_set_pricedb(GNCBook *book, GNCPriceDB *db);
void gnc_book_mark_saved(GNCBook *book);
#endif /* __GNC_BOOK_P_H__ */

View File

@ -44,12 +44,19 @@
#include "Backend.h"
#include "BackendP.h"
#include "FileIO.h"
#include "Group.h"
#include "NetIO.h"
#include "Scrub.h"
#include "TransLog.h"
#include "gnc-engine-util.h"
#include "gnc-pricedb-p.h"
#include "DateUtils.h"
#include "io-gncxml.h"
#include "io-gncbin.h"
#include "gnc-book.h"
#include "gnc-book-p.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
@ -58,6 +65,7 @@ static short module = MOD_IO;
struct _gnc_book
{
AccountGroup *topgroup;
GNCPriceDB *pricedb;
/* the requested book id, in the form or a URI, such as
* file:/some/where, or sql:server.host.com:555
@ -88,7 +96,7 @@ struct _gnc_book
Backend *backend;
};
/* ============================================================== */
/* ---------------------------------------------------------------------- */
static void
gnc_book_clear_error (GNCBook *book)
@ -102,7 +110,7 @@ gnc_book_push_error (GNCBook *book, GNCBackendError err)
book->last_err = err;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
GNCBackendError
gnc_book_get_error (GNCBook * book)
@ -121,31 +129,28 @@ gnc_book_pop_error (GNCBook * book)
return err;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
static void
gnc_book_init (GNCBook *book)
{
if (!book) return;
if(!book) return;
book->topgroup = xaccMallocAccountGroup ();
book->topgroup = xaccMallocAccountGroup();
book->pricedb = gnc_pricedb_create();
gnc_book_clear_error (book);
book->lockfd = -1;
};
}
GNCBook *
gnc_book_new (void)
{
GNCBook *book;
book = g_new0(GNCBook, 1);
gnc_book_init (book);
GNCBook *book = g_new0(GNCBook, 1);
gnc_book_init(book);
return book;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
gnc_commodity_table*
gnc_book_get_commodity_table(GNCBook *book)
@ -167,7 +172,23 @@ gnc_book_set_group (GNCBook *book, AccountGroup *grp)
book->topgroup = grp;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
GNCPriceDB *
gnc_book_get_pricedb(GNCBook *book)
{
if(!book) return NULL;
return book->pricedb;
}
void
gnc_book_set_pricedb(GNCBook *book, GNCPriceDB *db)
{
if(!book) return;
book->pricedb = db;
}
/* ---------------------------------------------------------------------- */
Backend *
xaccGNCBookGetBackend (GNCBook *book)
@ -176,7 +197,7 @@ xaccGNCBookGetBackend (GNCBook *book)
return book->backend;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
const char *
gnc_book_get_file_path (GNCBook *book)
@ -185,7 +206,7 @@ gnc_book_get_file_path (GNCBook *book)
return book->fullpath;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
const char *
gnc_book_get_url (GNCBook *book)
@ -194,8 +215,16 @@ gnc_book_get_url (GNCBook *book)
return book->book_id;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
void
gnc_book_mark_saved(GNCBook *book)
{
xaccGroupMarkSaved(gnc_book_get_group(book));
gnc_pricedb_mark_clean(gnc_book_get_pricedb(book));
}
/* ---------------------------------------------------------------------- */
static gboolean
gnc_book_get_file_lock (GNCBook *book)
{
@ -266,7 +295,104 @@ gnc_book_get_file_lock (GNCBook *book)
return TRUE;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
/* Load financial data from a file into the book, automtically
detecting the format of the file, if possible. Return FALSE on
error, and set the error parameter to indicate what went wrong if
it's not NULL. This function does not manage file locks in any
way. */
static gboolean
gnc_book_load_from_file(GNCBook *book)
{
const gchar *name = gnc_book_get_file_path(book);
if(!name) return FALSE;
if(gnc_is_xml_data_file(name)) {
if(gnc_book_load_from_xml_file(book)) {
return TRUE;
} else {
gnc_book_push_error(book, ERR_BACKEND_MISC);
return FALSE;
}
} else {
/* presume it's an old-style binary file */
GNCBackendError error;
gnc_book_load_from_binfile(book);
error = gnc_book_get_binfile_io_error();
if(error == ERR_BACKEND_NO_ERR) {
return TRUE;
} else {
gnc_book_push_error(book, error);
return FALSE;
}
}
/* Should never get here */
gnc_book_push_error(book, ERR_BACKEND_MISC);
return FALSE;
}
/* ---------------------------------------------------------------------- */
/* Write the financial data in a book to a file, returning FALSE on
error and setting the error_result to indicate what went wrong if
it's not NULL. This function does not manage file locks in any
way.
If make_backup is true, write out a time-stamped copy of the file
into the same directory as the indicated file, with a filename of
"file.YYYYMMDDHHMMSS.xac" where YYYYMMDDHHMMSS is replaced with the
current year/month/day/hour/minute/second. */
static gboolean
gnc_book_write_to_file(GNCBook *book,
gboolean make_backup)
{
const gchar *datafile = gnc_book_get_file_path(book);
if(!gnc_book_write_to_xml_file(book, datafile)) {
gnc_book_push_error(book, ERR_BACKEND_MISC);
return FALSE;
}
if(!make_backup) {
return TRUE;
} else {
char * timestamp;
int filenamelen;
char * backup;
/* also, write a time-stamped backup file */
/* tag each filename with a timestamp */
timestamp = xaccDateUtilGetStampNow ();
filenamelen = strlen (datafile) + strlen (timestamp) + 6;
backup = (char *) malloc (filenamelen);
strcpy (backup, datafile);
strcat (backup, ".");
strcat (backup, timestamp);
strcat (backup, ".xac");
free (timestamp);
if(gnc_book_write_to_xml_file(book, backup)) {
free (backup);
return TRUE;
} else {
gnc_book_push_error(book, ERR_BACKEND_MISC);
free (backup);
return FALSE;
}
}
/* Should never get here */
gnc_book_push_error(book, ERR_BACKEND_MISC);
return FALSE;
}
/* ---------------------------------------------------------------------- */
static gboolean
gnc_book_begin_file (GNCBook *book, const char * filefrag,
@ -305,7 +431,7 @@ gnc_book_begin_file (GNCBook *book, const char * filefrag,
return TRUE;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_begin (GNCBook *book, const char * book_id,
@ -450,12 +576,12 @@ gnc_book_begin (GNCBook *book, const char * book_id,
return rc;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_load (GNCBook *book)
{
GNCBackendError retval;
GNCBackendError backend_err;
if (!book) return FALSE;
if (!book->book_id) return FALSE;
@ -479,25 +605,25 @@ gnc_book_load (GNCBook *book)
xaccGroupMarkDoFree (book->topgroup);
xaccFreeAccountGroup (book->topgroup);
book->topgroup = NULL;
gnc_pricedb_destroy(book->pricedb);
book->pricedb = NULL;
xaccLogSetBaseName(book->fullpath);
gnc_book_clear_error (book);
book->topgroup = xaccReadAccountGroupFile (book->fullpath,
&retval);
if (ERR_BACKEND_NO_ERR != retval) gnc_book_push_error (book, retval);
gnc_book_load_from_file(book);
xaccLogEnable();
if (!book->topgroup || (gnc_book_get_error(book) != ERR_BACKEND_NO_ERR))
{
return FALSE;
}
if (!book->topgroup) return FALSE;
if (!book->pricedb) return FALSE;
if (gnc_book_get_error(book) != ERR_BACKEND_NO_ERR) return FALSE;
xaccGroupScrubSplits (book->topgroup);
LEAVE("book_id=%s", book->book_id);
return TRUE;
}
else if ((strncmp(book->book_id, "http://", 7) == 0) ||
(strncmp(book->book_id, "https://", 8) == 0) ||
(strncmp(book->book_id, "postgres://", 11) == 0))
@ -545,7 +671,19 @@ gnc_book_load (GNCBook *book)
}
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_not_saved(GNCBook *book)
{
if(!book) return FALSE;
return(xaccGroupNotSaved(book->topgroup)
||
gnc_pricedb_dirty(book->pricedb));
}
/* ---------------------------------------------------------------------- */
gboolean
gnc_book_save_may_clobber_data (GNCBook *book)
@ -562,12 +700,11 @@ gnc_book_save_may_clobber_data (GNCBook *book)
return FALSE;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
void
gnc_book_save (GNCBook *book)
{
GNCBackendError retval;
Backend *be;
if (!book) return;
@ -579,27 +716,26 @@ gnc_book_save (GNCBook *book)
* then give the user the option to save to disk.
*/
be = book->backend;
if (be && be->sync && book->topgroup)
{
/* if invoked as SaveAs(), then backend not yet set */
xaccGroupSetBackend (book->topgroup, be);
if (be && be->sync && book->topgroup) {
GNCBackendError err;
/* if invoked as SaveAs(), then backend not yet set */
xaccGroupSetBackend (book->topgroup, be);
(be->sync)(be, book->topgroup);
retval = xaccBackendGetError(be);
if (ERR_BACKEND_NO_ERR != retval)
{
gnc_book_push_error (book, retval);
/* we close the backend here ... isn't this a bit harsh ??? */
if (be->book_end)
{
(be->book_end)(be);
}
}
return;
(be->sync)(be, book->topgroup);
err = xaccBackendGetError(be);
if (ERR_BACKEND_NO_ERR != err) {
gnc_book_push_error (book, err);
/* we close the backend here ... isn't this a bit harsh ??? */
if (be->book_end) {
(be->book_end)(be);
}
}
return;
}
/* if the fullpath doesn't exist, either the user failed to initialize,
* or the lockfile was never obtained. Either way, we can't write. */
gnc_book_clear_error (book);
@ -610,16 +746,15 @@ gnc_book_save (GNCBook *book)
return;
}
if (book->topgroup)
{
xaccWriteAccountGroupFile (book->fullpath, book->topgroup,
TRUE, &retval);
if (ERR_BACKEND_NO_ERR != retval) gnc_book_push_error (book, retval);
if (book->topgroup) {
if(!gnc_book_write_to_file(book, TRUE)) {
gnc_book_push_error (book, ERR_BACKEND_MISC);
}
}
LEAVE(" ");
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
void
gnc_book_end (GNCBook *book)
@ -677,6 +812,10 @@ gnc_book_destroy (GNCBook *book)
xaccFreeAccountGroup (book->topgroup);
book->topgroup = NULL;
gnc_pricedb_destroy (book->pricedb);
book->pricedb = NULL;
xaccLogEnable();
g_free (book);
@ -684,7 +823,7 @@ gnc_book_destroy (GNCBook *book)
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
/*
* If $HOME/.gnucash/data directory doesn't exist, then create it.
*/
@ -723,7 +862,7 @@ MakeHomeDir (void)
g_free (data);
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
/* XXX hack alert -- we should be yanking this out of some config file */
static char * searchpaths[] =
{
@ -858,7 +997,7 @@ xaccResolveFilePath (const char * filefrag)
return NULL;
}
/* ============================================================== */
/* ---------------------------------------------------------------------- */
char *
xaccResolveURL (const char * pathfrag)
@ -886,5 +1025,3 @@ xaccResolveURL (const char * pathfrag)
return (xaccResolveFilePath (pathfrag));
}
/* ==================== END OF FILE ================== */

View File

@ -54,10 +54,11 @@
#define __GNC_BOOK_H__
#include "Group.h"
#include "FileIO.h"
#include "Backend.h"
#include "gnc-pricedb.h"
/** TYPES **********************************************************/
/** TYPEDEFS ********************************************************/
typedef struct _gnc_book GNCBook;
/** PROTOTYPES ******************************************************/
@ -116,14 +117,9 @@ gboolean gnc_book_load (GNCBook *book);
GNCBackendError gnc_book_get_error (GNCBook *book);
GNCBackendError gnc_book_pop_error (GNCBook *book);
/* The gnc_book_get_group() method will return the current top-level
* account group.
*
* The gnc_book_set_group() method will set the topgroup to a new value.
*/
AccountGroup * gnc_book_get_group (GNCBook *book);
void gnc_book_set_group (GNCBook *book, AccountGroup *topgroup);
AccountGroup *gnc_book_get_group (GNCBook *book);
GNCPriceDB *gnc_book_get_pricedb (GNCBook *book);
/*
* gnc_book_get_commodity_table returns the commodity table associated with
@ -148,6 +144,12 @@ gnc_commodity_table* gnc_book_get_commodity_table(GNCBook *book);
const char * gnc_book_get_file_path (GNCBook *book);
const char * gnc_book_get_url (GNCBook *book);
/*
* The gnc_book_not_saved() subroutine will return TRUE
* if any data in the book hasn't been saved to long-term storage.
*/
gboolean gnc_book_not_saved(GNCBook *book);
/* FIXME: This isn't as thorough as we might want it to be... */
gboolean gnc_book_save_may_clobber_data (GNCBook *book);

View File

@ -141,5 +141,5 @@ gnc_commodity_end_handler(gpointer data_for_children,
sixtp*
gnc_commodity_sixtp_parser_create(void)
{
return sixtp_dom_parser_new(gnc_commodity_end_handler);
return sixtp_dom_parser_new(gnc_commodity_end_handler, NULL, NULL);
}

View File

@ -261,5 +261,36 @@ gnc_strisnum(const char *s)
return FALSE;
}
/********************************************************************\
See header for docs.
\********************************************************************/
static void
kv_pair_helper(gpointer key, gpointer val, gpointer user_data)
{
GSList **result = (GSList **) user_data;
GHashTableKVPair *kvp = g_new(GHashTableKVPair, 1);
kvp->key = key;
kvp->value = val;
*result = g_slist_prepend(*result, kvp);
}
GSList *
g_hash_table_key_value_pairs(GHashTable *table)
{
GSList *result_list = NULL;
g_hash_table_foreach(table, kv_pair_helper, &result_list);
return result_list;
}
void
g_hash_table_kv_pair_free_gfunc(gpointer data, gpointer user_data)
{
GHashTableKVPair *kvp = (GHashTableKVPair *) data;
g_free(kvp);
}
/************************* END OF FILE ******************************\
\********************************************************************/

View File

@ -171,4 +171,30 @@ char * ultostr (unsigned long val, int base);
* whitespace. */
gboolean gnc_strisnum(const char *s);
/***********************************************************************\
g_hash_table_key_value_pairs(hash): Returns a GSList* of all the
keys and values in a given hash table. Data elements of lists are
actual hash elements, so be careful, and deallocation of the
GHashTableKVPairs in the result list are the caller's
responsibility. A typical sequence might look like this:
GSList *kvps = g_hash_table_key_value_pairs(hash);
... use kvps->data->key and kvps->data->val, etc. here ...
g_slist_foreach(kvps, g_hash_table_kv_pair_free_gfunc, NULL);
g_slist_free(kvps);
*/
typedef struct {
gpointer key;
gpointer value;
} GHashTableKVPair;
GSList *g_hash_table_key_value_pairs(GHashTable *table);
void g_hash_table_kv_pair_free_gfunc(gpointer data, gpointer user_data);
/***********************************************************************/
#endif

View File

@ -0,0 +1,34 @@
/********************************************************************
* gnc-pricedb-p.h -- a simple price database for gnucash. *
* Copyright (C) 2001 Rob Browning *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
*******************************************************************/
#ifndef __GNC_PRICEDB_P_H__
#define __GNC_PRICEDB_P_H__
#include "gnc-pricedb.h"
void gnc_pricedb_mark_clean(GNCPriceDB *db);
void gnc_pricedb_substitute_commodity(GNCPriceDB *db,
gnc_commodity *old_c,
gnc_commodity *new_c);
#endif

View File

@ -0,0 +1,373 @@
#include "config.h"
#include <string.h>
#include "gnc-engine-util.h"
#include "sixtp.h"
#include "sixtp-utils.h"
#include "sixtp-parsers.h"
#include "sixtp-dom-parsers.h"
#include "sixtp-dom-generators.h"
#include "sixtp-writers.h"
#include "sixtp-xml-write-utils.h"
#include "gnc-pricedb.h"
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_ENGINE;
/* 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: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)
{
if(!p || !sub_node) return FALSE;
if(safe_strcmp("price:commodity", sub_node->name) == 0) {
gnc_commodity *c = dom_tree_to_commodity_ref(sub_node);
if(!c) return FALSE;
gnc_price_set_commodity(p, c);
} else if(safe_strcmp("price:currency", sub_node->name) == 0) {
gnc_commodity *c = dom_tree_to_commodity_ref(sub_node);
if(!c) return FALSE;
gnc_price_set_currency(p, c);
} else if(safe_strcmp("price:time", sub_node->name) == 0) {
Timespec *t = dom_tree_to_timespec(sub_node);
if(!t) return FALSE;
gnc_price_set_time(p, t);
g_free(t);
} else if(safe_strcmp("price:source", sub_node->name) == 0) {
char *text = dom_tree_to_text(sub_node->xmlChildrenNode);
if(!text) return FALSE;
gnc_price_set_source(p, text);
g_free(text);
} else if(safe_strcmp("price:type", sub_node->name) == 0) {
char *text = dom_tree_to_text(sub_node->xmlChildrenNode);
if(!text) return FALSE;
gnc_price_set_type(p, text);
g_free(text);
} else if(safe_strcmp("price:value", 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);
}
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;
/* 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();
if(!p) { ok = FALSE; goto cleanup_and_exit; }
for(child = price_xml->xmlChildrenNode; child; child = child->next) {
switch(child->type) {
case XML_COMMENT_NODE:
break;
case XML_ELEMENT_NODE:
if(!price_parse_xml_sub_node(p, child)) {
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)
{
GNCPriceDB *db = gnc_pricedb_create();
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;
}
}
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;
}
/***********************************************************************/
/* WRITING */
/***********************************************************************/
static gboolean
add_child_or_kill_parent(xmlNodePtr parent, xmlNodePtr child)
{
if(!parent) return FALSE;
if(!child) {
xmlFreeNode(parent);
return FALSE;
}
xmlAddChild(parent, child);
return TRUE;
}
static xmlNodePtr
gnc_price_to_dom_tree(const char *tag, GNCPrice *price)
{
xmlNodePtr price_xml;
const gchar *typestr, *sourcestr;
xmlNodePtr tmpnode;
gnc_commodity *commodity;
gnc_commodity *currency;
Timespec *timesp;
gnc_numeric value;
if (!(tag && price)) return NULL;
price_xml = xmlNewNode(NULL, tag);
if(!price_xml) return NULL;
commodity = gnc_price_get_commodity(price);
currency = gnc_price_get_currency(price);
if(!(commodity && currency)) return NULL;
tmpnode = commodity_ref_to_dom_tree("price:commodity", commodity);
if(!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
tmpnode = commodity_ref_to_dom_tree("price:currency", currency);
if(!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
timesp = gnc_price_get_time(price);
tmpnode = timespec_to_dom_tree("price:time", timesp);
if(!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
sourcestr = gnc_price_get_source(price);
if(sourcestr && (strlen(sourcestr) != 0)) {
tmpnode = text_to_dom_tree("price:source", sourcestr);
if(!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
}
typestr = gnc_price_get_type(price);
if(typestr && (strlen(typestr) != 0)) {
tmpnode = text_to_dom_tree("price:type", typestr);
if(!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
}
value = gnc_price_get_value(price);
tmpnode = gnc_numeric_to_dom_tree("price:value", &value);
if(!add_child_or_kill_parent(price_xml, tmpnode)) return NULL;
return price_xml;
}
static gboolean
xml_add_gnc_price_adapter(GNCPrice *p, gpointer data)
{
xmlNodePtr xml_node = (xmlNodePtr) data;
if(p) {
xmlNodePtr price_xml = gnc_price_to_dom_tree("price", p);
if(!price_xml) return FALSE;
xmlAddChild(xml_node, price_xml);
return TRUE;
} else {
return TRUE;
}
}
gboolean
xml_add_gnc_pricedb(xmlNodePtr p, const char *tag, GNCPriceDB *db)
{
xmlNodePtr db_xml = NULL;
if(!p || !tag) return FALSE;
if(!db) return TRUE;
db_xml= xmlNewTextChild(p, NULL, tag, NULL);
if(!db_xml) return FALSE;
xmlSetProp(db_xml, "version", "1");
if(!gnc_pricedb_foreach_price(db, xml_add_gnc_price_adapter, db_xml, TRUE))
{
xmlFreeNode(db_xml);
return FALSE;
}
return TRUE;
}

733
src/engine/gnc-pricedb.c Normal file
View File

@ -0,0 +1,733 @@
/********************************************************************
* gnc-pricedb.c -- a simple price database for gnucash. *
* Copyright (C) 2001 Rob Browning *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
*******************************************************************/
#include "config.h"
#include <glib.h>
#include <string.h>
#include "gnc-pricedb.h"
#include "gnc-pricedb-p.h"
#include "gnc-engine.h"
#include "gnc-engine-util.h"
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_ENGINE;
struct _GNCPriceDB {
GHashTable *commodity_hash;
gboolean dirty;
};
struct _GNCPrice {
guint32 refcount;
GNCPriceDB *db;
gnc_commodity *commodity;
gnc_commodity *currency;
Timespec time;
char *source;
char *type;
gnc_numeric value;
};
/****************************************************************************/
/* GNCPrice functions
*/
/* allocation */
GNCPrice *
gnc_price_create()
{
GNCPrice *p = g_new0(GNCPrice, 1);
p->refcount = 1;
return p;
}
void
gnc_price_ref(GNCPrice *p)
{
if(!p) return;
p->refcount++;
}
void
gnc_price_unref(GNCPrice *p)
{
if(!p) return;
if(p->refcount == 0) {
PERR("gnc_price_unref: refcount == 0!");
assert(p->refcount != 0);
}
p->refcount--;
if(p->refcount == 0) {
if(p->type) g_cache_remove(gnc_engine_get_string_cache(), p->type);
memset(p, 0, sizeof(GNCPrice));
g_free(p);
}
}
GNCPrice *
gnc_price_clone(GNCPrice* p)
{
/* the clone doesn't belong to a PriceDB */
GNCPrice *new_p;
if(!p) return NULL;
new_p = gnc_price_create();
if(!new_p) return NULL;
gnc_price_set_commodity(new_p, gnc_price_get_commodity(p));
gnc_price_set_time(new_p, gnc_price_get_time(p));
gnc_price_set_source(new_p, gnc_price_get_source(p));
gnc_price_set_type(new_p, gnc_price_get_type(p));
gnc_price_set_value(new_p, gnc_price_get_value(p));
gnc_price_set_currency(new_p, gnc_price_get_currency(p));
return(new_p);
}
/* setters */
void
gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c)
{
if(!p) return;
if(!gnc_commodity_equiv(p->commodity, c)) {
p->commodity = c;
if(p->db) p->db->dirty = TRUE;
}
}
void
gnc_price_set_currency(GNCPrice *p, gnc_commodity *c)
{
if(!p) return;
if(!gnc_commodity_equiv(p->currency, c)) {
p->currency = c;
if(p->db) p->db->dirty = TRUE;
}
}
void
gnc_price_set_time(GNCPrice *p, Timespec *t)
{
if(!p) return;
if(!timespec_equal(&(p->time), t)) {
p->time = *t;
if(p->db) p->db->dirty = TRUE;
}
}
void
gnc_price_set_source(GNCPrice *p, const char *s)
{
if(!p) return;
if(safe_strcmp(p->source, s) != 0) {
GCache *cache = gnc_engine_get_string_cache();
char *tmp = g_cache_insert(cache, (gpointer) s);
if(p->source) g_cache_remove(cache, p->source);
p->source = tmp;
if(p->db) p->db->dirty = TRUE;
}
}
void
gnc_price_set_type(GNCPrice *p, const char* type)
{
if(!p) return;
if(safe_strcmp(p->type, type) != 0) {
GCache *cache = gnc_engine_get_string_cache();
gchar *tmp = g_cache_insert(cache, (gpointer) type);
if(p->type) g_cache_remove(cache, p->type);
p->type = tmp;
if(p->db) p->db->dirty = TRUE;
}
}
void
gnc_price_set_value(GNCPrice *p, gnc_numeric value)
{
if(!p) return;
if(!gnc_numeric_eq(p->value, value)) {
p->value = value;
if(p->db) p->db->dirty = TRUE;
}
}
/***********/
/* getters */
gnc_commodity *
gnc_price_get_commodity(GNCPrice *p)
{
if(!p) return NULL;
return p->commodity;
}
Timespec *
gnc_price_get_time(GNCPrice *p)
{
if(!p) return NULL;
return &(p->time);
}
const char *
gnc_price_get_source(GNCPrice *p)
{
if(!p) return NULL;
return p->source;
}
const char *
gnc_price_get_type(GNCPrice *p)
{
if(!p) return NULL;
return p->type;
}
gnc_numeric
gnc_price_get_value(GNCPrice *p)
{
if(!p) {
PERR("gnc_price_get_value: price NULL.\n");
return gnc_numeric_zero();
}
return p->value;
}
gnc_commodity *
gnc_price_get_currency(GNCPrice *p)
{
if(!p) return NULL;
return p->currency;
}
/* setters */
static gint
compare_prices_by_date(gconstpointer a, gconstpointer b)
{
Timespec *time_a;
Timespec *time_b;
if(!a && !b) return 0;
/* nothing is always less than something */
if(!a) return -1;
time_a = gnc_price_get_time((GNCPrice *) a);
time_b = gnc_price_get_time((GNCPrice *) b);
return timespec_cmp(time_a, time_b);
}
gboolean
gnc_price_list_insert(GList **prices, GNCPrice *p)
{
GList *result_list;
if(!prices) return FALSE;
if(!p) return FALSE;
gnc_price_ref(p);
result_list = g_list_insert_sorted(*prices, p, compare_prices_by_date);
if(!result_list) return FALSE;
*prices = result_list;
return TRUE;
}
gboolean
gnc_price_list_remove(GList **prices, GNCPrice *p)
{
GList *result_list;
GList *found_element;
if(!prices) return FALSE;
if(!p) return FALSE;
found_element = g_list_find(*prices, p);
if(!found_element) return TRUE;
result_list = g_list_remove_link(*prices, found_element);
gnc_price_unref((GNCPrice *) found_element->data);
g_list_free(found_element);
*prices = result_list;
return TRUE;
}
static void
price_list_destroy_helper(gpointer data, gpointer user_data)
{
gnc_price_unref((GNCPrice *) data);
}
void
gnc_price_list_destroy(GList *prices)
{
g_list_foreach(prices, price_list_destroy_helper, NULL);
g_list_free(prices);
}
/****************************************************************************/
/* GNCPriceDB functions
A pricedb is a hash mapping price commodities (of type
gnc_commodity*) to hashes mapping price currencies (of type
gnc_commodity*) to price lists. The top-level key is the commodity
you want the prices for, and the second level key is the commodity
that the value is expressed in terms of.
See the header for other info.
*/
GNCPriceDB *
gnc_pricedb_create()
{
GNCPriceDB * result = g_new0(GNCPriceDB, 1);
result->commodity_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
g_return_val_if_fail (result->commodity_hash, NULL);
return result;
}
static void
destroy_pricedb_currency_hash_data(gpointer key,
gpointer data,
gpointer user_data)
{
GList *price_list = (GList *) data;
gnc_price_list_destroy(price_list);
}
static void
destroy_pricedb_commodity_hash_data(gpointer key,
gpointer data,
gpointer user_data)
{
GHashTable *currency_hash = (GHashTable *) data;
if (!currency_hash) return;
g_hash_table_foreach (currency_hash,
destroy_pricedb_currency_hash_data,
NULL);
g_hash_table_destroy(currency_hash);
currency_hash = NULL;
}
gboolean
gnc_pricedb_dirty(GNCPriceDB *p)
{
if(!p) return FALSE;
return p->dirty;
}
void
gnc_pricedb_mark_clean(GNCPriceDB *p)
{
if(!p) return;
p->dirty = FALSE;
}
void
gnc_pricedb_destroy(GNCPriceDB *db)
{
if(!db) return;
g_hash_table_foreach (db->commodity_hash,
destroy_pricedb_commodity_hash_data,
NULL);
g_hash_table_destroy (db->commodity_hash);
db->commodity_hash = NULL;
}
gboolean
gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
{
/* this function will use p, adding a ref, so treat p as read-only
if this function succeeds. */
GList *price_list;
gnc_commodity *commodity;
gnc_commodity *currency;
GHashTable *currency_hash;
if(!db || !p) return FALSE;
commodity = gnc_price_get_commodity(p);
if(!commodity) return FALSE;
currency = gnc_price_get_currency(p);
if(!currency) return FALSE;
if(!db->commodity_hash) return FALSE;
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
if(!currency_hash) {
currency_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
g_hash_table_insert(db->commodity_hash, commodity, currency_hash);
}
price_list = g_hash_table_lookup(currency_hash, currency);
if(!gnc_price_list_insert(&price_list, p)) return FALSE;
if(!price_list) return FALSE;
g_hash_table_insert(currency_hash, currency, price_list);
db->dirty = TRUE;
return TRUE;
}
gboolean
gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
{
GList *price_list;
gnc_commodity *commodity;
gnc_commodity *currency;
GHashTable *currency_hash;
if(!db || !p) return FALSE;
commodity = gnc_price_get_commodity(p);
if(!commodity) return FALSE;
currency = gnc_price_get_currency(p);
if(!currency) return FALSE;
if(!db->commodity_hash) return FALSE;
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
if(!currency_hash) return FALSE;
price_list = g_hash_table_lookup(currency_hash, currency);
if(!gnc_price_list_remove(&price_list, p)) return FALSE;
if(price_list) {
g_hash_table_insert(currency_hash, currency, price_list);
} else {
g_hash_table_remove(currency_hash, currency);
}
db->dirty = TRUE;
return TRUE;
}
GNCPrice *
gnc_pricedb_lookup_latest(GNCPriceDB *db,
gnc_commodity *commodity,
gnc_commodity *currency)
{
GList *price_list;
GNCPrice *result;
GHashTable *currency_hash;
if(!db || !commodity || !currency) return NULL;
currency_hash = g_hash_table_lookup(db->commodity_hash, commodity);
if(!currency_hash) return NULL;
price_list = g_hash_table_lookup(currency_hash, currency);
if(!price_list) return NULL;
result = price_list->data;
gnc_price_ref(result);
return result;
}
GList *
gnc_pricedb_lookup_at_time(GNCPriceDB *db,
gnc_commodity *c,
gnc_commodity *currency,
Timespec *t)
{
GList *price_list;
GList *result = NULL;
GList *item = NULL;
GHashTable *currency_hash;
if(!db || !c || !currency || !t) return NULL;
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
if(!currency_hash) return NULL;
price_list = g_hash_table_lookup(currency_hash, currency);
if(!price_list) return NULL;
item = price_list;
while(item) {
GNCPrice *p = item->data;
Timespec *price_time = gnc_price_get_time(p);
if(timespec_equal(price_time, t)) {
result = g_list_prepend(result, p);
gnc_price_ref(p);
}
item = item->next;
}
return result;
}
/***************************************************************************/
/* gnc_pricedb_foreach_price infrastructure
*/
typedef struct {
gboolean ok;
gboolean (*func)(GNCPrice *p, gpointer user_data);
gpointer user_data;
} GNCPriceDBForeachData;
static void
pricedb_foreach_pricelist(gpointer key, gpointer val, gpointer user_data)
{
GList *price_list = (GList *) val;
GList *node = price_list;
GNCPriceDBForeachData *foreach_data = (GNCPriceDBForeachData *) user_data;
while(foreach_data->ok && node) {
GNCPrice *p = (GNCPrice *) node->data;
foreach_data->func(p, foreach_data->user_data);
node = node->next;
}
}
static void
pricedb_foreach_currencies_hash(gpointer key, gpointer val, gpointer user_data)
{
GHashTable *currencies_hash = (GHashTable *) val;
g_hash_table_foreach(currencies_hash, pricedb_foreach_pricelist, user_data);
}
static gboolean
unstable_price_traversal(GNCPriceDB *db,
gboolean (*f)(GNCPrice *p, gpointer user_data),
gpointer user_data)
{
GNCPriceDBForeachData foreach_data;
if(!db || !f) return FALSE;
foreach_data.ok = TRUE;
foreach_data.func = f;
foreach_data.user_data = user_data;
g_hash_table_foreach(db->commodity_hash,
pricedb_foreach_currencies_hash,
&foreach_data);
return foreach_data.ok;
}
static gint
compare_kvpairs_by_commodity_key(gconstpointer a, gconstpointer b)
{
GHashTableKVPair *kvpa = (GHashTableKVPair *) a;
GHashTableKVPair *kvpb = (GHashTableKVPair *) b;
gnc_commodity *ca;
gnc_commodity *cb;
int cmp_result;
if(a == b) return 0;
if(!a && !b) return 0;
if(!a) return -1;
if(!b) return 1;
ca = (gnc_commodity *) kvpa->key;
cb = (gnc_commodity *) kvpb->key;
cmp_result = safe_strcmp(gnc_commodity_get_namespace(ca),
gnc_commodity_get_namespace(cb));
if(cmp_result != 0) return cmp_result;
return safe_strcmp(gnc_commodity_get_mnemonic(ca),
gnc_commodity_get_mnemonic(cb));
}
static gboolean
stable_price_traversal(GNCPriceDB *db,
gboolean (*f)(GNCPrice *p, gpointer user_data),
gpointer user_data)
{
GSList *currency_hashes = NULL;
GSList *foo = NULL;
gboolean ok = TRUE;
GSList *i = NULL;
if(!db || !f) return FALSE;
currency_hashes = g_hash_table_key_value_pairs(db->commodity_hash);
currency_hashes = g_slist_sort(currency_hashes,
compare_kvpairs_by_commodity_key);
for(i = currency_hashes; i; i = i->next) {
GHashTableKVPair *kv_pair = (GHashTableKVPair *) i->data;
GHashTable *currency_hash = (GHashTable *) kv_pair->value;
GSList *price_lists = g_hash_table_key_value_pairs(currency_hash);
GSList *j;
price_lists = g_slist_sort(price_lists, compare_kvpairs_by_commodity_key);
for(j = price_lists; j; j = j->next) {
GHashTableKVPair *pricelist_kvp = (GHashTableKVPair *) j->data;
GList *price_list = (GList *) pricelist_kvp->value;
GList *node;
for(node = (GList *) price_list; node; node = node->next) {
GNCPrice *price = (GNCPrice *) node->data;
if(!f(price, user_data)) ok = FALSE;
}
}
if(price_lists) {
g_slist_foreach(price_lists, g_hash_table_kv_pair_free_gfunc, NULL);
g_slist_free(price_lists);
price_lists = NULL;
}
}
if(currency_hashes) {
g_slist_foreach(currency_hashes, g_hash_table_kv_pair_free_gfunc, NULL);
g_slist_free(currency_hashes);
}
return ok;
}
gboolean
gnc_pricedb_foreach_price(GNCPriceDB *db,
gboolean (*f)(GNCPrice *p, gpointer user_data),
gpointer user_data,
gboolean stable_order)
{
if(stable_order) return stable_price_traversal(db, f, user_data);
return unstable_price_traversal(db, f, user_data);
}
/***************************************************************************/
/* commodity substitution
*/
typedef struct {
gnc_commodity *old_c;
gnc_commodity *new_c;
} GNCPriceFixupData;
static gboolean
gnc_price_fixup_legacy_commods(GNCPrice *p, gpointer data)
{
GNCPriceFixupData *fixup_data = (GNCPriceFixupData *) data;
gnc_commodity *price_c;
if (!p) return FALSE;
price_c = gnc_price_get_commodity(p);
if (gnc_commodity_equiv(price_c, fixup_data->old_c)) {
gnc_price_set_commodity(p, fixup_data->new_c);
}
price_c = gnc_price_get_currency(p);
if (gnc_commodity_equiv(price_c, fixup_data->old_c)) {
gnc_price_set_currency(p, fixup_data->new_c);
}
return TRUE;
}
static void
remap_currency_hash_keys(gpointer key, gpointer val, gpointer user_data)
{
GHashTable *currencies_hash = (GHashTable *) val;
GNCPriceFixupData *fixup_data = (GNCPriceFixupData *) user_data;
GList *price_list;
price_list = g_hash_table_lookup(currencies_hash, fixup_data->old_c);
if(price_list) {
g_hash_table_remove(currencies_hash, fixup_data->old_c);
g_hash_table_insert(currencies_hash, fixup_data->new_c, price_list);
}
}
void
gnc_pricedb_substitute_commodity(GNCPriceDB *db,
gnc_commodity *old_c,
gnc_commodity *new_c)
{
GHashTable *currency_hash;
GNCPriceFixupData data;
if(!db || !old_c || !new_c) return;
data.old_c = old_c;
data.new_c = new_c;
/* first remap the relevant commodity -> currency hash, if any */
currency_hash = g_hash_table_lookup(db->commodity_hash, old_c);
if(currency_hash) {
g_hash_table_remove(db->commodity_hash, old_c);
g_hash_table_insert(db->commodity_hash, new_c, currency_hash);
}
g_hash_table_foreach(db->commodity_hash, remap_currency_hash_keys, &data);
if(!gnc_pricedb_foreach_price(db,
gnc_price_fixup_legacy_commods,
&data,
FALSE)) {
PERR("Adjustments to legacy commodity pointers in pricedb failed!");
}
}
/***************************************************************************/
#if 0
/* Semi-lame debugging code */
void
gnc_price_print(GNCPrice *p, FILE *f)
{
gnc_commodity *commodity;
gnc_commodity *currency;
if(!p) return;
if(!f) return;
commodity = gnc_price_get_commodity(p);
currency = gnc_price_get_currency(p);
if(!commodity) return;
if(!currency) return;
fprintf(f, " <pdb:price>\n");
fprintf(f, " <pdb:commodity pointer=%p>\n", commodity);
fprintf(f, " <cmdty:ref-space> %s</gnc:cmdty:ref-space>\n",
gnc_commodity_get_namespace(commodity));
fprintf(f, " <cmdty:ref-id>%s</cmdty:ref-id>\n",
gnc_commodity_get_mnemonic(commodity));
fprintf(f, " </pdb:commodity>\n");
fprintf(f, " <pdb:currency pointer=%p>\n", currency);
fprintf(f, " <cmdty:ref-space>%s</gnc:cmdty:ref-space>\n",
gnc_commodity_get_namespace(currency));
fprintf(f, " <cmdty:ref-id>%s</cmdty:ref-id>\n",
gnc_commodity_get_mnemonic(currency));
fprintf(f, " </pdb:currency>\n");
fprintf(f, " %s\n", gnc_price_get_source(p));
fprintf(f, " %s\n", gnc_price_get_type(p));
fprintf(f, " %g\n", gnc_numeric_to_double(gnc_price_get_value(p)));
fprintf(f, " </pdb:price>\n");
}
static gboolean
print_pricedb_adapter(GNCPrice *p, gpointer user_data)
{
FILE *f = (FILE *) user_data;
gnc_price_print(p, f);
return TRUE;
}
void
gnc_pricedb_print_contents(GNCPriceDB *db, FILE *f)
{
if(!db) { PERR("NULL PriceDB\n"); return; }
if(!f) { PERR("NULL FILE*\n"); return; }
fprintf(f, "<gnc:pricedb>\n");
gnc_pricedb_foreach_price(db, print_pricedb_adapter, f, FALSE);
fprintf(f, "</gnc:pricedb>\n");
}
#endif

169
src/engine/gnc-pricedb.h Normal file
View File

@ -0,0 +1,169 @@
/********************************************************************
* gnc-pricedb.h -- a simple price database for gnucash. *
* Copyright (C) 2001 Rob Browning *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
*******************************************************************/
#ifndef __GNC_PRICEDB_H__
#define __GNC_PRICEDB_H__
#include "date.h"
#include "gnc-numeric.h"
#include "gnc-commodity.h"
#include <stdio.h>
/**********************************************************************\
GNCPrice:
commodity: the item being priced.
value: the value of the item being priced.
currency: the denomination of the value of the item being priced.
time: the time the price was valid.
source: a string describing the source of the quote. These
strings will be something like this: "Finance::Quote",
"user:misc", "user:foo", etc. If the quote came from a user, as a
matter of policy, you *must* prefix the string you give with
"user:". For now, the only other reserved values are
"Finance::Quote" and "old-file-import".
type: the type of quote - types possible right now are bid, ask,
last, and unknown.
NOTE: for source and type, NULL and the empty string are
considered the same, so if one of these is "", then after a file
save/restore, it might be NULL. Behave accordingly.
GNCPrices are reference counted. When you gnc_price_create one or
clone it, the new price's count is set to 1. When you're finished
with a price, call gnc_price_unref. If you hand the pointer to
some other code that needs to keep it, make sure it calls
gnc_price_ref to indicate it's interest in that price, and calls
gnc_price_unref when it's finished with the price.
All of the getters return data that's internal to the GNCPrice,
not copies, so don't free these values.
All of the setters store copies of the data, with the exception of
the commodity field which just stores the pointer given. It is
assumed that commodities are a global resource and are pointer
unique.
*/
typedef struct _GNCPrice GNCPrice;
/* allocation */
GNCPrice *gnc_price_create(void); /* create and initialize a price */
GNCPrice *gnc_price_clone(GNCPrice* p);
void gnc_price_ref(GNCPrice *p);
void gnc_price_unref(GNCPrice *p);
/* setters */
void gnc_price_set_commodity(GNCPrice *p, gnc_commodity *c);
void gnc_price_set_currency(GNCPrice *p, gnc_commodity *c);
void gnc_price_set_time(GNCPrice *p, Timespec *t);
void gnc_price_set_source(GNCPrice *p, const char *source);
void gnc_price_set_type(GNCPrice *p, const char* type);
void gnc_price_set_value(GNCPrice *p, gnc_numeric value);
/* getters */
gnc_commodity * gnc_price_get_commodity(GNCPrice *p);
gnc_commodity * gnc_price_get_currency(GNCPrice *p);
Timespec * gnc_price_get_time(GNCPrice *p);
const char * gnc_price_get_source(GNCPrice *p);
const char * gnc_price_get_type(GNCPrice *p);
gnc_numeric gnc_price_get_value(GNCPrice *p);
/* price_list funcs
A price list is a time sorted GList of prices. The price list
maintains a ref for itself for all the prices in the list
(i.e. adding a price calls gnc_price_ref on it, removing a price
calls gnc_price_unref, etc. Destroying a list also removes this
list reference from the prices in the list. */
gboolean gnc_price_list_insert(GList **prices, GNCPrice *p);
gboolean gnc_price_list_remove(GList **prices, GNCPrice *p);
void gnc_price_list_destroy(GList *prices);
/**********************************************************************
GNCPriceDB
Whenever a you store a price in the pricedb, the pricedb adds its
own reference to the price, so you can safely unref that price when
you're finished with it.
Similarly, when the pricedb returns a price to you, either singly,
or in a price list, the price will have had a ref added for you, so
you only need to unref the price(s) when you're finished with them.
*/
typedef struct _GNCPriceDB GNCPriceDB;
GNCPriceDB * gnc_pricedb_create(void);
void gnc_pricedb_destroy(GNCPriceDB *db);
/* Add a price to the pricedb, you may drop your reference to the
price (i.e. call unref) after this succeeds, whenever you're
finished with the price. */
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p);
/* Remove a price from the pricedb. */
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p);
GNCPrice * gnc_pricedb_lookup_latest(GNCPriceDB *db,
gnc_commodity *comodity,
gnc_commodity *currency);
/* Return all prices that match the given commodity, currency, and
timespec. Prices will be returned as a price_list (see above) */
GList * gnc_pricedb_lookup_at_time(GNCPriceDB *db,
gnc_commodity *commodity,
gnc_commodity *currency,
Timespec *t);
/* Call f once for each price in db, until and unless f returns FALSE.
If stable_order is not FALSE, make sure the ordering of the
traversal is stable (i.e. the same order every time given the same
db contents). */
gboolean gnc_pricedb_foreach_price(GNCPriceDB *db,
gboolean (*f)(GNCPrice *p,
gpointer user_data),
gpointer user_data,
gboolean stable_order);
/* Return FALSE if the database has not been modified */
gboolean gnc_pricedb_dirty(GNCPriceDB *p);
#if 0
/* semi-lame debugging code */
void gnc_price_print(GNCPrice *db, FILE *f);
void gnc_pricedb_print_contents(GNCPriceDB *db, FILE *f);
#endif
#endif

View File

@ -108,7 +108,9 @@
#include "gnc-engine.h"
#include "gnc-engine-util.h"
#include "GNCIdP.h"
#include "gnc-pricedb.h"
#include "gnc-book-p.h"
#define PERMS 0666
#define WFLAGS (O_WRONLY | O_CREAT | O_TRUNC)
@ -152,12 +154,96 @@ static AccountGroup *maingrp; /* temporary holder for file
in our handling of keys and values here. We use int32s and
pointers interchangably ATM, and that may be problematic on some
architectures...
Only used *during* file read process.
*/
/* used while reading */
static GHashTable *ids_to_finished_accounts;
static GHashTable *ids_to_unfinished_accounts;
/** PriceDB bits ******************************************************/
/* Commodity prices (stock quotes, etc.) used to be stored as zero
quantity (damount) splits inside the relevant accounts. Now we
used the pricedb. potential_quotes is where we put the info from
all these "quote splits" while reading, until we have a chance to
cram them into the pricedb. We can't do it any sooner because
until we finish reading the file, the Account*'s havent' been
filled out and so their commodities and types may not have been
initilized yet.
This shouldn't be a static global but see comments above RE ids_to*
hashes. */
typedef struct {
Split *split;
gnc_numeric price;
} GNCPotentialQuote;
static GSList *potential_quotes;
static void
mark_potential_quote(Split *s, double price, double quantity)
{
static int count = 0;
if((price != 0) && DEQ(quantity, 0)){
GNCPotentialQuote *q = g_new0(GNCPotentialQuote, 1);
q->split = s;
q->price = double_to_gnc_numeric(price,
GNC_DENOM_AUTO,
GNC_DENOM_SIGFIGS(9) | GNC_RND_ROUND);
potential_quotes = g_slist_prepend(potential_quotes, q);
}
}
static gboolean
cvt_potential_prices_to_pricedb_and_cleanup(GNCPriceDB **prices)
{
GSList *item = potential_quotes;
*prices = gnc_pricedb_create();
if (!*prices) return FALSE;
while(item)
{
GNCPotentialQuote *q = (GNCPotentialQuote *) item->data;
Account *split_acct = xaccSplitGetAccount(q->split);
GNCAccountType acct_type = xaccAccountGetType(split_acct);
/* at this point, we already know it's a split with a zero amount */
if((acct_type == STOCK) ||
(acct_type == MUTUAL) ||
(acct_type == CURRENCY)) {
/* this is a quote -- file it in the db and kill the split */
Transaction *txn = xaccSplitGetParent(q->split);
GNCPrice *price = gnc_price_create();
Timespec time = xaccTransRetDateEnteredTS(txn);
gnc_price_set_commodity(price, xaccAccountGetSecurity(split_acct));
gnc_price_set_currency(price, xaccAccountGetCurrency(split_acct));
gnc_price_set_time(price, &time);
gnc_price_set_source(price, "old-file-import");
gnc_price_set_type(price, "unknown");
gnc_price_set_value(price, q->price);
if(!gnc_pricedb_add_price(*prices, price)) {
PERR("problem adding price to pricedb.\n");
}
gnc_price_unref(price);
xaccTransBeginEdit(txn);
xaccSplitDestroy(q->split);
xaccTransCommitEdit(txn);
}
g_free(item->data);
item->data = NULL;
item = item->next;
}
g_slist_free(potential_quotes);
potential_quotes = NULL;
return TRUE;
}
/** PROTOTYPES ******************************************************/
static Account *locateAccount (int acc_id);
@ -197,8 +283,8 @@ static int readTSDate( int fd, Timespec *, int token );
/*******************************************************/
GNCBackendError
xaccGetGncBinFileIOError (void)
GNCBackendError
gnc_book_get_binfile_io_error(void)
{
/* reset the error code */
int rc = error_code;
@ -307,15 +393,15 @@ gnc_commodity_import_legacy(const char * currency_name) {
\********************************************************************/
/********************************************************************\
* xaccReadAccountGroup *
* reads in the data from file descriptor *
* *
* Args: fd -- the file descriptor to read the data from *
* Args: book -- the book in which to store the data *
* fd -- the file descriptor to read the data from *
* Return: the struct with the program data in it *
\********************************************************************/
static AccountGroup *
xaccReadAccountGroup(int fd)
{
static gboolean
gnc_load_financials_from_fd(GNCBook *book, int fd)
{
int err=0;
int token=0;
int num_unclaimed;
@ -328,7 +414,7 @@ xaccReadAccountGroup(int fd)
if( 0 > fd )
{
error_code = ERR_FILEIO_FILE_NOT_FOUND;
return NULL;
return FALSE;
}
/* Read in the file format token */
@ -336,7 +422,7 @@ xaccReadAccountGroup(int fd)
if( sizeof(int) != err )
{
error_code = ERR_FILEIO_FILE_EMPTY;
return NULL;
return FALSE;
}
XACC_FLIP_INT (token);
@ -350,36 +436,33 @@ xaccReadAccountGroup(int fd)
* with, warn the user */
if( VERSION < token ) {
error_code = ERR_FILEIO_FILE_TOO_NEW;
return NULL;
return FALSE;
}
/* FIXME: is this OK (i.e. direct hashes for ints?) */
ids_to_finished_accounts = g_hash_table_new(g_direct_hash, g_direct_equal);
if(!ids_to_finished_accounts) {
error_code = ERR_FILEIO_ALLOC;
return(NULL);
error_code = ERR_BACKEND_ALLOC;
return FALSE;
}
ids_to_unfinished_accounts = g_hash_table_new(g_direct_hash, g_direct_equal);
if(!ids_to_unfinished_accounts) {
error_code = ERR_FILEIO_ALLOC;
error_code = ERR_BACKEND_ALLOC;
g_hash_table_destroy(ids_to_finished_accounts);
ids_to_finished_accounts = NULL;
return(NULL);
return FALSE;
}
potential_quotes = NULL;
/* disable logging during load; otherwise its just a mess */
xaccLogDisable();
holder = xaccMallocAccountGroup();
grp = readGroup (fd, NULL, token);
/* mark the newly read group as saved, since the act of putting
* it together will have caused it to be marked up as not-saved.
*/
xaccGroupMarkSaved (grp);
/* auto-number the accounts, if they are not already numbered */
xaccGroupDepthAutoCode (grp);
@ -409,11 +492,35 @@ xaccReadAccountGroup(int fd)
g_hash_table_destroy(ids_to_unfinished_accounts);
ids_to_unfinished_accounts = NULL;
{
GNCPriceDB *tmpdb;
if(cvt_potential_prices_to_pricedb_and_cleanup(&tmpdb)) {
GNCPriceDB *db = gnc_book_get_pricedb(book);
if(db) gnc_pricedb_destroy(db);
gnc_book_set_pricedb(book, tmpdb);
} else {
PWARN("pricedb import failed.");
error_code = ERR_BACKEND_MISC;
gnc_pricedb_destroy(tmpdb);
}
}
/* set up various state that is not normally stored in the byte stream */
xaccRecomputeGroupBalance (grp);
xaccLogEnable();
return grp;
{
AccountGroup *g = gnc_book_get_group(book);
if (g) xaccFreeAccountGroup(g);
gnc_book_set_group(book, grp);
}
/* mark the newly read book as saved, since the act of putting it
* together will have caused it to be marked up as not-saved. */
gnc_book_mark_saved(book);
return (error_code == ERR_BACKEND_NO_ERR);
}
/********************************************************************\
@ -423,25 +530,30 @@ xaccReadAccountGroup(int fd)
* Args: datafile - the file to load the data from *
* Return: the struct with the program data in it *
\********************************************************************/
AccountGroup *
xaccReadGncBinAccountGroupFile( const char *datafile )
{
void
gnc_book_load_from_binfile(GNCBook *book)
{
int fd;
AccountGroup *grp = 0x0;
const gchar *datafile = gnc_book_get_file_path(book);
if(!datafile) {
error_code = ERR_BACKEND_MISC;
return;
}
maingrp = 0x0;
error_code = ERR_BACKEND_NO_ERR;
fd = open( datafile, RFLAGS, 0 );
if( 0 > fd )
{
if( 0 > fd ) {
error_code = ERR_FILEIO_FILE_NOT_FOUND;
return NULL;
}
grp = xaccReadAccountGroup (fd);
return;
}
if(!gnc_load_financials_from_fd(book, fd)) return;
close(fd);
return grp;
return;
}
/********************************************************************\
@ -644,17 +756,15 @@ readAccount( int fd, AccountGroup *grp, int token )
DEBUG ("expecting %d transactions \n", numTrans);
/* read the transactions */
for( i=0; i<numTrans; i++ )
{
for( i=0; i<numTrans; i++ ) {
Transaction *trans;
trans = readTransaction( fd, acc, token );
if( trans == NULL )
{
if(trans == NULL ) {
PERR ("Short Transaction Read: \n"
"\texpected %d got %d transactions \n",numTrans,i);
"\texpected %d got %d transactions \n", numTrans, i);
break;
}
}
}
/* Not needed now. Since we always look in ids_to_finished_accounts
* first, it doesn't matter if we don't ever delete anything from
@ -802,7 +912,7 @@ xaccTransSetAction (Transaction *trans, const char *action)
\********************************************************************/
static Transaction *
readTransaction( int fd, Account *acc, int token )
readTransaction( int fd, Account *acc, int revision)
{
int err=0;
int acc_id;
@ -821,13 +931,7 @@ readTransaction( int fd, Account *acc, int token )
trans = xaccMallocTransaction();
xaccTransBeginEdit (trans);
/* add in one split -- xaccMallocTransaction no longer auto-creates them. */
{
Split *s = xaccMallocSplit ();
xaccTransAppendSplit (trans, s);
}
tmp = readString( fd, token );
tmp = readString( fd, revision );
if (NULL == tmp)
{
PERR ("Premature end of Transaction at num");
@ -838,9 +942,9 @@ readTransaction( int fd, Account *acc, int token )
xaccTransSetNum (trans, tmp);
free (tmp);
if (7 >= token) {
if (revision <= 7) {
time_t secs;
secs = readDMYDate( fd, token );
secs = readDMYDate( fd, revision );
if( 0 == secs )
{
PERR ("Premature end of Transaction at date");
@ -855,7 +959,7 @@ readTransaction( int fd, Account *acc, int token )
int rc;
/* read posted date first ... */
rc = readTSDate( fd, &ts, token );
rc = readTSDate( fd, &ts, revision );
if( -1 == rc )
{
PERR ("Premature end of Transaction at date");
@ -866,7 +970,7 @@ readTransaction( int fd, Account *acc, int token )
xaccTransSetDateTS (trans, &ts);
/* then the entered date ... */
rc = readTSDate( fd, &ts, token );
rc = readTSDate( fd, &ts, revision );
if( -1 == rc )
{
PERR ("Premature end of Transaction at date");
@ -877,7 +981,7 @@ readTransaction( int fd, Account *acc, int token )
xaccTransSetDateEnteredTS (trans, &ts);
}
tmp = readString( fd, token );
tmp = readString( fd, revision );
if( NULL == tmp )
{
PERR ("Premature end of Transaction at description");
@ -892,8 +996,8 @@ readTransaction( int fd, Account *acc, int token )
deprecated, and we don't think anyone ever used them anyway, but
to be safe, if we find one, we store it in the old-docref slot, a
la old-price-source. */
if (8 <= token) {
tmp = readString( fd, token );
if (revision >= 8) {
tmp = readString( fd, revision );
if( NULL == tmp ) {
PERR ("Premature end of Transaction at docref");
xaccTransDestroy(trans);
@ -916,17 +1020,10 @@ readTransaction( int fd, Account *acc, int token )
* moved to splits. Thus, vast majority of stuff below
* is skipped
*/
if (4 >= token)
{
Split * s;
if (revision <= 4) {
Split* s;
/* The code below really wants to assume that there are a pair
* of splits in every transaction, so make it so.
*/
s = xaccMallocSplit ();
xaccTransAppendSplit (trans, s);
tmp = readString( fd, token );
tmp = readString( fd, revision );
if( NULL == tmp )
{
PERR ("Premature end of Transaction at memo");
@ -941,9 +1038,9 @@ readTransaction( int fd, Account *acc, int token )
free (tmp);
/* action first introduced in version 3 of the file format */
if (3 <= token)
if (revision >= 3)
{
tmp = readString( fd, token );
tmp = readString( fd, revision );
if( NULL == tmp )
{
PERR ("Premature end of Transaction at action");
@ -973,12 +1070,21 @@ readTransaction( int fd, Account *acc, int token )
xaccTransCommitEdit (trans);
return NULL;
}
/* The code below really wants to assume that there are a pair
* of splits in every transaction, so make it so.
*/
s = xaccMallocSplit ();
xaccTransAppendSplit (trans, s);
s = xaccMallocSplit ();
xaccTransAppendSplit (trans, s);
s = xaccTransGetSplit (trans, 0);
xaccSplitSetReconcile (s, recn);
s = xaccTransGetSplit (trans, 1);
xaccSplitSetReconcile (s, recn);
if( 1 >= token ) {
if(revision <= 1) {
/* Note: this is for version 0 of file format only.
* What used to be reconciled, is now cleared... transactions
* aren't reconciled until you get your bank statement, and
@ -996,8 +1102,9 @@ readTransaction( int fd, Account *acc, int token )
* with the amount recorded as pennies.
* Version 2 and above store the share amounts and
* prices as doubles. */
if (1 == token) {
if (1 == revision) {
int amount;
err = read( fd, &amount, sizeof(int) );
if( sizeof(int) != err )
{
@ -1010,7 +1117,12 @@ readTransaction( int fd, Account *acc, int token )
num_shares = 0.01 * ((double) amount); /* file stores pennies */
s = xaccTransGetSplit (trans, 0);
DxaccSplitSetShareAmount (s, num_shares);
/* Version 1 files did not do double-entry */
s = xaccTransGetSplit (trans, 0);
xaccAccountInsertSplit( acc, s );
} else {
Account *peer_acc;
double damount;
/* first, read number of shares ... */
@ -1037,15 +1149,12 @@ readTransaction( int fd, Account *acc, int token )
XACC_FLIP_DOUBLE (damount);
share_price = damount;
s = xaccTransGetSplit (trans, 0);
DxaccSplitSetSharePriceAndAmount (s, share_price, num_shares);
}
DEBUG ("num_shares %f \n", num_shares);
/* Read the account numbers for double-entry */
/* These are first used in Version 2 of the file format */
if (1 < token) {
Account *peer_acc;
/* Read the account numbers for double-entry */
/* These are first used in Version 2 of the file format */
/* first, read the credit account number */
err = read( fd, &acc_id, sizeof(int) );
if( err != sizeof(int) )
@ -1064,6 +1173,8 @@ readTransaction( int fd, Account *acc, int token )
s = xaccTransGetSplit (trans, 0);
if (peer_acc) xaccAccountInsertSplit( peer_acc, s);
mark_potential_quote(s, share_price, num_shares);
/* next read the debit account number */
err = read( fd, &acc_id, sizeof(int) );
if( err != sizeof(int) )
@ -1077,74 +1188,50 @@ readTransaction( int fd, Account *acc, int token )
DEBUG ("debit %d\n", acc_id);
peer_acc = locateAccount (acc_id);
if (peer_acc) {
Split *split;
s = xaccTransGetSplit (trans, 0);
split = xaccTransGetSplit (trans, 1);
Split *s0 = xaccTransGetSplit (trans, 0);
Split *s1 = xaccTransGetSplit (trans, 1);
/* duplicate many of the attributes in the credit split */
DxaccSplitSetSharePriceAndAmount (split, share_price, -num_shares);
xaccSplitSetReconcile (split, xaccSplitGetReconcile (s));
xaccSplitSetMemo (split, xaccSplitGetMemo (s));
xaccSplitSetAction (split, xaccSplitGetAction (s));
xaccAccountInsertSplit (peer_acc, split);
DxaccSplitSetSharePriceAndAmount (s1, share_price, -num_shares);
xaccSplitSetReconcile (s1, xaccSplitGetReconcile (s0));
xaccSplitSetMemo (s1, xaccSplitGetMemo (s0));
xaccSplitSetAction (s1, xaccSplitGetAction (s0));
xaccAccountInsertSplit (peer_acc, s1);
mark_potential_quote(s1, share_price, -num_shares);
}
} else {
/* Version 1 files did not do double-entry */
s = xaccTransGetSplit (trans, 0);
xaccAccountInsertSplit( acc, s );
}
} else { /* else, read version 5 and above files */
Split *split;
int offset = 0;
const char *notes = NULL;
if (5 == token)
{
Split *s = trans->splits->data;
if (revision == 5) {
/* Version 5 files included a split that immediately
* followed the transaction, before the destination splits.
* Later versions don't have this. */
offset = 1;
split = readSplit (fd, token);
xaccRemoveEntity(&s->guid);
xaccFreeSplit (s);
trans->splits->data = split;
split->parent = trans;
Split *split = readSplit (fd, revision);
xaccTransAppendSplit(trans, split);
}
/* read number of splits */
err = read( fd, &(numSplits), sizeof(int) );
if( err != sizeof(int) )
{
if( err != sizeof(int) ) {
PERR ("Premature end of Transaction at num-splits");
xaccTransDestroy(trans);
xaccTransCommitEdit (trans);
return NULL;
}
XACC_FLIP_INT (numSplits);
for (i=0; i<numSplits; i++) {
split = readSplit (fd, token);
if (0 == i+offset) {
Split *s = trans->splits->data;
/* the first split has been malloced. just replace it */
xaccRemoveEntity (&s->guid);
xaccFreeSplit (s);
trans->splits->data = split;
split->parent = trans;
} else {
xaccTransAppendSplit(trans, split);
}
if (!notes) {
notes = xaccSplitGetMemo (split);
if (notes)
xaccTransSetNotes (trans, notes);
}
}
for (i = 0; i < numSplits; i++) {
Split *split = readSplit(fd, revision);
xaccTransAppendSplit(trans, split);
if(!notes) {
notes = xaccSplitGetMemo (split);
if(notes) xaccTransSetNotes (trans, notes);
}
}
}
xaccTransCommitEdit (trans);
return trans;
@ -1275,6 +1362,7 @@ readSplit ( int fd, int token )
return NULL;
}
XACC_FLIP_DOUBLE (share_price);
DxaccSplitSetSharePriceAndAmount (split, share_price, num_shares);
DEBUG ("num_shares %f \n", num_shares);
@ -1293,6 +1381,7 @@ readSplit ( int fd, int token )
peer_acc = locateAccount (acc_id);
xaccAccountInsertSplit (peer_acc, split);
mark_potential_quote(split, share_price, num_shares);
return split;
}
@ -1486,7 +1575,7 @@ xaccWriteGncBinAccountGroup(FILE *f, AccountGroup *grp )
accounts_to_ids = g_hash_table_new(g_direct_hash, g_direct_equal);
if(!accounts_to_ids) {
error_code = ERR_FILEIO_ALLOC;
error_code = ERR_BACKEND_ALLOC;
return(-1);
}

View File

@ -31,47 +31,26 @@
#define __IO_GNCBIN_H__
#include "Backend.h"
#include "Group.h"
#include "FileIO.h"
#include "gnc-book.h"
/** PROTOTYPES ******************************************************/
/*
* The xaccReadAccountGroupFD() and xaccWriteAccountGroupFD()
* routines read and write the GnuCash "xacc" byte stream (file)
* format. This is a binary format that exactly represents all of the
* data that can appear in the AccountGroup structure as a sequence of
* bytes. The Read and Write routines are exact inverses of each other:
* that is, there is no loss of data involved in converting an
* AccountGroup into a byte stream and back again. These routines
* can be thought of as implementing a kind of "object persistance"
* for the AccountGroup object. Note that these routines can also
* be used to provide inter-process communication using either pipes or
* sockets. That is, by writing into a socket or pipe with the
* xaccWriteAccountGroupFD() routine, and reading from it at the other
* end with the xaccReadAccountGroupFD() routine, an exact duplicate of
* the AccountGroup can be created in a different process.
* NOTE: These routines should not be used directly for file IO. They
* are not inherently safe against file-locking errors. For direct
* file IO, the gnc-book's higher level functions should be used.
*
* NOTE: These routines should not be used directly for file IO.
* They are not inherently safe against file-locking errors.
* For direct file IO, the Session object should be used.
* gnc_book_load_from_binfile() will load the financial data
* represented by the book's file_path into the indicated book.
*
* The xaccReadOldBinAccountGroupFile() method will read the "xacc"
* format byte stream from the indicated file, and build and return
* the corresponding AccountGroup structure.
*
* If a read error occurred during reading, the returned value
* may or may not be null. Use the xaccGetOldBinFileIOError() routine
* to check for read errors.
*
* The xaccGetOldBinFileIOError() method will return an error code for
* any error detected that occured during reading or writing. It
* will reset the error code after being called. The current
* implementation can be thought of as a "stack of depth one", and
* this routine as a "pop". Future implementations may have a
* deeper stack.
* gnc_book_get_binfile_io_error() will return an error code for any
* error detected that occured during reading or writing. It will
* reset the error code after being called. The current
* implementation can be thought of as a "stack of depth one", and
* this routine as a "pop". Future implementations may have a
* deeper stack.
* */
AccountGroup *xaccReadGncBinAccountGroupFile (const char *filename);
GNCBackendError xaccGetGncBinFileIOError (void);
void gnc_book_load_from_binfile(GNCBook *book);
GNCBackendError gnc_book_get_binfile_io_error(void);
#endif /* __IO_GNCBIN_H__ */

57
src/engine/io-gncxml-p.h Normal file
View File

@ -0,0 +1,57 @@
/********************************************************************\
* io-gncxml-p.h - private header for xml read code *
* *
* Copyright (C) 1999-2001 Rob Browning *
* *
* 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 *
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
* Boston, MA 02111-1307, USA gnu@gnu.org *
* *
********************************************************************/
#ifndef __IO_GNCXML_P_H__
#define __IO_GNCXML_P_H__
#include "sixtp.h"
#include "io-gncxml.h"
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 account group */
AccountGroup *account_group;
/* The pricedb */
GNCPriceDB *pricedb;
/* The query */
Query *query;
GNCParseErr error;
} GNCParseStatus;
#endif

View File

@ -16,9 +16,12 @@
#include "gnc-xml-helper.h"
#include "Account.h"
#include "Query.h"
#include "gnc-pricedb.h"
#include "gnc-engine-util.h"
#include "gnc-book-p.h"
#include "io-gncxml.h"
#include "io-gncxml-p.h"
#include "sixtp.h"
#include "sixtp-stack.h"
@ -48,33 +51,7 @@
*/
/* static short module = MOD_IO; */
/* ========================================================== */
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 account group */
AccountGroup *account_group;
/* The query */
Query *query;
GNCParseErr error;
} GNCParseStatus;
static short module = MOD_IO;
/* ================================================================= */
/* <version> (lineage <gnc>)
@ -283,6 +260,7 @@ gncxml_setup_for_read (GNCParseStatus *global_parse_status)
global_parse_status->seen_version = FALSE;
global_parse_status->gnc_parser = gnc_pr;
global_parse_status->account_group = NULL;
global_parse_status->pricedb = NULL;
global_parse_status->query = NULL;
global_parse_status->error = GNC_PARSE_ERR_NONE;
@ -292,16 +270,18 @@ gncxml_setup_for_read (GNCParseStatus *global_parse_status)
/* ================================================================== */
gboolean
gncxml_read(const gchar *filename,
AccountGroup **result_group)
gnc_book_load_from_xml_file(GNCBook *book)
{
gboolean parse_ok;
gpointer parse_result = NULL;
sixtp *top_level_pr;
GNCParseStatus global_parse_status;
const gchar *filename;
g_return_val_if_fail(book, FALSE);
filename = gnc_book_get_file_path(book);
g_return_val_if_fail(filename, FALSE);
g_return_val_if_fail(result_group, FALSE);
top_level_pr = gncxml_setup_for_read (&global_parse_status);
g_return_val_if_fail(top_level_pr, FALSE);
@ -314,8 +294,29 @@ gncxml_read(const gchar *filename,
sixtp_destroy(top_level_pr);
if(parse_ok && global_parse_status.account_group) {
*result_group = global_parse_status.account_group;
if(parse_ok) {
if(!global_parse_status.account_group)
return FALSE;
{
AccountGroup *g = gnc_book_get_group(book);
if(g) xaccFreeAccountGroup(g);
gnc_book_set_group(book, global_parse_status.account_group);
}
if(global_parse_status.pricedb)
{
GNCPriceDB *db = gnc_book_get_pricedb(book);
if(db) gnc_pricedb_destroy(db);
gnc_book_set_pricedb(book, global_parse_status.pricedb);
}
else
{
gnc_book_set_pricedb(book, gnc_pricedb_create());
}
return(TRUE);
} else {
return(FALSE);
@ -324,6 +325,40 @@ gncxml_read(const gchar *filename,
/* ================================================================== */
gboolean
gnc_is_xml_data_file(const gchar *filename)
{
FILE *f = NULL;
char first_chunk[256];
const char* cursor = NULL;
ssize_t num_read;
gboolean result = FALSE;
g_return_val_if_fail(filename, result);
f = fopen(filename, "r");
g_return_val_if_fail(f, result);
num_read = fread(first_chunk, sizeof(char), sizeof(first_chunk) - 1, f);
if(num_read == 0) goto cleanup_and_exit;
first_chunk[num_read] = '\0';
cursor = first_chunk;
while(*cursor && isspace(*cursor)) cursor++;
if(cursor && strncmp(cursor, "<?xml", 5) == 0) {
result = TRUE;
}
cleanup_and_exit:
if(f) fclose(f);
return(result);
}
/* ================================================================== */
#if 0
static GNCParseStatus*
read_from_buf(char *bufp, int bufsz)
{
@ -389,36 +424,6 @@ gncxml_read_query (char * bufp, int bufsz)
return ret;
}
/* ================================================================== */
gboolean
is_gncxml_file(const gchar *filename)
{
FILE *f = NULL;
char first_chunk[256];
const char* cursor = NULL;
ssize_t num_read;
gboolean result = FALSE;
g_return_val_if_fail(filename, result);
f = fopen(filename, "r");
g_return_val_if_fail(f, result);
num_read = fread(first_chunk, sizeof(char), sizeof(first_chunk) - 1, f);
if(num_read == 0) goto cleanup_and_exit;
first_chunk[num_read] = '\0';
cursor = first_chunk;
while(*cursor && isspace(*cursor)) cursor++;
if(cursor && strncmp(cursor, "<?xml", 5) == 0) {
result = TRUE;
}
cleanup_and_exit:
if(f) fclose(f);
return(result);
}
#endif
/* ======================= END OF FILE ============================== */

View File

@ -14,7 +14,7 @@
* e.g. even some simple #define macros might help here ...
*
* HISTORY:
* Initial code by Rob L. Browning 4Q 2000
* Initial code by Rob Browning 4Q 2000
* Tuneups by James LewisMoss Dec 2000-Feb 2001
* Generic I/O hack by Linas Vepstas January 2001
*
@ -81,6 +81,8 @@ static const gchar *gncxml_emacs_trailer =
"<!-- mode: xml -->\n"
"<!-- End: -->\n";
/* ============================================================== */
static gboolean
gncxml_append_emacs_trailer(const gchar *filename)
{
@ -98,6 +100,7 @@ gncxml_append_emacs_trailer(const gchar *filename)
return fclose(toappend);
}
#if 0
/* =============================================================== */
/* create a new xml document and poke all the query terms into it. */
@ -134,16 +137,20 @@ gncxml_new_query_doc (Query *q)
return doc;
}
#endif
/* =============================================================== */
/* create a new xml document and poke all account & txn data into it. */
static xmlDocPtr
gncxml_newdoc (AccountGroup *group, int do_txns)
gncxml_newdoc (GNCBook *book, int do_txns)
{
xmlDocPtr doc;
xmlNodePtr ledger_data;
xmlNodePtr tmpnode;
AccountGroup *group = gnc_book_get_group(book);
GNCPriceDB *pricedb = gnc_book_get_pricedb(book);
doc = xmlNewDoc("1.0");
doc->xmlRootNode = xmlNewDocNode(doc, NULL, "gnc", NULL);
@ -162,7 +169,13 @@ gncxml_newdoc (AccountGroup *group, int do_txns)
}
if(!xml_add_commodity_restorers(ledger_data)) {
PERR ("couldn't commodity restore");
PERR ("couldn't create commodity restorers");
xmlFreeDoc(doc);
return 0x0;
}
if(!xml_add_gnc_pricedb(ledger_data, "pricedb", pricedb)) {
PERR ("couldn't create pricedb");
xmlFreeDoc(doc);
return 0x0;
}
@ -184,6 +197,8 @@ gncxml_newdoc (AccountGroup *group, int do_txns)
return doc;
}
#if 0
/* =============================================================== */
void
@ -229,18 +244,20 @@ gncxml_write_query_to_buf (Query *q, char **bufp, int *sz)
PINFO ("wrote %d bytes", *sz);
}
#endif
/* =============================================================== */
/* write the account group to a filename */
gboolean
gncxml_write(AccountGroup *group, const gchar *filename)
gnc_book_write_to_xml_file(GNCBook *book, const gchar *filename)
{
xmlDocPtr doc;
int status;
if (!group || !filename) return FALSE;
if(!filename) return FALSE;
doc = gncxml_newdoc (group, 1);
doc = gncxml_newdoc (book, 1);
if (!doc) return FALSE;
xmlIndentTreeOutput = TRUE;

View File

@ -12,30 +12,25 @@
#include <glib.h>
#include "Group.h"
#include "gnc-book.h"
#include "Query.h"
/* read in an account group from a file */
gboolean gncxml_read(const gchar *filename, AccountGroup **result_group);
/* FIXME: eventually, we probably need to add an error stack
accessable via gnc_book_get_xml_io_error() a la binfile. */
/* read in an account group from a file */
gboolean gnc_book_load_from_xml_file(GNCBook *book);
/* write all account info to a file */
gboolean gnc_book_write_to_xml_file(GNCBook *book, const char *filename);
#if 0
/* read an account group from a buffer in memory
* the pointer bufp must point at the ascii xml, and bufsz
* must be the size of the buffer.
*/
AccountGroup *
gncxml_read_from_buf (char *bufp, int bufsz);
Query *
gncxml_read_query (char *bufp, int bufsz);
/* write all account info to a file */
gboolean gncxml_write(AccountGroup *group, const gchar *name);
/* The is_gncxml_file() routine checks to see if the first few
* chars of the file look like gnc-xml data.
*/
gboolean is_gncxml_file(const gchar *name);
AccountGroup *gncxml_read_from_buf (char *bufp, int bufsz);
/* write all account info into memory. This routine returns a
* pointer to freshly malloced memory in bufp. You muse free
@ -43,14 +38,22 @@ gboolean is_gncxml_file(const gchar *name);
* returned in sz
*/
void gncxml_write_to_buf (AccountGroup *group, char **bufp, int *sz);
/*
* write only the account and currency info to the buf, do *not*
* write any of the splits or transactions
*/
void gncxml_write_group_to_buf (AccountGroup *group, char **bufp, int *sz);
/* write the query terms to memory */
void gncxml_write_query_to_buf (Query *q, char **bufp, int *sz);
#endif
/* write/read query terms to/from memory */
void gncxml_write_query_to_buf (Query *q, char **bufp, int *sz);
Query *gncxml_read_query (char *bufp, int bufsz);
/* The is_gncxml_file() routine checks to see if the first few
* chars of the file look like gnc-xml data.
*/
gboolean gnc_is_xml_data_file(const gchar *name);
#endif /* __IO_GNCXML_H__ */

View File

@ -10,6 +10,19 @@
#include "GNCId.h"
#include "kvp_frame.h"
xmlNodePtr
text_to_dom_tree(const char *tag, const char *str)
{
xmlNodePtr result;
g_return_val_if_fail(tag, NULL);
g_return_val_if_fail(str, NULL);
result = xmlNewNode(NULL, tag);
g_return_val_if_fail(result, NULL);
xmlNodeAddContent(result, str);
return result;
}
xmlNodePtr
guid_to_dom_tree(const char *tag, const GUID* gid)
{

View File

@ -14,6 +14,7 @@
#include "gnc-numeric.h"
#include "kvp_frame.h"
xmlNodePtr text_to_dom_tree(const char *tag, const char *str);
xmlNodePtr guid_to_dom_tree(const char *tag, const GUID* gid);
xmlNodePtr commodity_ref_to_dom_tree(const char *tag, const gnc_commodity *c);
xmlNodePtr timespec_to_dom_tree(const char *tag, const Timespec *spec);

View File

@ -368,14 +368,17 @@ dom_tree_to_kvp_frame(xmlNodePtr node)
gchar *
dom_tree_to_text(xmlNodePtr tree)
{
/* Expect *only* text and comment sibling nodes. Ignore comment
nodes and collapse text nodes into one string. Return NULL if
expectations are unsatisfied.
/* Expect *only* text and comment sibling nodes in the given tree --
which actually may only be a "list". i.e. if you're trying to
extract bar from <foo>bar</foo>, pass in <foo>->xmlChildrenNode
to this function. This expectation is different from the rest of
the dom_tree_to_* converters...
Ignores comment nodes and collapse text nodes into one string.
Returns NULL if expectations are unsatisfied.
If there are a lot of text sub-nodes, this algorithm may be
inefficient because it'll reallocate a lot.
*/
inefficient as it will reallocate a lot. */
gboolean ok = TRUE;
xmlNodePtr current;
@ -392,7 +395,9 @@ dom_tree_to_text(xmlNodePtr tree)
case XML_COMMENT_NODE:
break;
default:
PERR("dom_tree_to_text: hit illegal node type while extracting text.");
PERR("dom_tree_to_text: hit illegal node while extracting text.");
PERR(" (name %s) (type %d) (content %s)",
current->name, current->type, current->content);
current = NULL;
ok = FALSE;
break;

View File

@ -26,10 +26,15 @@ sixtp* query_server_parser_new (void);
sixtp* kvp_frame_parser_new(void);
/* from sixtp-to-dom-parser.c */
sixtp* sixtp_dom_parser_new(sixtp_end_handler ender);
/* Create a parser that will turn the entire sub-tree into a DOM tree
an pass it in as (don't put anything into parent_data)
you must deal with the xml tree in *result.
*/
sixtp* sixtp_dom_parser_new(sixtp_end_handler ender,
sixtp_result_handler cleanup_result_by_default_func,
sixtp_result_handler cleanup_result_on_fail_func);
sixtp* gnc_pricedb_parser_new(void);
#endif /* _SIXTP_PARSERS_H_ */

View File

@ -21,12 +21,18 @@ static gboolean dom_start_handler(
if(parent_data == NULL)
{
thing = xmlNewNode(global_namespace, tag);
/* only publish the result if we're the parent */
*result = thing;
}
else
{
thing = xmlNewChild((xmlNodePtr)parent_data, global_namespace,
tag, NULL);
thing = xmlNewChild((xmlNodePtr) parent_data,
global_namespace,
tag,
NULL);
*result = NULL;
}
*data_for_children = thing;
if(attrs != NULL)
{
@ -36,10 +42,32 @@ static gboolean dom_start_handler(
atptr += 2;
}
}
return TRUE;
}
*result = thing;
*data_for_children = thing;
static void
dom_fail_handler(gpointer data_for_children,
GSList* data_from_children,
GSList* sibling_data,
gpointer parent_data,
gpointer global_data,
gpointer *result,
const gchar *tag)
{
if(*result) xmlFreeNode(*result);
}
static gboolean is_whitespace(const char *text, int len)
{
int i;
for(i = 0; i < len; i++)
{
if(!isspace(text[i]))
{
return FALSE;
}
}
return TRUE;
}
@ -55,7 +83,10 @@ static gboolean dom_chars_handler(
}
sixtp* sixtp_dom_parser_new(sixtp_end_handler ender)
sixtp *
sixtp_dom_parser_new(sixtp_end_handler ender,
sixtp_result_handler cleanup_result_by_default_func,
sixtp_result_handler cleanup_result_on_fail_func)
{
sixtp *top_level;
@ -66,11 +97,22 @@ sixtp* sixtp_dom_parser_new(sixtp_end_handler ender)
SIXTP_START_HANDLER_ID, dom_start_handler,
SIXTP_CHARACTERS_HANDLER_ID, dom_chars_handler,
SIXTP_END_HANDLER_ID, ender,
SIXTP_FAIL_HANDLER_ID, dom_fail_handler,
SIXTP_NO_MORE_HANDLERS)))
{
return NULL;
}
if(cleanup_result_by_default_func)
{
sixtp_set_cleanup_result(top_level, cleanup_result_by_default_func);
}
if(cleanup_result_by_default_func)
{
sixtp_set_result_fail(top_level, cleanup_result_on_fail_func);
}
if(!sixtp_add_sub_parser(top_level, SIXTP_MAGIC_CATCHER, top_level))
{
sixtp_destroy(top_level);
@ -79,4 +121,3 @@ sixtp* sixtp_dom_parser_new(sixtp_end_handler ender)
return top_level;
}

View File

@ -115,6 +115,10 @@ concatenate_child_result_chars(GSList *data_from_children) {
I don't understand the claim; I'm just going to use
atof or strtod to accomplish this.
RLB writes: FIXME: OK, but at the very least this may cause a
locale dependency. Whoever fixes that, please delete this whole
comment block.
*/
gboolean

View File

@ -1,13 +1,12 @@
#ifndef _SIXTP_WRITERS_H_
#define _SIXTP_WRITERS_H_
#include <config.h>
#include <glib.h>
#include "gnc-xml-helper.h"
#include "Query.h"
#include "gnc-pricedb.h"
gboolean xml_add_account_restorers(xmlNodePtr p, AccountGroup *g);
@ -20,6 +19,8 @@ gboolean xml_add_query_restorers(xmlNodePtr p, Query *q);
gboolean xml_add_txn_and_split_restorers(xmlNodePtr p, AccountGroup *g);
gboolean xml_add_gnc_price(xmlNodePtr p, const char *tag, GNCPrice *db);
gboolean xml_add_gnc_pricedb(xmlNodePtr p, const char *tag, GNCPriceDB *db);
#endif /* _SIXTP_WRITERS_H_ */

View File

@ -1,3 +1,5 @@
#include "config.h"
#include <glib.h>
#include <stdarg.h>
@ -328,7 +330,7 @@ sixtp_sax_start_handler(void *user_data,
gchar *next_parser_tag = NULL;
gboolean lookup_success = FALSE;
sixtp_stack_frame *new_frame = NULL;
g_return_if_fail(pdata->parsing_ok);
current_frame = (sixtp_stack_frame *) pdata->stack->data;

View File

@ -216,7 +216,7 @@ gnc_ui_get_account_field_value_string (Account *account,
break;
case ACCOUNT_BALANCE_EURO :
{
const gnc_commodity * account_currency =
gnc_commodity * account_currency =
xaccAccountGetCurrency(account);
gnc_numeric balance = gnc_ui_account_get_balance(account, FALSE);
gnc_numeric euro_balance = gnc_convert_to_euro(account_currency,
@ -237,7 +237,7 @@ gnc_ui_get_account_field_value_string (Account *account,
break;
case ACCOUNT_TOTAL_EURO :
{
const gnc_commodity * account_currency =
gnc_commodity * account_currency =
xaccAccountGetCurrency(account);
gnc_numeric balance = gnc_ui_account_get_balance(account, TRUE);
gnc_numeric euro_balance = gnc_convert_to_euro(account_currency,
@ -411,7 +411,7 @@ gnc_localeconv (void)
return &lc;
}
const gnc_commodity *
gnc_commodity *
gnc_locale_default_currency (void)
{
static gnc_commodity * currency;
@ -526,7 +526,7 @@ is_decimal_fraction (int fraction, guint8 *max_decimal_places_p)
}
GNCPrintAmountInfo
gnc_commodity_print_info (const gnc_commodity *commodity,
gnc_commodity_print_info (gnc_commodity *commodity,
gboolean use_symbol)
{
GNCPrintAmountInfo info;

View File

@ -100,7 +100,7 @@ PriceSourceCode gnc_get_source_code (const char * codename);
struct lconv * gnc_localeconv (void);
/* Returns the default currency of the current locale. */
const gnc_commodity * gnc_locale_default_currency (void);
gnc_commodity * gnc_locale_default_currency (void);
/* Returns the number of decimal place to print in the current locale */
int gnc_locale_decimal_places (void);
@ -123,7 +123,7 @@ int gnc_locale_decimal_places (void);
typedef struct _GNCPrintAmountInfo
{
const gnc_commodity *commodity; /* may be NULL */
gnc_commodity *commodity; /* may be NULL */
guint8 max_decimal_places;
guint8 min_decimal_places;
@ -137,7 +137,7 @@ typedef struct _GNCPrintAmountInfo
GNCPrintAmountInfo gnc_default_print_info (gboolean use_symbol);
GNCPrintAmountInfo gnc_commodity_print_info (const gnc_commodity *commodity,
GNCPrintAmountInfo gnc_commodity_print_info (gnc_commodity *commodity,
gboolean use_symbol);
GNCPrintAmountInfo gnc_account_quantity_print_info (Account *account,

View File

@ -125,7 +125,7 @@ static void
gnc_account_to_ui(AccountWindow *aw)
{
Account *account = aw_get_account (aw);
const gnc_commodity * commodity;
gnc_commodity * commodity;
const char *string;
gboolean tax_related;
gint pos = 0;
@ -179,7 +179,7 @@ static void
gnc_ui_to_account(AccountWindow *aw)
{
Account *account = aw_get_account (aw);
const gnc_commodity *commodity;
gnc_commodity *commodity;
Account *parent_account;
const char *old_string;
const char *string;
@ -280,7 +280,7 @@ gnc_finish_ok (AccountWindow *aw,
/* do it all again, if needed */
if (aw->dialog_type == NEW_ACCOUNT && aw->subaccount_names)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
Account *parent;
Account *account;
GList *node;
@ -440,11 +440,11 @@ static void
gnc_account_change_currency_security(Account *account,
GHashTable *change_currency,
GHashTable *change_security,
const gnc_commodity * currency,
const gnc_commodity * security)
gnc_commodity * currency,
gnc_commodity * security)
{
const gnc_commodity * old_currency;
const gnc_commodity * old_security;
gnc_commodity * old_currency;
gnc_commodity * old_security;
gboolean new_currency;
gboolean new_security;
GSList *stack;
@ -782,8 +782,8 @@ gnc_edit_account_ok(AccountWindow *aw)
GNCAccountType current_type;
const char *name;
const gnc_commodity * currency;
const gnc_commodity * security;
gnc_commodity * currency;
gnc_commodity * security;
/* check for valid name */
name = gtk_entry_get_text(GTK_ENTRY(aw->name_entry));
@ -1483,7 +1483,7 @@ static AccountWindow *
gnc_ui_new_account_window_internal (Account *base_account,
GList *subaccount_names)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
AccountWindow *aw;
Account *account;

View File

@ -78,10 +78,10 @@ select_modal_callback(const gnc_commodity * arg, void * data) {
* gnc_ui_select_commodity_modal()
********************************************************************/
const gnc_commodity *
gnc_ui_select_commodity_modal(const gnc_commodity * orig_sel,
gnc_commodity *
gnc_ui_select_commodity_modal(gnc_commodity * orig_sel,
GtkWidget * parent) {
const gnc_commodity * retval = NULL;
gnc_commodity * retval = NULL;
SelectCommodityWindow * win =
gnc_ui_select_commodity_create(orig_sel, &select_modal_callback, &retval);
@ -406,7 +406,7 @@ new_modal_callback(const gnc_commodity * arg, void * data) {
* gnc_ui_new_commodity_modal()
********************************************************************/
const gnc_commodity *
gnc_commodity *
gnc_ui_new_commodity_modal(const char * selected_namespace,
GtkWidget * parent) {
gnc_commodity * retval = NULL;

View File

@ -38,11 +38,11 @@ void gnc_ui_select_commodity_destroy(SelectCommodityWindow * w);
void gnc_ui_new_commodity_destroy(NewCommodityWindow * w);
const gnc_commodity *
gnc_ui_select_commodity_modal(const gnc_commodity * orig_sel,
gnc_commodity *
gnc_ui_select_commodity_modal(gnc_commodity * orig_sel,
GtkWidget * parent);
const gnc_commodity *
gnc_commodity *
gnc_ui_new_commodity_modal(const char * default_namespace,
GtkWidget * parent);

View File

@ -496,7 +496,7 @@ close_handler (gpointer user_data)
FinCalcDialog *
gnc_ui_fincalc_dialog_create(void)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
GNCPrintAmountInfo print_info;
FinCalcDialog *fcd;
GtkObject *fcdo;

View File

@ -195,7 +195,7 @@ gnc_option_set_ui_value(GNCOption *option, gboolean use_default)
}
else if (safe_strcmp(type, "currency") == 0)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
commodity = gnc_scm_to_commodity (value);
if (commodity)
@ -206,7 +206,7 @@ gnc_option_set_ui_value(GNCOption *option, gboolean use_default)
}
else if (safe_strcmp(type, "commodity") == 0)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
commodity = gnc_scm_to_commodity (value);
if (commodity)
@ -480,7 +480,7 @@ gnc_option_get_ui_value(GNCOption *option)
}
else if (safe_strcmp(type, "currency") == 0)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
commodity =
gnc_currency_edit_get_currency(GNC_CURRENCY_EDIT(option->widget));
@ -489,7 +489,7 @@ gnc_option_get_ui_value(GNCOption *option)
}
else if (safe_strcmp(type, "commodity") == 0)
{
const gnc_commodity *commodity;
gnc_commodity *commodity;
commodity =
gnc_commodity_edit_get_commodity(GNC_COMMODITY_EDIT(option->widget));

View File

@ -38,7 +38,12 @@
#include "gnc-commodity.h"
#include "gnc-engine.h"
#include "gnc-ui.h"
#include "gnc-pricedb-p.h"
#include "gnc-engine-util.h"
/* This static indicates the debugging module that this .o belongs to. */
static short module = MOD_GUI;
struct _commoditydruid {
GtkWidget * window;
@ -395,7 +400,6 @@ gnc_ui_commodity_druid_comm_check_cb(GnomeDruidPage * page, gpointer druid,
}
}
static void
finish_helper(gpointer key, gpointer value, gpointer data) {
CommodityDruid * cd = data;
@ -404,11 +408,22 @@ finish_helper(gpointer key, gpointer value, gpointer data) {
key);
GList * accts;
GList * node;
GNCBook * book = gncGetCurrentBook();
if(!book) {
PERR("finish_helper - no current book.");
return;
}
/* key is the old mnemonic, value is a pointer to the gnc_commodity
* structure. */
gnc_commodity_table_insert(gnc_engine_commodities(), comm);
/* s/old commodity/new commodity/g in the pricedb */
gnc_pricedb_substitute_commodity(gnc_book_get_pricedb(book),
old_comm,
comm);
/* now replace all the accounts using old_comm with new_comm */
accts = xaccGroupGetSubAccounts(gncGetCurrentGroup());

View File

@ -1184,7 +1184,7 @@ gnc_ui_qif_import_convert(QIFImportWindow * wind) {
char * mnemonic = NULL;
char * namespace = NULL;
char * fullname = NULL;
gchar * row_text[4] = { NULL, NULL, NULL, NULL };
const gchar * row_text[4] = { NULL, NULL, NULL, NULL };
int rownum;
/* get the default currency */
@ -1281,7 +1281,7 @@ gnc_ui_qif_import_convert(QIFImportWindow * wind) {
}
rownum = gtk_clist_append(GTK_CLIST(wind->new_transaction_list),
row_text);
(gchar **) row_text);
retval = gh_cdr(retval);
}
@ -1662,7 +1662,7 @@ refresh_old_transactions(QIFImportWindow * wind, int selection) {
SCM selected;
Transaction * gnc_xtn;
Split * gnc_split;
gchar * row_text[4] = { NULL, NULL, NULL, NULL };
const gchar * row_text[4] = { NULL, NULL, NULL, NULL };
int rownum;
gtk_clist_column_titles_passive (GTK_CLIST(wind->old_transaction_list));
@ -1703,7 +1703,7 @@ refresh_old_transactions(QIFImportWindow * wind, int selection) {
}
rownum = gtk_clist_append(GTK_CLIST(wind->old_transaction_list),
row_text);
(gchar **) row_text);
gnc_clist_set_check (GTK_CLIST(wind->old_transaction_list),
rownum, 3, selected != SCM_BOOL_F);

View File

@ -141,7 +141,7 @@ static void
select_currency_cb(GtkButton * button, gpointer user_data)
{
GNCCommodityEdit *gce = user_data;
const gnc_commodity *new_commodity;
gnc_commodity *new_commodity;
GtkWidget *toplevel;
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
@ -202,7 +202,7 @@ gnc_commodity_edit_new (void)
*/
void
gnc_commodity_edit_set_commodity (GNCCommodityEdit *gce,
const gnc_commodity *commodity)
gnc_commodity *commodity)
{
const char *text;
@ -225,7 +225,7 @@ gnc_commodity_edit_set_commodity (GNCCommodityEdit *gce,
*
* Returns the commodity currently selected by the widget.
*/
const gnc_commodity *
gnc_commodity *
gnc_commodity_edit_get_commodity (GNCCommodityEdit *gce)
{
g_return_val_if_fail(gce != NULL, NULL);

View File

@ -48,7 +48,7 @@ typedef struct {
GtkWidget *entry; /* display of commodity name */
GtkWidget *button; /* button for popping up commodity window */
const gnc_commodity *selected_commodity;
gnc_commodity *selected_commodity;
} GNCCommodityEdit;
typedef struct {
@ -60,9 +60,9 @@ guint gnc_commodity_edit_get_type (void);
GtkWidget *gnc_commodity_edit_new (void);
void gnc_commodity_edit_set_commodity (GNCCommodityEdit *gce,
const gnc_commodity *commodity);
gnc_commodity *commodity);
const gnc_commodity * gnc_commodity_edit_get_commodity (GNCCommodityEdit *gce);
gnc_commodity * gnc_commodity_edit_get_commodity (GNCCommodityEdit *gce);
END_GNOME_DECLS

View File

@ -202,7 +202,7 @@ gnc_currency_edit_set_currency (GNCCurrencyEdit *gce,
*
* Returns the selected currency.
*/
const gnc_commodity *
gnc_commodity *
gnc_currency_edit_get_currency (GNCCurrencyEdit *gce)
{
const char *mnemonic;

View File

@ -57,7 +57,7 @@ GtkWidget *gnc_currency_edit_new (void);
void gnc_currency_edit_set_currency (GNCCurrencyEdit *gce,
const gnc_commodity *currency);
const gnc_commodity * gnc_currency_edit_get_currency (GNCCurrencyEdit *gce);
gnc_commodity * gnc_currency_edit_get_currency (GNCCurrencyEdit *gce);
END_GNOME_DECLS

View File

@ -36,7 +36,6 @@
#include "AccWindow.h"
#include "FileBox.h"
#include "FileDialog.h"
#include "FileIO.h"
#include "MainWindow.h"
#include "SplitLedger.h"
#include "TransLog.h"

View File

@ -105,7 +105,7 @@ static GNCMainInfo * gnc_get_main_info(void);
* kept around for the duration of the calculation. There may, in fact
* be better ways to do this, but none occurred. */
struct _GNCCurrencyAcc {
const gnc_commodity * currency;
gnc_commodity * currency;
gnc_numeric assets;
gnc_numeric profits;
};
@ -117,7 +117,7 @@ typedef struct _GNCCurrencyAcc GNCCurrencyAcc;
* currency, plus (eventually) one for the default currency
* accumulation (like the EURO). */
struct _GNCCurrencyItem {
const gnc_commodity * currency;
gnc_commodity * currency;
GtkWidget *listitem;
GtkWidget *assets_label;
GtkWidget *profits_label;
@ -132,7 +132,7 @@ typedef struct _GNCCurrencyItem GNCCurrencyItem;
* only handles a single currency.
*/
static GNCCurrencyItem *
gnc_ui_build_currency_item(const gnc_commodity * currency)
gnc_ui_build_currency_item(gnc_commodity * currency)
{
GtkWidget *label;
GtkWidget *topbox;
@ -200,7 +200,7 @@ gnc_ui_build_currency_item(const gnc_commodity * currency)
* This will search the given list, and if no accumulator is found,
* will allocate a fresh one. */
static GNCCurrencyAcc *
gnc_ui_get_currency_accumulator(GList **list, const gnc_commodity * currency)
gnc_ui_get_currency_accumulator(GList **list, gnc_commodity * currency)
{
GList *current;
GNCCurrencyAcc *found;
@ -231,7 +231,7 @@ gnc_ui_get_currency_accumulator(GList **list, const gnc_commodity * currency)
* the item into the list. */
static GNCCurrencyItem *
gnc_ui_get_currency_item(GList **list, const gnc_commodity * currency,
gnc_ui_get_currency_item(GList **list, gnc_commodity * currency,
GtkWidget *holder)
{
GList *current;
@ -261,9 +261,9 @@ gnc_ui_accounts_recurse (AccountGroup *group, GList **currency_list,
gnc_numeric amount;
AccountGroup *children;
GNCAccountType account_type;
const gnc_commodity * account_currency;
const gnc_commodity * default_currency;
const gnc_commodity * euro_commodity;
gnc_commodity * account_currency;
gnc_commodity * default_currency;
gnc_commodity * euro_commodity;
GNCCurrencyAcc *currency_accum;
GNCCurrencyAcc *euro_accum = NULL;
GList *list;
@ -368,7 +368,7 @@ gnc_ui_refresh_statusbar (void)
AccountGroup *group;
char asset_string[256];
char profit_string[256];
const gnc_commodity * default_currency;
gnc_commodity * default_currency;
GNCCurrencyAcc *currency_accum;
GNCCurrencyItem *currency_item;
GList *currency_list;
@ -1368,7 +1368,7 @@ gnc_main_create_summary_bar (GnomeApp *app, GNCMainInfo *main_info)
{
GtkWidget *summarybar;
GtkWidget *combo_box;
const gnc_commodity * default_currency;
gnc_commodity * default_currency;
GNCCurrencyItem *def_item;
summarybar = gtk_hbox_new (FALSE, 5);

View File

@ -2113,7 +2113,7 @@ static void
gnc_register_redraw_all_cb (GnucashRegister *g_reg, gpointer data)
{
RegWindow *regData = data;
const gnc_commodity * currency;
gnc_commodity * currency;
GNCPrintAmountInfo print_info;
gnc_numeric amount;
Account *leader;

View File

@ -2,3 +2,4 @@ Makefile
Makefile.in
gnc-prices
gnc-prices-2
price-quote-helper

View File

@ -1,5 +1,6 @@
bin_SCRIPTS = gnc-prices
pkgdata_SCRIPTS=price-quote-helper
perllibdir = ${GNC_LIBDIR}/perl
@ -8,8 +9,8 @@ perlsharedir = ${GNC_SHAREDIR}/perl
EXTRA_DIST = \
.cvsignore \
Quote_example.pl \
gnc-prices-2.in \
gnc-prices.in \
price-qupte-helper.in \
value_portfolio
## We borrow guile's convention and use @-...-@ as the substitution
@ -26,3 +27,13 @@ gnc-prices: gnc-prices.in
mv $@.tmp $@
CLEANFILES += gnc-prices
price-quote-helper: price-quote-helper.in
rm -f $@.tmp
sed < $@.in > $@.tmp \
-e 's:@-PERL-@:${PERL}:g' \
-e 's:@-PERLINCL-@:${PERLINCL}:g'
chmod +x $@.tmp
mv $@.tmp $@
CLEANFILES += price-quote-helper

View File

@ -1,131 +0,0 @@
#!/usr/bin/perl -w
use strict;
use English;
use Finance::Quote;
## Simple program to get quotes and feed them back to gnucash.
## Modified by Paul Fenwick <pjf@cpan.org>, June 2000, to take
## advantage of new Finance::Quote features.
## Input (on standard input - one entry per line and one line per entry)
##
## (fetch "NYSE" "IBM")
## (fetch "nyse" "ibm" "axp")
## (fetch "nasdaq" "jdsu")
## (fetch "nasdaq" "CSCO" "jdsu")
## Output (on standard output, one output form per input line)
## Schemified version of finance-quote's output, so basically an alist
## of alists, as in the example below. Only fields that this script
## knows about (and knows how to convert to scheme) are returned, so
## the conversion function will have to be updated whenever
## Finance::Quote changes.
## On error, result may be just #f, or errors may be stored with each
## quote as indicated in Finance::Quote.
## Exit status
##
## 0 - success
## non-zero - failure
# TODO:
# Is this safe? Can we just double all backslashes and backslash
# escape all double quotes and get the right answer?
# Right now this is more inefficient than it needs to be. We ask for
# each stock individually, but we should batch reqests to the same
# source. Perhaps later...
my $exit_status = 0;
# Create a stockquote object.
my $quoter = Finance::Quote->new();
sub schemify_str {
my($str) = @_;
# Right now this is a hack. We just make sure the outgoing string
# has no double quotes in it by mangling them all to single quotes.
# This is wrong, but it's better than letting dangerous strings
# through. We can always improve this later.
# Have to do this because the perl-mode parser freaks out otherwise.
my $dq = '"';
my $sq = "'";
$str =~ s/$dq/$sq/gmo;
return $str;
}
my @lookup_items = ();
while(<>) {
# This big ugly nasty thing just matches something like this
# ("FOO" "BAR")
# where, rougly speaking, whitespace is allowed almost everywhere,
# this text constitutes the entire line, and the double-quotes and
# parens shown are the only occurences of those characters allowed
# in the line.
# Have to do this because the perl-mode parser freaks out otherwise.
my $dq = '"';
my $security_name;
my $quote_source_name;
if($_ =~ m/^\s* \(\s* $dq ([^$dq]+) $dq \s* $dq([^$dq]+)$dq \s*\) \s*$/ox) {
$security_name = $1;
$quote_source_name = $2;
} else {
my $scm_str = schemify_str($_);
print "(error bad-input-line \"Ignoring bad input line: $scm_str\")\n";
$exit_status |= 1;
# Yes this is ugly, but it really is what we mean here.
next;
}
my %quote_data = $quoter->fetch($quote_source_name,$security_name);
unless($quote_data{$quote_source_name,'success'}) {
# We don't have to schemify the source or name - the regexp filtered it.
print "(error quote-lookup-failed ";
print "\"Lookup of $security_name at $quote_source_name failed.\")\n";
$exit_status |= 1;
next;
}
my $security_price = $quote_data{$security_name, 'price'};
my $quote_date = $quote_data{$security_name, 'date'};
if(!$security_price) {
# We don't have to schemify the source or name - the regexp filtered it.
print "(error price-not-found " .
"\"Couldn't find price for $security_name " .
"in response from $quote_source_name.\")\n";
$exit_status |= 1;
next;
}
## We'll just let gnucash deal with the date...
$quote_date = schemify_str($quote_date);
## Whew. Finally.
print "(quote";
print " (name . \"$security_name\")";
print " (date . \"$quote_date\")";
print " (price . \"$security_price\")\n";
}
exit $exit_status;
__END__

View File

@ -0,0 +1,282 @@
#!@-PERL-@ -w
######################################################################
### price-quote-helper - present a scheme interface to Finance::Quote
### Copyright 2001 Rob Browning <rlb@cs.utexas.edu>
###
### 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
### 59 Temple Place - Suite 330 Fax: +1-617-542-2652
### Boston, MA 02111-1307, USA gnu@gnu.org
######################################################################
use lib '@-PERLINCL-@';
use strict;
use English;
use FileHandle;
use Date::Manip;
use Finance::Quote;
# Input: (on standard input - one entry per line and one line per
# entry, and double quotes must only be delimiters, not string
# content -- remember, we don't have a real scheme parser on the perl
# side :>).
#
# (fetch "NYSE" "IBM")
# (fetch "NYSE" "IBM" "AXP")
# (fetch "NASDAQ" "JDSU")
# (fetch "NASDAQ" "CSCO" "JDSU")
#
# Output (on standard output, one output form per input line):
# Schemified version of finance-quote's output, basically an alist of
# alists, as in the example below. Right now, the only a few fields
# that this script knows about (and knows how to convert to scheme)
# are returned, so the conversion function will have to be updated
# whenever Finance::Quote changes. Right now, you'll get symbol,
# utc, and last, as here:
#
# $ echo '(fetch "NASDAQ" "CSCO")' | ./price-quote-helper
# (("CSCO" (symbol . "CSCO") (utc . 982709400) (last . 26.5625))
# ("JDSU" (symbol . "JDSU") (utc . 982709400) (last . 33.0625)))
# On error, result may be just #f, or errors may be stored with each
# quote as indicated in Finance::Quote. Also, whenever the
# conversion fails, the field will have the value 'failed-conversion,
# and accordingly this symbol will never be a legitimate conversion.
# Exit status
#
# 0 - success
# non-zero - failure
sub schemify_string {
my($str) = @_;
if(!$str) { return "failed-conversion"; }
# FIXME: Is this safe? Can we just double all backslashes and backslash
# escape all double quotes and get the right answer?
# double all backslashes.
my $bs = "\\";
$str =~ s/$bs$bs/$bs$bs/gmo;
# escape all double quotes.
# Have to do this because the perl-mode parser freaks out otherwise.
my $dq = '"';
$str =~ s/$dq/$bs$dq/gmo;
return '"' . $str . '"';
}
sub schemify_boolean {
my($bool) = @_;
if($bool) {
return "#t";
} else {
return "#f";
}
}
sub schemify_num {
my($numstr) = @_;
# This is for normal numbers, not the funny ones like "2.346B".
# For now we don't need to do anything.
if(!$numstr) { return "failed-conversion"; }
if($numstr =~ /^\s*(\d+(\.\d+)?)$/o) {
return $1;
} else {
return "failed-conversion";
}
}
sub schemify_date {
# return the date in epoch seconds.
my ($datestr) = @_;
my $date = ParseDate($datestr);
my $result = UnixDate($date, "%s");
if($result !~ /^(\+|-)?\d+$/) {
$result = "failed-conversion";
}
return("$result");
}
# sub schemify_range {
# #convert range in form ``num1 - num2'' to ``(num1 num2)''.
# }
sub get_quote_utc {
# return the date in utc epoch seconds.
my ($item, $timezone, $quotehash) = @_;
if(!$timezone) { return "failed-conversion"; }
my $datestr = $$quotehash{$item, 'date'};
my $timestr = $$quotehash{$item, 'time'};
if(!$datestr) {
return "failed-conversion";
}
my $parsestr = $datestr;
if($timestr) {
$parsestr .= " $timestr";
}
my $date = Date_ConvTZ(ParseDate($parsestr), $timezone, 'UTC');
my $result = UnixDate($date, "%s");
if($result !~ /^(\+|-)?\d+$/) {
$result = "failed-conversion";
}
return $result;
}
sub schemify_quote {
my($itemname, $quotehash, $indentlevel, $timezone) = @_;
my $scmname = schemify_string($itemname);
my $quotedata = "";
my $field;
my $data;
$field = 'symbol';
$data = schemify_string($$quotehash{$itemname, $field});
$quotedata .= "($field . $data)";
$field = 'utc';
$data = get_quote_utc($itemname, $timezone, $quotehash);
$quotedata .= " ($field . $data)";
$field = 'last';
$data = schemify_num($$quotehash{$itemname, $field});
$quotedata .= " ($field . $data)";
return "($scmname $quotedata)";
}
sub schemify_quotes {
my($items, $quotehash, $timezone) = @_;
my $resultstr = "";
my $i;
my $separator = "";
# we have to pass in @$items because Finance::Quote just uses the
# mangled "$name$field string as the key, so there's no way (I know
# of) to find out which stocks are in a given quotehash, just given
# the quotehash.
foreach $i (@$items) {
$resultstr .= $separator . schemify_quote($i, $quotehash, 2, $timezone);
if(!$separator) { $separator = "\n "; }
}
return "($resultstr)\n";
}
sub get_exchange_timezone {
my($exchange) = @_;
my $tz;
$exchange = lc($exchange);
if($exchange eq "nasdaq") {
$tz = 'EST';
} elsif ($exchange eq "nyse") {
$tz = 'EST';
} else {
return undef;
}
}
sub parse_input_line {
# FIXME: we need to rewrite parsing to handle commands modularly.
# Right now all we do is hard-code "fetch".
my($input) = @_;
# Have to do this because the perl-mode parser freaks out otherwise.
my $dq = '"';
my $exchange;
my @items;
# Make sure we have an opening ( preceeded only by whitespace.
# and kill it off if we do...
if($input !~ s/^\s*\(\s*fetch\s+//o) { return 0; }
# Make sure we have an ending ) followed only by whitespace
# and kill it off if we do...
if($input !~ s/\s*\)\s*$//o) { return 0; }
# Now grab the exchange.
if($input !~ /^$dq([^$dq]+)$dq\s*/o) { return 0; }
$exchange = $1;
$input = $POSTMATCH;
# Now grab all the items.
while($input) {
if($input !~ /^$dq([^$dq]+)$dq\s*/o) { return 0; }
my $item = $1;
push @items, $item;
$input = $POSTMATCH;
}
my @result = ($exchange, \@items);
return \@result;
}
#---------------------------------------------------------------------------
# Runtime.
# Create a stockquote object.
my $quoter = Finance::Quote->new();
my $prgnam = "scmio-finance-quote";
# Make sure USD is the default.
$quoter->set_currency("USD");
while(<>) {
my $result = parse_input_line($_);
if(!$result) {
print STDERR "$prgnam: bad input line ($_)\n";
exit 1;
}
my($exchange, $items) = @$result;
my $quote_data = $quoter->fetch($exchange, @$items);
if(!$quote_data) {
print "#f\n";
exit 1;
}
my $zone = get_exchange_timezone($exchange);
print schemify_quotes(\@$items, $quote_data, $zone);
STDOUT->flush();
}
exit 0;
__END__
## Local Variables:
## mode: perl
## End:

View File

@ -38,6 +38,8 @@ gnc_regular_scm_files = \
options-utilities.scm \
path.scm \
prefs.scm \
price-quotes.scm \
process.scm \
report.scm \
report-html.scm \
report-utilities.scm \

View File

@ -19,6 +19,8 @@
(define gnc:*command-line-remaining* #f)
(gnc:depend "price-quotes.scm")
;;(use-modules (ice-9 getopt-long))
;;(define (gnc:is-boolean-arg? arg)
@ -169,6 +171,26 @@
(cons (lambda () (load val))
gnc:*batch-mode-things-to-do*)))))
; (cons "add-price-quotes"
; (cons 'string
; (lambda (val)
; (set! gnc:*batch-mode-things-to-do*
; (cons
; (lambda ()
; (display (get-1-quote "NASDAQ" val)))
; gnc:*batch-mode-things-to-do*)))))
; (cons "add-price-quotes"
; (cons 'string
; (lambda (val)
; (set! gnc:*batch-mode-things-to-do*
; (cons
; (lambda ()
; (with-
; (gnc:book-add-quotes
;
; gnc:*batch-mode-things-to-do*)))))
(cons "load-user-config"
(cons 'boolean gnc:load-user-config-if-needed))

View File

@ -16,9 +16,8 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (import-old-currencies from-filename)
(if (gnc:commodity-table-has-namespace
(gnc:engine-commodities)
"GNC_LEGACY_CURRENCIES")
(if (gnc:commodity-table-has-namespace (gnc:engine-commodities)
"GNC_LEGACY_CURRENCIES")
(gnc:import-legacy-commodities from-filename)))
(gnc:hook-add-dangler gnc:*file-opened-hook* import-old-currencies)

View File

@ -23,8 +23,8 @@
(gnc:support "engine-utilities.scm")
(define (gnc:filename->account-group filename)
"Returns an account group on success and #f on failure"
(define (gnc:filename->book filename)
"Returns a book on success and #f on failure"
(let* ((session (gnc:malloc-session)))
(if (not session)
#f
@ -84,13 +84,15 @@
(loop (cdr splits)))))
(reverse retval)))
;;(define (gnc:group-map-accounts thunk group)
;; (let ((retval '()))
;; (let loop ((accounts (or (gnc:group-get-subaccounts group) '())))
;; (if (not (null? accounts))
;; (begin
;; (set! retval (cons (thunk (car accounts)) retval))
;; (loop (cdr accounts)))))
;; (reverse retval)))
(define (gnc:group-map-accounts thunk group)
(let ((retval '()))
(let loop ((accounts (or (gnc:group-get-subaccounts group) '())))
(if (not (null? accounts))
(begin
(set! retval (cons (thunk (car accounts)) retval))
(loop (cdr accounts)))))
(reverse retval)))
(let ((accounts (or (gnc:group-get-subaccounts group) '())))
(map thunk accounts)))

View File

@ -42,6 +42,7 @@
(gnc:depend "report/report-list.scm")
(gnc:depend "qif-import/qif-import.scm")
(gnc:depend "printing/print-check.scm")
(gnc:depend "src/price-quotes.scm")
;; Load the system configs
(if (not (gnc:load-system-config-if-needed))

65
src/scm/price-quotes.scm Normal file
View File

@ -0,0 +1,65 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; price-quotes.scm - manage sub-processes.
;;; Copyright 2001 Rob Browning <rlb@cs.utexas.edu>
;;;
;;; 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
;;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652
;;; Boston, MA 02111-1307, USA gnu@gnu.org
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(gnc:support "price-quotes.scm")
(gnc:depend "process.scm")
(define gnc:*price-quote-helper*
"/home/rlb/opt/gnucash-working/share/gnucash/price-quote-helper")
(define (get-1-quote exchange . items)
(let ((cmd (apply list 'fetch exchange items))
(quoter (run-sub-process #f
gnc:*price-quote-helper*
gnc:*price-quote-helper*)))
(and quoter
(write cmd (caddr quoter))
(newline (caddr quoter))
(force-output (caddr quoter))
(let ((result (read (cadr quoter))))
(close-input-port (cadr quoter))
(close-output-port (caddr quoter))
result))))
(define (gnc:book-add-quotes book)
(define (find-quotables group)
(define (quotable-account? a)
(case (gnc:account-get-type a)
;; we no longer care what the price source was - Finance::Quote
;; doesn't let you specify a particular source.
((stock mutual-fund currency) (gnc:account-get-price-src a))
(else #f)))
(filter quotable-account? (gnc:group-get-subaccounts group)))
(display (list book)) (newline)
(display (list (gnc:book-get-group book))) (newline)
(let* ((group (gnc:book-get-group book))
(quotables (and group (find-quotables group)))
(commodities (and quotables
(map gnc:account-get-commodity quotables))))
(for-each (lambda (c)
(display (list "Get quote for" (gnc:commodity-get-mnemonic c)))
(newline))
commodities)))

179
src/scm/process.scm Normal file
View File

@ -0,0 +1,179 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; process.scm - manage sub-processes.
;;; Copyright 2001 Rob Browning <rlb@cs.utexas.edu>
;;;
;;; 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
;;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652
;;; Boston, MA 02111-1307, USA gnu@gnu.org
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(gnc:support "process.scm")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Run the program specified by path with the given args as a
;;; sub-proces. If envt is not #f, then use it as the sub-process
;;; environment (as per execle in the guile info pages). Note that
;;; you must specify the path explicitly.
;;;
;;; Returns #f on failure, or
;;; (pid child-output-pipe child-input-pipe child-standard-error-pipe)
;;; on success. Right now the standard-error pipe is always #f.
;;;
;;; For example:
;;;
;;; (run-sub-process "/bin/date" "--rfc-822")
;;;
(define (run-sub-process envt path . args)
(let ((parent-to-child-pipe (false-if-exception (pipe)))
(child-to-parent-pipe (false-if-exception (pipe))))
(if (not (and parent-to-child-pipe
child-to-parent-pipe))
#f
(let* ((parent-read-pipe (car child-to-parent-pipe))
(parent-write-pipe (cdr parent-to-child-pipe))
(child-read-pipe (car parent-to-child-pipe))
(child-write-pipe (cdr child-to-parent-pipe))
(pid (false-if-exception (primitive-fork))))
(if (not (zero? pid))
;; we're the parent
(begin
(close-input-port child-read-pipe)
(close-output-port child-write-pipe)
(list pid parent-read-pipe parent-write-pipe #f))
;; else we're the child
(begin
;; set standard-input and standard-output at the fd
;; level -- which is really all that matters since
;; we're about to exec...
(close-input-port parent-read-pipe)
(close-output-port parent-write-pipe)
(dup->fdes child-read-pipe 0)
(dup->fdes child-write-pipe 1)
;; now launch the child process.
(or (false-if-exception
(if envt
(apply execle path envt args)
(apply execl path args)))
(exit 1))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Random test code.
;;;
; (define (get-1-quote exchange . items)
; (let ((cmd (apply list 'fetch exchange items))
; (quoter (run-sub-process #f
; "./scmio-finance-quote"
; "./scmio-finance-quote")))
; (and quoter
; (write cmd (caddr quoter))
; (newline (caddr quoter))
; (force-output (caddr quoter))
; (let ((result (read (cadr quoter))))
; (close-input-port (cadr quoter))
; (close-output-port (caddr quoter))
; result))))
; (define (parrot)
; (let loop ((input (false-if-exception (read))))
; (cond
; ((eof-object? input) (quit 0))
; ((not input) (quit 0))
; (else (write input)
; (force-output)
; (loop (read))))))
; (define (launch-parrot envt path args)
; ;; Returns (pid child-input-port child-output-port child-error-port)
; ;; Right now the error port is broken...
; (let* ((pid #f)
; (sockets (false-if-exception (socketpair AF_UNIX SOCK_STREAM 0))))
; (if sockets
; (set! pid (false-if-exception (primitive-fork))))
; (cond
; ((not pid) #f)
; ((= pid 0)
; ;; We're the child.
; ;; set standard-input and standard-output, swapping input and
; ;; output sockets from parent...
; (display 'foo) (newline) (flush-all-ports)
; ;;(redirect-port (car sockets) (current-input-port))
; (set-current-input-port (cdr sockets))
; (display 'bar) (newline) (flush-all-ports)
; ;;(redirect-port (cdr sockets) (current-output-port))
; (set-current-output-port (cdr sockets))
; (parrot))
; ; (or (false-if-exception
; ; (if envt
; ; (apply execle path envt args)
; ; (apply execl path args)))
; ; (exit 1)))
; (else
; ;; we're the parent
; ;; child-input-port child-output-port child-error-port
; (list pid (car sockets) #f)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This code was part of an attempt to just return one
;;; read-write-port for the child, but I had some trouble getting it
;;; to work. I think either (1) this was misguided from the start
;;; since you can't hook up the plumbing this way, or (2) I was
;;; forgetting some flushing or something somewhere that kept it from
;;; working. At one point, I knew which of these two options was
;;; true, but I can't recall what I concluded now, so I'll leave the
;;; code here in case we want to resurrect it...
; (define (run-sub-process envt path . args)
; (let ((pid #f)
; (sockets (false-if-exception (socketpair AF_UNIX SOCK_STREAM 0))))
; (if sockets
; (set! pid (false-if-exception (primitive-fork))))
; (cond
; ((or (not sockets) (not pid)) #f)
; ((= pid 0)
; ;; We're the child: set standard-input and standard-output to be
; ;; the socket that's connected to the parent.
; (set-current-input-port (cdr sockets))
; (set-current-output-port (cdr sockets))
; (dup->fdes (cdr sockets) 0)
; (dup->fdes (cdr sockets) 1)
; ;; now launch the child process.
; (or (false-if-exception
; (if envt
; (apply execle path envt args)
; (apply execl path args)))
; (exit 1)))
; (else
; ;; we're the parent
; (list pid (car sockets) #f)))))