mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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:
parent
ce532a2a86
commit
e30a8bc509
238
ChangeLog
238
ChangeLog
@ -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
|
||||
|
29
Makefile.am
29
Makefile.am
@ -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
|
||||
|
26
configure.in
26
configure.in
@ -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
|
||||
|
@ -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 "$@" ...
|
||||
|
@ -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 (),
|
||||
|
@ -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__ */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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(!(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;
|
||||
}
|
||||
|
||||
if(!ok && c_xml) xmlFreeNode(c_xml);
|
||||
return(TRUE);
|
||||
xmlAddChild(p, c_xml);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
@ -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;
|
||||
}
|
@ -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__ */
|
@ -70,6 +70,7 @@ void xaccGroupMergeAccounts (AccountGroup *grp);
|
||||
gboolean xaccGroupNotSaved (AccountGroup *grp);
|
||||
void xaccGroupMarkSaved (AccountGroup *grp);
|
||||
void xaccGroupMarkNotSaved (AccountGroup *grp);
|
||||
|
||||
void xaccGroupMarkDoFree (AccountGroup *grp);
|
||||
|
||||
/*
|
||||
|
@ -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))
|
||||
|
@ -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 \
|
||||
|
@ -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 ======================== */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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__ */
|
@ -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,21 +716,20 @@ 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 (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);
|
||||
err = xaccBackendGetError(be);
|
||||
|
||||
if (ERR_BACKEND_NO_ERR != retval)
|
||||
{
|
||||
gnc_book_push_error (book, retval);
|
||||
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)
|
||||
{
|
||||
if (be->book_end) {
|
||||
(be->book_end)(be);
|
||||
}
|
||||
}
|
||||
@ -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 ================== */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 ******************************\
|
||||
\********************************************************************/
|
||||
|
@ -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
|
||||
|
34
src/engine/gnc-pricedb-p.h
Normal file
34
src/engine/gnc-pricedb-p.h
Normal 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
|
373
src/engine/gnc-pricedb-xml-v1.c
Normal file
373
src/engine/gnc-pricedb-xml-v1.c
Normal 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
733
src/engine/gnc-pricedb.c
Normal 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
169
src/engine/gnc-pricedb.h
Normal 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
|
@ -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);
|
||||
|
||||
@ -198,7 +284,7 @@ static int readTSDate( int fd, Timespec *, int token );
|
||||
/*******************************************************/
|
||||
|
||||
GNCBackendError
|
||||
xaccGetGncBinFileIOError (void)
|
||||
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;
|
||||
return;
|
||||
}
|
||||
grp = xaccReadAccountGroup (fd);
|
||||
|
||||
if(!gnc_load_financials_from_fd(book, fd)) return;
|
||||
|
||||
close(fd);
|
||||
return grp;
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************\
|
||||
@ -644,14 +756,12 @@ 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
DxaccSplitSetSharePriceAndAmount (s, share_price, 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;
|
||||
|
||||
/* 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 {
|
||||
for (i = 0; i < numSplits; i++) {
|
||||
Split *split = readSplit(fd, revision);
|
||||
xaccTransAppendSplit(trans, split);
|
||||
|
||||
if(!notes) {
|
||||
notes = xaccSplitGetMemo (split);
|
||||
if(notes) xaccTransSetNotes (trans, notes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* 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
57
src/engine/io-gncxml-p.h
Normal 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
|
@ -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 ============================== */
|
||||
|
@ -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,15 +137,19 @@ 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;
|
||||
|
@ -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
|
||||
@ -50,7 +45,15 @@ void gncxml_write_to_buf (AccountGroup *group, char **bufp, int *sz);
|
||||
*/
|
||||
void gncxml_write_group_to_buf (AccountGroup *group, char **bufp, int *sz);
|
||||
|
||||
/* write the query terms to memory */
|
||||
#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__ */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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_ */
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -2,3 +2,4 @@ Makefile
|
||||
Makefile.in
|
||||
gnc-prices
|
||||
gnc-prices-2
|
||||
price-quote-helper
|
||||
|
@ -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
|
||||
|
@ -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__
|
282
src/quotes/price-quote-helper.in
Normal file
282
src/quotes/price-quote-helper.in
Normal 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:
|
@ -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 \
|
||||
|
@ -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))
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define (import-old-currencies from-filename)
|
||||
(if (gnc:commodity-table-has-namespace
|
||||
(gnc:engine-commodities)
|
||||
(if (gnc:commodity-table-has-namespace (gnc:engine-commodities)
|
||||
"GNC_LEGACY_CURRENCIES")
|
||||
(gnc:import-legacy-commodities from-filename)))
|
||||
|
||||
|
@ -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)))
|
||||
|
@ -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
65
src/scm/price-quotes.scm
Normal 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
179
src/scm/process.scm
Normal 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)))))
|
Loading…
Reference in New Issue
Block a user