summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2008-04-12 01:40:49 -0400
committerJohn Wiegley <johnw@newartisans.com>2008-05-08 02:50:19 -0400
commitb45037e334ca336498217d4f54756c319c074d7b (patch)
treed0f8338df6c8adf86debcdcf236a25bbdd8d305b
parent289cc97d45bbd3a8cdfcc3371ade71715b75b000 (diff)
downloadfork-ledger-b45037e334ca336498217d4f54756c319c074d7b.tar.gz
fork-ledger-b45037e334ca336498217d4f54756c319c074d7b.tar.bz2
fork-ledger-b45037e334ca336498217d4f54756c319c074d7b.zip
Migrated over both code and build environment from was-v3.0 branch.
-rw-r--r--Makefile.am230
-rwxr-xr-xacprep147
-rw-r--r--amount.cc2455
-rw-r--r--amount.h1156
-rw-r--r--balance.cc657
-rw-r--r--balance.h1242
-rw-r--r--balpair.h367
-rw-r--r--binary.cc1335
-rw-r--r--binary.h275
-rw-r--r--commodity.cc598
-rw-r--r--commodity.h394
-rw-r--r--configure.in299
-rw-r--r--datetime.cc363
-rw-r--r--datetime.h314
-rw-r--r--debug.cc125
-rw-r--r--debug.h146
-rw-r--r--error.h33
-rw-r--r--fdstream.hpp31
-rw-r--r--flags.h103
-rw-r--r--journal.h5
-rw-r--r--main.py373
-rw-r--r--mask.cc70
-rw-r--r--mask.h56
-rw-r--r--parser.h79
-rw-r--r--pushvar.h67
-rw-r--r--py_amount.cc320
-rw-r--r--py_commodity.cc63
-rw-r--r--py_times.cc132
-rw-r--r--py_utils.cc172
-rw-r--r--pyfstream.h169
-rw-r--r--pyinterp.cc238
-rw-r--r--pyinterp.h98
-rw-r--r--pyledger.cc46
-rw-r--r--pyledger.h47
-rw-r--r--pyutils.h112
-rwxr-xr-xsetup.py18
-rw-r--r--system.hh156
-rw-r--r--times.cc80
-rw-r--r--times.h123
-rw-r--r--timing.h62
-rw-r--r--tuples.hpp281
-rw-r--r--util.h62
-rw-r--r--utils.cc720
-rw-r--r--utils.h540
-rwxr-xr-xvalgrind.sh9
-rw-r--r--value.cc2142
-rw-r--r--value.h1085
-rwxr-xr-xversion5
48 files changed, 10098 insertions, 7502 deletions
diff --git a/Makefile.am b/Makefile.am
index 5de39382..7ced6a31 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,20 +1,31 @@
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = LICENSE doc test contrib scripts setup.py \
+ acprep valgrind.sh version
+
+ESC_srcdir=`echo "$(srcdir)" | sed 's/\//\\\\\//g'`
+ESC_builddir=`echo "$(top_builddir)" | sed 's/\//\\\\\//g'`
+ESC_distdir=`echo "$(distdir)" | sed 's/\//\\\\\//g'`
+
+dist-hook:
+ rm -fr .git
+
lib_LTLIBRARIES = libamounts.la libledger.la
-libamounts_la_CXXFLAGS =
+libamounts_la_CPPFLAGS =
libamounts_la_SOURCES = \
amount.cc \
balance.cc \
- datetime.cc \
value.cc
if HAVE_BOOST_PYTHON
-libamounts_la_CXXFLAGS += -DUSE_BOOST_PYTHON=1
+libamounts_la_CPPFLAGS += -DUSE_BOOST_PYTHON=1
endif
if DEBUG
-libamounts_la_CXXFLAGS += -DDEBUG_LEVEL=4
-libamounts_la_SOURCES += debug.cc
+libamounts_la_CPPFLAGS += -DDEBUG_MODE
+libamounts_la_SOURCES += utils.cc
endif
-libledger_la_CXXFLAGS =
+libledger_la_CPPFLAGS =
libledger_la_SOURCES = \
binary.cc \
config.cc \
@@ -36,19 +47,19 @@ libledger_la_SOURCES = \
walk.cc \
xml.cc
if HAVE_EXPAT
-libledger_la_CXXFLAGS += -DHAVE_EXPAT=1
+libledger_la_CPPFLAGS += -DHAVE_EXPAT=1
libledger_la_SOURCES += gnucash.cc
endif
if HAVE_XMLPARSE
-libledger_la_CXXFLAGS += -DHAVE_XMLPARSE=1
+libledger_la_CPPFLAGS += -DHAVE_XMLPARSE=1
libledger_la_SOURCES += gnucash.cc
endif
if HAVE_LIBOFX
-libledger_la_CXXFLAGS += -DHAVE_LIBOFX=1
+libledger_la_CPPFLAGS += -DHAVE_LIBOFX=1
libledger_la_SOURCES += ofx.cc
endif
if DEBUG
-libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
+libledger_la_CPPFLAGS += -DDEBUG_MODE
endif
libledger_la_LDFLAGS = -release 2.6.0.90
@@ -85,92 +96,199 @@ pkginclude_HEADERS = \
walk.h \
xml.h
+if USE_PCH
+nodist_libledger_la_SOURCES = system.hh.gch
+
+BUILT_SOURCES += system.hh.gch
+CLEANFILES += system.hh.gch system.hh
+
+$(top_builddir)/system.hh.gch: $(srcdir)/src/utility/system.hh acconf.h
+ echo "#include \"src/utility/system.hh\"" > $(top_builddir)/system.hh
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(libledger_la_CPPFLAGS) \
+ -o $@ $(srcdir)/src/utility/system.hh
+endif
+
######################################################################
bin_PROGRAMS = ledger
-ledger_CXXFLAGS =
+ledger_CPPFLAGS =
ledger_SOURCES = main.cc
ledger_LDADD = $(LIBOBJS) libamounts.la libledger.la
if HAVE_EXPAT
-ledger_CXXFLAGS += -DHAVE_EXPAT=1
+ledger_CPPFLAGS += -DHAVE_EXPAT=1
endif
if HAVE_XMLPARSE
-ledger_CXXFLAGS += -DHAVE_XMLPARSE=1
+ledger_CPPFLAGS += -DHAVE_XMLPARSE=1
endif
if HAVE_LIBOFX
-ledger_CXXFLAGS += -DHAVE_LIBOFX=1
+ledger_CPPFLAGS += -DHAVE_LIBOFX=1
endif
if DEBUG
-ledger_CXXFLAGS += -DDEBUG_LEVEL=4
+ledger_CPPFLAGS += -DDEBUG_MODE
endif
ledger_LDFLAGS = -static # for the sake of command-line speed
-info_TEXINFOS = ledger.texi
+info_TEXINFOS = doc/ledger.texi
######################################################################
-lisp_LISP = ledger.el timeclock.el
dist_lisp_LISP = ledger.el timeclock.el
+DISTCLEANFILES = ledger.elc timeclock.elc
+
######################################################################
if HAVE_BOOST_PYTHON
noinst_PROGRAMS = amounts.so
+CLEANFILES += amounts.so
+
+clean-local:
+ rm -fr build
+
+PYLIBS = amounts gmp
+
amounts.so: amounts.cc libamounts.la
- CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
- python setup.py build --build-lib=.
+ CFLAGS="$(CPPFLAGS) -I$(srcdir) $(libamounts_la_CPPFLAGS)" \
+ LDFLAGS="$(LDFLAGS) -L. -L.libs" \
+ PYLIBS="$(PYLIBS)" SRCDIR="$(srcdir)" \
+ python $(srcdir)/setup.py build --build-lib=.
install-exec-hook:
- CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
- python setup.py install --prefix=$(prefix)
+ CFLAGS="$(CPPFLAGS) -I$(srcdir) $(libamounts_la_CPPFLAGS)" \
+ LDFLAGS="$(LDFLAGS) -L. -L.libs" \
+ PYLIBS="$(PYLIBS)" SRCDIR="$(srcdir)" \
+ python $(srcdir)/setup.py install --prefix=$(prefix)
endif
######################################################################
-TESTS = alltests
+TESTS = UnitTests
+if HAVE_BOOST_PYTHON
+TESTS += PyUnitTests
+endif
-CXXTEST_DIR = /usr/local/cxxtest
-TESTGEN = $(CXXTEST_DIR)/cxxtestgen.py
-TESTSUITES = tests/*.h
+check_PROGRAMS = $(TESTS)
-AM_CXXFLAGS =
-if HAVE_EXPAT
-AM_CXXFLAGS += -DHAVE_EXPAT=1
-endif
-if HAVE_XMLPARSE
-AM_CXXFLAGS += -DHAVE_XMLPARSE=1
-endif
-if HAVE_LIBOFX
-AM_CXXFLAGS += -DHAVE_LIBOFX=1
-endif
-if DEBUG
-AM_CXXFLAGS += -DDEBUG_LEVEL=4
-endif
+nodist_UnitTests_SOURCES = tests/UnitTests.cc \
+ \
+ tests/utility/t_utils.cc \
+ tests/utility/t_times.cc \
+ tests/numerics/t_commodity.cc \
+ tests/numerics/t_amount.cc \
+ tests/numerics/t_balance.cc
+
+UnitTests_CPPFLAGS = -I$(srcdir)/tests $(libledger_la_CPPFLAGS)
+UnitTests_LDFLAGS = $(LIBADD_DL)
+UnitTests_LDADD = $(lib_LTLIBRARIES) gdtoa/libgdtoa.la -lcppunit
+
+nodist_PyUnitTests_SOURCES = tests/python/PyUnitTests.py
+
+# jww (2007-05-10): This rule will not be triggered on systems that
+# define an EXEEXT.
+PyUnitTests: $(srcdir)/tests/python/PyUnitTests.py
+ cat $(srcdir)/tests/python/PyUnitTests.py \
+ | sed "s/%srcdir%/$(ESC_srcdir)/g" \
+ | sed "s/%builddir%/$(ESC_builddir)/g" > $@
+ chmod 755 $@
-alltests.cc: $(TESTSUITES)
- test -f $(TESTGEN) && python $(TESTGEN) -o $@ --error-printer $(TESTSUITES)
+fullcheck: check
+ MallocGuardEdges=1 \
+ MallocScribble=1 \
+ MallocPreScribble=1 \
+ MallocCheckHeapStart=100 \
+ MallocCheckHeapEach=100 \
+ DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib \
+ $(srcdir)/valgrind.sh $(top_builddir)/UnitTests$(EXEEXT) --verify
-alltests: alltests.cc ledger
- $(CXXCOMPILE) -I$(CXXTEST_DIR) -lexpat -lgmp -lpcre -o $@ \
- alltests.cc -L. -L.libs -lamounts -lledger
+###############################################################################
-runtests: alltests
- LD_LIBRARY_PATH=.libs ./alltests && tests/regress && tests/regtest
+DISTCLEANFILES += Doxyfile.gen
-verify: runtests
- python tests/runtests.py
+alldocs: docs/ledger.info docs/ledger.pdf doxygen-docs
+
+$(top_builddir)/Doxyfile.gen: $(srcdir)/docs/Doxyfile
+ cat $(srcdir)/docs/Doxyfile \
+ | sed "s/%srcdir%/$(ESC_srcdir)/g" \
+ | sed "s/%builddir%/$(ESC_builddir)/g" > $@
+
+doxygen-docs: $(top_builddir)/Doxyfile.gen
+ doxygen $(top_builddir)/Doxyfile.gen
######################################################################
-all-clean: maintainer-clean
- rm -fr *~ .*~ .\#* *.html *.info *.pdf *.a *.so *.o *.lo *.la \
- *.elc *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr \
- .gdb_history gmon.out h out TAGS ledger valexpr .deps \
- .libs build AUTHORS COPYING INSTALL Makefile acconf.h \
- acconf.h.in aclocal.m4 autom4te config.guess config.sub \
- configure depcomp install-sh libtool ltconfig ltmain.sh \
- missing stamp texinfo.tex Makefile.in mkinstalldirs \
- elisp-comp elc-stamp py-compile
+clean-backupfiles:
+ rm -fr *~ \
+ .*~ \
+ .\#*
+
+clean-documentation:
+ (cd doc; \
+ rm -fr *.aux \
+ *.cp \
+ *.fn \
+ *.info \
+ *.ky \
+ *.log \
+ *.pdf \
+ *.pg \
+ *.toc \
+ *.tp \
+ *.vr)
+
+clean-buildproducts:
+ rm -fr *.Plo \
+ *.Po \
+ *.a \
+ *.elc \
+ *.gcno \
+ *.gdca \
+ *.la \
+ *.lo \
+ *.o \
+ *.so \
+ .deps \
+ .libs \
+ build
+
+clean-debugdata:
+ rm -fr .gdb_history \
+ TAGS \
+ gmon.out \
+ h \
+ out
+
+clean-autoconf:
+ rm -fr Makefile \
+ Makefile.in \
+ acconf.h \
+ acconf.h.in \
+ aclocal.m4 \
+ autom4te.cache \
+ compile \
+ config.guess \
+ config.sub \
+ configure \
+ depcomp \
+ elc-stamp \
+ elc-temp \
+ elisp-comp \
+ install-sh \
+ libtool \
+ ltconfig \
+ ltmain.sh \
+ missing \
+ mkinstalldirs \
+ py-compile \
+ stamp \
+ texinfo.tex \
+ ylwrap
+
+all-clean: maintainer-clean \
+ clean-buildproducts \
+ clean-backupfiles \
+ clean-debugdata \
+ clean-documentation \
+ clean-autoconf
diff --git a/acprep b/acprep
index 252a9aab..ccd6541e 100755
--- a/acprep
+++ b/acprep
@@ -1,5 +1,14 @@
#!/bin/sh
+# acprep, version 3.0
+#
+# This script configures my ledger source tree on my Mac OS/X machine.
+# This is not necessary, however, since I keep all the files necessary
+# for building checked in to the source tree. Users can just type
+# './configure && make'. This script simply sets up the compiler and
+# linker flags for all the various build permutations I use for testing
+# and profiling.
+
export AUTOCONF_VERSION=2.61
export AUTOMAKE_VERSION=1.9
@@ -11,23 +20,15 @@ if [ -x "$cmd" ]; then
fi
autoreconf --force --install
-HERE="$PWD"
+INCDIRS="-I/sw/include -I/opt/local/include"
+INCDIRS="$INCDIRS -I/usr/local/include"
+INCDIRS="$INCDIRS -I/usr/local/include/boost-1_35"
-if [ ! "$1" = "--local" ]; then
- if [ -d "$HOME/Products" ]; then
- projdir="$HOME/Products/$(basename $HERE)"
- if [ ! -d "$projdir" ]; then
- mkdir -p "$projdir"
- fi
- cd "$projdir" || (echo "Cannot change to $projdir"; exit 1)
- fi
-else
- shift 1
-fi
+LIBDIRS="-L/sw/lib -L/opt/local/lib"
+LIBDIRS="$LIBDIRS -L/usr/local/lib"
+
+PYTHON_HOME="/Library/Frameworks/Python.framework/Versions/2.5"
-INCDIRS="-I/opt/local/include -I/usr/local/include -I/usr/include/httpd/xml"
-INCDIRS="$INCDIRS -I/usr/include/python2.5"
-LIBDIRS="-L/opt/local/lib -L/usr/local/lib"
SYSTEM=`uname -s`
@@ -43,33 +44,93 @@ else
CXXFLAGS=""
fi
-if [ "$1" = "--debug" ]; then
- shift 1
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -g" --enable-debug --enable-python "$@"
-elif [ "$1" = "--opt" ]; then
- shift 1
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3 -mcpu=7450 -fPIC" "$@"
-elif [ "$1" = "--flat-opt" ]; then
- shift 1
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3 -mcpu=7450" "$@"
-elif [ "$1" = "--safe-opt" ]; then
- shift 1
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3 -mcpu=7450 -fPIC -DDEBUG_LEVEL=1" "$@"
-elif [ "$1" = "--perf" ]; then
- shift 1
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -g -pg" "$@"
-elif [ "$1" = "--python" ]; then
- shift 1
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -g" --enable-python "$@"
-else
- $HERE/configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
- CXXFLAGS="$CXXFLAGS -g" "$@"
+# Building the command-line tool as a shared library is a luxury,
+# since there are no clients except a GUI tool which might use it (and
+# that is built again anyway by Xcode).
+SWITCHES=""
+CPPFLAGS="$INCDIRS"
+CXXFLAGS="-pipe"
+LDFLAGS="$LIBDIRS"
+LOCAL=false
+
+# Warning flags
+CXXFLAGS="$CXXFLAGS -Wall -ansi"
+#CXXFLAGS="$CXXFLAGS -Wextra"
+#CXXFLAGS="$CXXFLAGS -Weffc++"
+#CXXFLAGS="$CXXFLAGS -Wcast-align"
+#CXXFLAGS="$CXXFLAGS -Wcast-qual"
+#CXXFLAGS="$CXXFLAGS -Wconversion"
+#CXXFLAGS="$CXXFLAGS -Wfloat-equal"
+#CXXFLAGS="$CXXFLAGS -Wmissing-field-initializers"
+#CXXFLAGS="$CXXFLAGS -Wno-endif-labels"
+#CXXFLAGS="$CXXFLAGS -Wold-style-cast"
+#CXXFLAGS="$CXXFLAGS -Woverloaded-virtual"
+#CXXFLAGS="$CXXFLAGS -Wshorten-64-to-32"
+#CXXFLAGS="$CXXFLAGS -Wsign-compare"
+#CXXFLAGS="$CXXFLAGS -Wsign-promo"
+#CXXFLAGS="$CXXFLAGS -Wstrict-null-sentinel"
+#CXXFLAGS="$CXXFLAGS -Wwrite-strings"
+
+
+while [ -n "$1" ]; do
+ case "$1" in
+ --devel)
+ SWITCHES="$SWITCHES --disable-shared --enable-pch"
+ ;;
+
+ --debug)
+ SWITCHES="$SWITCHES --enable-debug"
+ #CPPFLAGS="$CPPFLAGS -D_GLIBCXX_DEBUG=1"
+ CXXFLAGS="$CXXFLAGS -g" ;;
+
+ --boost)
+ shift 1
+ SWITCHES="$SWITCHES --with-boost-suffix=$1"
+ ;;
+
+ --gcov)
+ CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" ;;
+
+ --gprof)
+ CXXFLAGS="$CXXFLAGS -g -pg" ;;
+
+ --python)
+ if [ -d "$PYTHON_HOME" ]; then
+ SWITCHES="$SWITCHES --enable-python"
+ CPPFLAGS="$CPPFLAGS -I$PYTHON_HOME/include/python2.5"
+ LDFLAGS="$LDFLAGS -L$PYTHON_HOME/lib/python2.5/config"
+ fi ;;
+
+ --pic)
+ CXXFLAGS="$CXXFLAGS -fPIC" ;;
+
+ --opt)
+ CXXFLAGS="$CXXFLAGS -fomit-frame-pointer -O3" ;;
+
+ --local)
+ LOCAL=true ;;
+
+ *)
+ break ;;
+ esac
+ shift 1
+done
+
+
+HERE="$PWD"
+
+if [ "$LOCAL" = "false" -a -d "$HOME/Products" ]; then
+ version=""
+ if [ -x version ]; then
+ version="-$(./version)"
+ fi
+ projdir="$HOME/Products/$(basename $HERE)$version"
+ if [ ! -d "$projdir" ]; then
+ mkdir -p "$projdir"
+ fi
+ cd "$projdir" || (echo "Cannot change to $projdir"; exit 1)
fi
-rm -f AUTHORS COPYING
+"$HERE/configure" --srcdir="$HERE" \
+ CPPFLAGS="$CPPFLAGS" CXXFLAGS="$CXXFLAGS $local_cxxflags" \
+ LDFLAGS="$LDFLAGS" LIBS="$LIBS" $SWITCHES "$@"
diff --git a/amount.cc b/amount.cc
index 4f07b0b0..2424f0ac 100644
--- a/amount.cc
+++ b/amount.cc
@@ -1,287 +1,313 @@
-#include "amount.h"
-#include "util.h"
-
-#include <list>
-#include <sstream>
-#include <cstdlib>
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file amount.cc
+ * @author John Wiegley
+ * @date Thu Apr 26 15:19:46 2007
+ *
+ * @brief Types for handling commoditized math.
+ *
+ * This file defines member functions for amount_t, and also defines a
+ * helper class, bigint_t, which is used as a refcounted wrapper
+ * around libgmp's mpz_t type.
+ */
-#include <gmp.h>
+#include "amount.h"
+#include "parser.h"
namespace ledger {
-bool do_cleanup = true;
+commodity_pool_t * amount_t::current_pool = NULL;
+
+bool amount_t::keep_base = false;
bool amount_t::keep_price = false;
bool amount_t::keep_date = false;
-bool amount_t::keep_tag = false;
-bool amount_t::keep_base = false;
+bool amount_t::keep_tag = false;
-#define BIGINT_BULK_ALLOC 0x0001
-#define BIGINT_KEEP_PREC 0x0002
+bool amount_t::stream_fullstrings = false;
-class amount_t::bigint_t {
- public:
- mpz_t val;
- unsigned char prec;
- unsigned char flags;
- unsigned int ref;
- unsigned int index;
+#ifndef THREADSAFE
+/**
+ * These global temporaries are pre-initialized for the sake of
+ * efficiency, and reused over and over again.
+ */
+static mpz_t temp;
+static mpz_t divisor;
+#endif
+
+struct amount_t::bigint_t : public supports_flags<>
+{
+#define BIGINT_BULK_ALLOC 0x01
+#define BIGINT_KEEP_PREC 0x02
- bigint_t() : prec(0), flags(0), ref(1), index(0) {
+ mpz_t val;
+ precision_t prec;
+ uint_least16_t ref;
+ uint_fast32_t index;
+
+#define MPZ(bigint) ((bigint)->val)
+
+ bigint_t() : prec(0), ref(1), index(0) {
+ TRACE_CTOR(bigint_t, "");
mpz_init(val);
}
- bigint_t(mpz_t _val) : prec(0), flags(0), ref(1), index(0) {
+ bigint_t(mpz_t _val) : prec(0), ref(1), index(0) {
+ TRACE_CTOR(bigint_t, "mpz_t");
mpz_init_set(val, _val);
}
bigint_t(const bigint_t& other)
- : prec(other.prec), flags(other.flags & BIGINT_KEEP_PREC),
- ref(1), index(0) {
+ : supports_flags<>(other.flags() & BIGINT_KEEP_PREC),
+ prec(other.prec), ref(1), index(0) {
+ TRACE_CTOR(bigint_t, "copy");
mpz_init_set(val, other.val);
}
- ~bigint_t();
+ ~bigint_t() {
+ TRACE_DTOR(bigint_t);
+ assert(ref == 0);
+ mpz_clear(val);
+ }
};
-unsigned int sizeof_bigint_t() {
- return sizeof(amount_t::bigint_t);
-}
-
-#define MPZ(x) ((x)->val)
-
-static mpz_t temp; // these are the global temp variables
-static mpz_t divisor;
-
-static amount_t::bigint_t true_value;
-
-inline amount_t::bigint_t::~bigint_t() {
- assert(ref == 0 || (! do_cleanup && this == &true_value));
- mpz_clear(val);
-}
-
-base_commodities_map commodity_base_t::commodities;
-
-commodity_base_t::updater_t * commodity_base_t::updater = NULL;
-
-commodities_map commodity_t::commodities;
-bool commodity_t::commodities_sorted = false;
-commodity_t * commodity_t::null_commodity;
-commodity_t * commodity_t::default_commodity = NULL;
-
-static struct _init_amounts {
- _init_amounts() {
- mpz_init(temp);
- mpz_init(divisor);
-
- mpz_set_ui(true_value.val, 1);
-
- commodity_base_t::updater = NULL;
- commodity_t::null_commodity = commodity_t::create("");
- commodity_t::default_commodity = NULL;
-
- commodity_t::null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
- COMMODITY_STYLE_BUILTIN);
+void amount_t::initialize()
+{
+ mpz_init(temp);
+ mpz_init(divisor);
- // Add time commodity conversions, so that timelog's may be parsed
- // in terms of seconds, but reported as minutes or hours.
- commodity_t * commodity;
+ // jww (2007-05-02): Be very careful here!
+ if (! current_pool)
+ current_pool = new commodity_pool_t;
- commodity = commodity_t::create("s");
+ // Add time commodity conversions, so that timelog's may be parsed
+ // in terms of seconds, but reported as minutes or hours.
+ if (commodity_t * commodity = current_pool->create("s")) {
commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
parse_conversion("1.0m", "60s");
parse_conversion("1.0h", "60m");
-
-#if 0
- commodity = commodity_t::create("b");
- commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
-
- parse_conversion("1.00 Kb", "1024 b");
- parse_conversion("1.00 Mb", "1024 Kb");
- parse_conversion("1.00 Gb", "1024 Mb");
- parse_conversion("1.00 Tb", "1024 Gb");
-#endif
+ } else {
+ assert(false);
}
+}
- ~_init_amounts() {
- if (! do_cleanup)
- return;
-
- mpz_clear(temp);
- mpz_clear(divisor);
-
- if (commodity_base_t::updater) {
- delete commodity_base_t::updater;
- commodity_base_t::updater = NULL;
- }
-
- for (commodities_map::iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
- i++)
- delete (*i).second;
-
- commodity_t::commodities.clear();
+void amount_t::shutdown()
+{
+ mpz_clear(temp);
+ mpz_clear(divisor);
- true_value.ref--;
+ // jww (2007-05-02): Be very careful here!
+ if (current_pool) {
+ checked_delete(current_pool);
+ current_pool = NULL;
}
-} _init_obj;
+}
-static void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
+void amount_t::_copy(const amount_t& amt)
{
- // Round `value', with an encoding precision of `value_prec', to a
- // rounded value with precision `round_prec'. Result is stored in
- // `out'.
-
- assert(value_prec > round_prec);
-
- mpz_t quotient;
- mpz_t remainder;
-
- mpz_init(quotient);
- mpz_init(remainder);
+ if (quantity != amt.quantity) {
+ if (quantity)
+ _release();
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_tdiv_qr(quotient, remainder, value, divisor);
- mpz_divexact_ui(divisor, divisor, 10);
- mpz_mul_ui(divisor, divisor, 5);
-
- if (mpz_sgn(remainder) < 0) {
- mpz_neg(divisor, divisor);
- if (mpz_cmp(remainder, divisor) < 0) {
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_add(remainder, divisor, remainder);
- mpz_ui_sub(remainder, 0, remainder);
- mpz_add(out, value, remainder);
- } else {
- mpz_sub(out, value, remainder);
- }
- } else {
- if (mpz_cmp(remainder, divisor) >= 0) {
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_sub(remainder, divisor, remainder);
- mpz_add(out, value, remainder);
+ // Never maintain a pointer into a bulk allocation pool; such
+ // pointers are not guaranteed to remain.
+ if (amt.quantity->has_flags(BIGINT_BULK_ALLOC)) {
+ quantity = new bigint_t(*amt.quantity);
} else {
- mpz_sub(out, value, remainder);
+ quantity = amt.quantity;
+ DEBUG("amounts.refs",
+ quantity << " ref++, now " << (quantity->ref + 1));
+ quantity->ref++;
}
}
- mpz_clear(quotient);
- mpz_clear(remainder);
-
- // chop off the rounded bits
- mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
- mpz_tdiv_q(out, out, divisor);
+ commodity_ = amt.commodity_;
}
-amount_t::amount_t(const bool value)
+void amount_t::_dup()
{
- if (value) {
- quantity = &true_value;
- quantity->ref++;
- } else {
- quantity = NULL;
+ if (quantity->ref > 1) {
+ bigint_t * q = new bigint_t(*quantity);
+ _release();
+ quantity = q;
}
- commodity_ = NULL;
}
-amount_t::amount_t(const long value)
+void amount_t::_resize(precision_t prec)
{
- if (value != 0) {
- quantity = new bigint_t;
- mpz_set_si(MPZ(quantity), value);
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
-}
+ assert(prec < 256);
-amount_t::amount_t(const unsigned long value)
-{
- if (value != 0) {
- quantity = new bigint_t;
- mpz_set_ui(MPZ(quantity), value);
- } else {
- quantity = NULL;
- }
- commodity_ = NULL;
+ if (! quantity || prec == quantity->prec)
+ return;
+
+ _dup();
+
+ assert(prec > quantity->prec);
+ mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
+ mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
+
+ quantity->prec = prec;
}
-amount_t::amount_t(const double value)
+void amount_t::_clear()
{
- if (value != 0.0) {
- quantity = new bigint_t;
- mpz_set_d(MPZ(quantity), value);
+ if (quantity) {
+ _release();
+ quantity = NULL;
+ commodity_ = NULL;
} else {
- quantity = NULL;
+ assert(! commodity_);
}
- commodity_ = NULL;
}
void amount_t::_release()
{
- DEBUG_PRINT("amounts.refs",
- quantity << " ref--, now " << (quantity->ref - 1));
+ DEBUG("amounts.refs", quantity << " ref--, now " << (quantity->ref - 1));
+
if (--quantity->ref == 0) {
- if (! (quantity->flags & BIGINT_BULK_ALLOC))
- delete quantity;
- else
+ if (quantity->has_flags(BIGINT_BULK_ALLOC))
quantity->~bigint_t();
+ else
+ checked_delete(quantity);
}
}
-void amount_t::_init()
-{
- if (! quantity) {
- quantity = new bigint_t;
- }
- else if (quantity->ref > 1) {
- _release();
- quantity = new bigint_t;
- }
-}
-void amount_t::_dup()
-{
- if (quantity->ref > 1) {
- bigint_t * q = new bigint_t(*quantity);
- _release();
- quantity = q;
- }
-}
+namespace {
+ amount_t::precision_t convert_double(mpz_t dest, double val)
+ {
+#ifndef HAVE_GDTOA
+ // This code is far too imprecise to be worthwhile.
-void amount_t::_copy(const amount_t& amt)
-{
- if (quantity != amt.quantity) {
- if (quantity)
- _release();
+ mpf_t temp;
+ mpf_init_set_d(temp, val);
- // Never maintain a pointer into a bulk allocation pool; such
- // pointers are not guaranteed to remain.
- if (amt.quantity->flags & BIGINT_BULK_ALLOC) {
- quantity = new bigint_t(*amt.quantity);
+ mp_exp_t exp;
+ char * buf = mpf_get_str(NULL, &exp, 10, 1000, temp);
+
+ int len = std::strlen(buf);
+ if (len > 0 && buf[0] == '-')
+ exp++;
+
+ if (exp <= len) {
+ exp = len - exp;
} else {
- quantity = amt.quantity;
- DEBUG_PRINT("amounts.refs",
- quantity << " ref++, now " << (quantity->ref + 1));
- quantity->ref++;
+ // There were trailing zeros, which we have to put back on in
+ // order to convert this buffer into an integer.
+
+ int zeroes = exp - len;
+
+ char * newbuf = (char *)std::malloc(len + zeroes);
+ std::strcpy(newbuf, buf);
+
+ int i;
+ for (i = 0; i < zeroes; i++)
+ newbuf[len + i] = '0';
+ newbuf[len + i] = '\0';
+
+ free(buf);
+ buf = newbuf;
+
+ exp = (len - exp) + zeroes;
}
+
+ mpz_set_str(dest, buf, 10);
+ free(buf);
+
+ return amount_t::precision_t(exp);
+#else
+ int decpt, sign;
+ char * buf = dtoa(val, 0, 0, &decpt, &sign, NULL);
+ char * result;
+ int len = std::strlen(buf);
+
+ if (decpt <= len) {
+ decpt = len - decpt;
+ result = NULL;
+ } else {
+ // There were trailing zeros, which we have to put back on in
+ // order to convert this buffer into an integer.
+
+ int zeroes = decpt - len;
+ result = new char[len + zeroes + 1];
+
+ std::strcpy(result, buf);
+ int i;
+ for (i = 0; i < zeroes; i++)
+ result[len + i] = '0';
+ result[len + i] = '\0';
+
+ decpt = (len - decpt) + zeroes;
+ }
+
+ if (sign) {
+ char * newbuf = new char[std::strlen(result ? result : buf) + 2];
+ newbuf[0] = '-';
+ std::strcpy(&newbuf[1], result ? result : buf);
+ mpz_set_str(dest, newbuf, 10);
+ checked_array_delete(newbuf);
+ } else {
+ mpz_set_str(dest, result ? result : buf, 10);
+ }
+
+ if (result)
+ checked_array_delete(result);
+ freedtoa(buf);
+
+ return decpt;
+#endif
}
- commodity_ = amt.commodity_;
}
-amount_t& amount_t::operator=(const std::string& value)
+amount_t::amount_t(const double val) : commodity_(NULL)
{
- std::istringstream str(value);
- parse(str);
- return *this;
+ TRACE_CTOR(amount_t, "const double");
+ quantity = new bigint_t;
+ quantity->prec = convert_double(MPZ(quantity), val);
}
-amount_t& amount_t::operator=(const char * value)
+amount_t::amount_t(const unsigned long val) : commodity_(NULL)
{
- std::string valstr(value);
- std::istringstream str(valstr);
- parse(str);
- return *this;
+ TRACE_CTOR(amount_t, "const unsigned long");
+ quantity = new bigint_t;
+ mpz_set_ui(MPZ(quantity), val);
}
-// assignment operator
+amount_t::amount_t(const long val) : commodity_(NULL)
+{
+ TRACE_CTOR(amount_t, "const long");
+ quantity = new bigint_t;
+ mpz_set_si(MPZ(quantity), val);
+}
+
+
amount_t& amount_t::operator=(const amount_t& amt)
{
if (this != &amt) {
@@ -293,99 +319,59 @@ amount_t& amount_t::operator=(const amount_t& amt)
return *this;
}
-amount_t& amount_t::operator=(const bool value)
-{
- if (! value) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- if (quantity)
- _release();
- quantity = &true_value;
- quantity->ref++;
- }
- return *this;
-}
-amount_t& amount_t::operator=(const long value)
+int amount_t::compare(const amount_t& amt) const
{
- if (value == 0) {
+ if (! quantity || ! amt.quantity) {
if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- _init();
- mpz_set_si(MPZ(quantity), value);
+ throw_(amount_error, "Cannot compare an amount to an uninitialized amount");
+ else if (amt.quantity)
+ throw_(amount_error, "Cannot compare an uninitialized amount to an amount");
+ else
+ throw_(amount_error, "Cannot compare two uninitialized amounts");
}
- return *this;
-}
-amount_t& amount_t::operator=(const unsigned long value)
-{
- if (value == 0) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- _init();
- mpz_set_ui(MPZ(quantity), value);
- }
- return *this;
-}
+ if (has_commodity() && amt.has_commodity() &&
+ commodity() != amt.commodity())
+ throw_(amount_error,
+ "Cannot compare amounts with different commodities: " <<
+ commodity().symbol() << " and " << amt.commodity().symbol());
-amount_t& amount_t::operator=(const double value)
-{
- if (value == 0.0) {
- if (quantity)
- _clear();
- } else {
- commodity_ = NULL;
- _init();
- mpz_set_d(MPZ(quantity), value);
+ if (quantity->prec == amt.quantity->prec) {
+ return mpz_cmp(MPZ(quantity), MPZ(amt.quantity));
}
- return *this;
-}
-
-
-void amount_t::_resize(unsigned int prec)
-{
- assert(prec < 256);
-
- if (! quantity || prec == quantity->prec)
- return;
-
- _dup();
-
- if (prec < quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, quantity->prec - prec);
- mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor);
- } else {
- mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
- mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
+ else if (quantity->prec < amt.quantity->prec) {
+ amount_t t(*this);
+ t._resize(amt.quantity->prec);
+ return mpz_cmp(MPZ(t.quantity), MPZ(amt.quantity));
+ }
+ else {
+ amount_t t = amt;
+ t._resize(quantity->prec);
+ return mpz_cmp(MPZ(quantity), MPZ(t.quantity));
}
-
- quantity->prec = prec;
}
amount_t& amount_t::operator+=(const amount_t& amt)
{
- if (! amt.quantity)
- return *this;
-
- if (! quantity) {
- _copy(amt);
- return *this;
+ if (! quantity || ! amt.quantity) {
+ if (quantity)
+ throw_(amount_error, "Cannot add an amount to an uninitialized amount");
+ else if (amt.quantity)
+ throw_(amount_error, "Cannot add an uninitialized amount to an amount");
+ else
+ throw_(amount_error, "Cannot add two uninitialized amounts");
}
- _dup();
-
if (commodity() != amt.commodity())
- throw new amount_error
- (std::string("Adding amounts with different commodities: ") +
- commodity_->qualified_symbol + " != " +
- amt.commodity_->qualified_symbol);
+ throw_(amount_error,
+ "Adding amounts with different commodities: " <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
+ " != " <<
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
+
+ _dup();
if (quantity->prec == amt.quantity->prec) {
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
@@ -395,9 +381,9 @@ amount_t& amount_t::operator+=(const amount_t& amt)
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
}
else {
- amount_t temp = amt;
- temp._resize(quantity->prec);
- mpz_add(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity));
+ amount_t t = amt;
+ t._resize(quantity->prec);
+ mpz_add(MPZ(quantity), MPZ(quantity), MPZ(t.quantity));
}
return *this;
@@ -405,23 +391,23 @@ amount_t& amount_t::operator+=(const amount_t& amt)
amount_t& amount_t::operator-=(const amount_t& amt)
{
- if (! amt.quantity)
- return *this;
-
- if (! quantity) {
- quantity = new bigint_t(*amt.quantity);
- commodity_ = amt.commodity_;
- mpz_neg(MPZ(quantity), MPZ(quantity));
- return *this;
+ if (! quantity || ! amt.quantity) {
+ if (quantity)
+ throw_(amount_error, "Cannot subtract an amount from an uninitialized amount");
+ else if (amt.quantity)
+ throw_(amount_error, "Cannot subtract an uninitialized amount from an amount");
+ else
+ throw_(amount_error, "Cannot subtract two uninitialized amounts");
}
- _dup();
-
if (commodity() != amt.commodity())
- throw new amount_error
- (std::string("Subtracting amounts with different commodities: ") +
- commodity_->qualified_symbol + " != " +
- amt.commodity_->qualified_symbol);
+ throw_(amount_error,
+ "Subtracting amounts with different commodities: " <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
+ " != " <<
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
+
+ _dup();
if (quantity->prec == amt.quantity->prec) {
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
@@ -431,30 +417,95 @@ amount_t& amount_t::operator-=(const amount_t& amt)
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
}
else {
- amount_t temp = amt;
- temp._resize(quantity->prec);
- mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity));
+ amount_t t = amt;
+ t._resize(quantity->prec);
+ mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(t.quantity));
}
return *this;
}
+namespace {
+ void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
+ {
+ // Round `value', with an encoding precision of `value_prec', to a
+ // rounded value with precision `round_prec'. Result is stored in
+ // `out'.
+
+ assert(value_prec > round_prec);
+
+ mpz_t quotient;
+ mpz_t remainder;
+
+ mpz_init(quotient);
+ mpz_init(remainder);
+
+ mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
+ mpz_tdiv_qr(quotient, remainder, value, divisor);
+ mpz_divexact_ui(divisor, divisor, 10);
+ mpz_mul_ui(divisor, divisor, 5);
+
+ if (mpz_sgn(remainder) < 0) {
+ mpz_neg(divisor, divisor);
+ if (mpz_cmp(remainder, divisor) < 0) {
+ mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
+ mpz_add(remainder, divisor, remainder);
+ mpz_ui_sub(remainder, 0, remainder);
+ mpz_add(out, value, remainder);
+ } else {
+ mpz_sub(out, value, remainder);
+ }
+ } else {
+ if (mpz_cmp(remainder, divisor) >= 0) {
+ mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
+ mpz_sub(remainder, divisor, remainder);
+ mpz_add(out, value, remainder);
+ } else {
+ mpz_sub(out, value, remainder);
+ }
+ }
+ mpz_clear(quotient);
+ mpz_clear(remainder);
+
+ // chop off the rounded bits
+ mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
+ mpz_tdiv_q(out, out, divisor);
+ }
+}
+
amount_t& amount_t::operator*=(const amount_t& amt)
{
- if (! amt.quantity)
- return (*this = amt);
- else if (! quantity)
- return *this;
+ if (! quantity || ! amt.quantity) {
+ if (quantity)
+ throw_(amount_error, "Cannot multiply an amount by an uninitialized amount");
+ else if (amt.quantity)
+ throw_(amount_error, "Cannot multiply an uninitialized amount by an amount");
+ else
+ throw_(amount_error, "Cannot multiply two uninitialized amounts");
+ }
+
+ if (has_commodity() && amt.has_commodity() &&
+ commodity() != amt.commodity())
+ throw_(amount_error,
+ "Multiplying amounts with different commodities: " <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
+ " != " <<
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
_dup();
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
quantity->prec += amt.quantity->prec;
- unsigned int comm_prec = commodity().precision();
- if (quantity->prec > comm_prec + 6U) {
- mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
- quantity->prec = comm_prec + 6U;
+ if (! has_commodity())
+ commodity_ = amt.commodity_;
+
+ if (has_commodity() && ! (quantity->has_flags(BIGINT_KEEP_PREC))) {
+ precision_t comm_prec = commodity().precision();
+ if (quantity->prec > comm_prec + 6U) {
+ mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
+ quantity->prec = comm_prec + 6U;
+ }
}
return *this;
@@ -462,121 +513,194 @@ amount_t& amount_t::operator*=(const amount_t& amt)
amount_t& amount_t::operator/=(const amount_t& amt)
{
- if (! amt.quantity || ! amt)
- throw new amount_error("Divide by zero");
- else if (! quantity)
- return *this;
+ if (! quantity || ! amt.quantity) {
+ if (quantity)
+ throw_(amount_error, "Cannot divide an amount by an uninitialized amount");
+ else if (amt.quantity)
+ throw_(amount_error, "Cannot divide an uninitialized amount by an amount");
+ else
+ throw_(amount_error, "Cannot divide two uninitialized amounts");
+ }
+
+ if (has_commodity() && amt.has_commodity() &&
+ commodity() != amt.commodity())
+ throw_(amount_error,
+ "Dividing amounts with different commodities: " <<
+ (has_commodity() ? commodity().symbol() : "NONE") <<
+ " != " <<
+ (amt.has_commodity() ? amt.commodity().symbol() : "NONE"));
+
+ if (! amt)
+ throw_(amount_error, "Divide by zero");
_dup();
// Increase the value's precision, to capture fractional parts after
- // the divide.
- mpz_ui_pow_ui(divisor, 10, amt.quantity->prec + 6U);
+ // the divide. Round up in the last position.
+
+ mpz_ui_pow_ui(divisor, 10, (2 * amt.quantity->prec) + quantity->prec + 7U);
mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
- quantity->prec += 6U;
+ quantity->prec += amt.quantity->prec + quantity->prec + 7U;
+
+ mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, quantity->prec - 1);
+ quantity->prec -= 1;
- unsigned int comm_prec = commodity().precision();
- if (quantity->prec > comm_prec + 6U) {
- mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
- quantity->prec = comm_prec + 6U;
+ if (! has_commodity())
+ commodity_ = amt.commodity_;
+
+ // If this amount has a commodity, and we're not dealing with plain
+ // numbers, or internal numbers (which keep full precision at all
+ // times), then round the number to within the commodity's precision
+ // plus six places.
+
+ if (has_commodity() && ! (quantity->has_flags(BIGINT_KEEP_PREC))) {
+ precision_t comm_prec = commodity().precision();
+ if (quantity->prec > comm_prec + 6U) {
+ mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
+ quantity->prec = comm_prec + 6U;
+ }
}
return *this;
}
-// unary negation
-void amount_t::negate()
+
+amount_t::precision_t amount_t::precision() const
+{
+ if (! quantity)
+ throw_(amount_error, "Cannot determine precision of an uninitialized amount");
+
+ return quantity->prec;
+}
+
+amount_t& amount_t::in_place_negate()
{
if (quantity) {
_dup();
mpz_neg(MPZ(quantity), MPZ(quantity));
+ } else {
+ throw_(amount_error, "Cannot negate an uninitialized amount");
}
+ return *this;
}
-int amount_t::sign() const
+amount_t amount_t::round() const
{
- return quantity ? mpz_sgn(MPZ(quantity)) : 0;
+ if (! quantity)
+ throw_(amount_error, "Cannot round an uninitialized amount");
+
+ if (! has_commodity())
+ return *this;
+
+ return round(commodity().precision());
}
-int amount_t::compare(const amount_t& amt) const
+amount_t amount_t::round(precision_t prec) const
{
- if (! quantity) {
- if (! amt.quantity)
- return 0;
- return - amt.sign();
- }
- if (! amt.quantity)
- return sign();
+ if (! quantity)
+ throw_(amount_error, "Cannot round an uninitialized amount");
- if (commodity() && amt.commodity() && commodity() != amt.commodity())
- throw new amount_error
- (std::string("Cannot compare amounts with different commodities: ") +
- commodity().symbol() + " and " + amt.commodity().symbol());
+ amount_t t(*this);
- if (quantity->prec == amt.quantity->prec) {
- return mpz_cmp(MPZ(quantity), MPZ(amt.quantity));
- }
- else if (quantity->prec < amt.quantity->prec) {
- amount_t temp = *this;
- temp._resize(amt.quantity->prec);
- return mpz_cmp(MPZ(temp.quantity), MPZ(amt.quantity));
- }
- else {
- amount_t temp = amt;
- temp._resize(quantity->prec);
- return mpz_cmp(MPZ(quantity), MPZ(temp.quantity));
+ if (quantity->prec <= prec) {
+ if (quantity && quantity->has_flags(BIGINT_KEEP_PREC)) {
+ t._dup();
+ t.quantity->drop_flags(BIGINT_KEEP_PREC);
+ }
+ return t;
}
+
+ t._dup();
+
+ mpz_round(MPZ(t.quantity), MPZ(t.quantity), t.quantity->prec, prec);
+
+ t.quantity->prec = prec;
+ t.quantity->drop_flags(BIGINT_KEEP_PREC);
+
+ return t;
}
-bool amount_t::operator==(const amount_t& amt) const
+amount_t amount_t::unround() const
{
- if (commodity() != amt.commodity())
- return false;
- return compare(amt) == 0;
+ if (! quantity)
+ throw_(amount_error, "Cannot unround an uninitialized amount");
+ else if (quantity->has_flags(BIGINT_KEEP_PREC))
+ return *this;
+
+ amount_t t(*this);
+ t._dup();
+ t.quantity->add_flags(BIGINT_KEEP_PREC);
+
+ return t;
}
-bool amount_t::operator!=(const amount_t& amt) const
+amount_t& amount_t::in_place_reduce()
{
- if (commodity() != amt.commodity())
- return true;
- return compare(amt) != 0;
+ if (! quantity)
+ throw_(amount_error, "Cannot reduce an uninitialized amount");
+
+ while (commodity_ && commodity().smaller()) {
+ *this *= commodity().smaller()->number();
+ commodity_ = commodity().smaller()->commodity_;
+ }
+ return *this;
}
-amount_t::operator bool() const
+amount_t& amount_t::in_place_unreduce()
{
if (! quantity)
- return false;
+ throw_(amount_error, "Cannot unreduce an uninitialized amount");
- if (quantity->prec <= commodity().precision()) {
- return mpz_sgn(MPZ(quantity)) != 0;
+ while (commodity_ && commodity().larger()) {
+ *this /= commodity().larger()->number();
+ commodity_ = commodity().larger()->commodity_;
+ if (abs() < amount_t(1.0))
+ break;
+ }
+ return *this;
+}
+
+optional<amount_t> amount_t::value(const optional<moment_t>& moment) const
+{
+ if (quantity) {
+ optional<amount_t> amt(commodity().value(moment));
+ if (amt)
+ return (*amt * number()).round();
} else {
- mpz_set(temp, MPZ(quantity));
- if (quantity->flags & BIGINT_KEEP_PREC)
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- else
- mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision());
- mpz_tdiv_q(temp, temp, divisor);
- bool zero = mpz_sgn(temp) == 0;
- return ! zero;
+ throw_(amount_error, "Cannot determine value of an uninitialized amount");
}
+ return none;
}
-amount_t::operator long() const
+
+int amount_t::sign() const
{
if (! quantity)
- return 0;
+ throw_(amount_error, "Cannot determine sign of an uninitialized amount");
- mpz_set(temp, MPZ(quantity));
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_q(temp, temp, divisor);
- return mpz_get_si(temp);
+ return mpz_sgn(MPZ(quantity));
}
-amount_t::operator double() const
+bool amount_t::is_zero() const
{
if (! quantity)
- return 0.0;
+ throw_(amount_error, "Cannot determine sign if an uninitialized amount is zero");
+
+ if (has_commodity()) {
+ if (quantity->prec <= commodity().precision())
+ return is_realzero();
+ else
+ return round(commodity().precision()).sign() == 0;
+ }
+ return is_realzero();
+}
+
+
+double amount_t::to_double(bool no_check) const
+{
+ if (! quantity)
+ throw_(amount_error, "Cannot convert an uninitialized amount to a double");
mpz_t remainder;
mpz_init(remainder);
@@ -596,486 +720,151 @@ amount_t::operator double() const
mpz_clear(remainder);
- return std::atof(num.str().c_str());
-}
+ double value = lexical_cast<double>(num.str());
-bool amount_t::realzero() const
-{
- if (! quantity)
- return true;
- return mpz_sgn(MPZ(quantity)) == 0;
-}
+ if (! no_check && *this != value)
+ throw_(amount_error, "Conversion of amount to_double loses precision");
-amount_t amount_t::value(const datetime_t& moment) const
-{
- if (quantity) {
- amount_t amt(commodity().value(moment));
- if (! amt.realzero())
- return (amt * *this).round();
- }
- return *this;
+ return value;
}
-amount_t amount_t::round(unsigned int prec) const
+long amount_t::to_long(bool no_check) const
{
- amount_t temp = *this;
-
- if (! quantity || quantity->prec <= prec) {
- if (quantity && quantity->flags & BIGINT_KEEP_PREC) {
- temp._dup();
- temp.quantity->flags &= ~BIGINT_KEEP_PREC;
- }
- return temp;
- }
+ if (! quantity)
+ throw_(amount_error, "Cannot convert an uninitialized amount to a long");
- temp._dup();
+ mpz_set(temp, MPZ(quantity));
+ mpz_ui_pow_ui(divisor, 10, quantity->prec);
+ mpz_tdiv_q(temp, temp, divisor);
- mpz_round(MPZ(temp.quantity), MPZ(temp.quantity), temp.quantity->prec, prec);
+ long value = mpz_get_si(temp);
- temp.quantity->prec = prec;
- temp.quantity->flags &= ~BIGINT_KEEP_PREC;
+ if (! no_check && *this != value)
+ throw_(amount_error, "Conversion of amount to_long loses precision");
- return temp;
+ return value;
}
-amount_t amount_t::unround() const
+bool amount_t::fits_in_double() const
{
- if (! quantity) {
- amount_t temp(0L);
- assert(temp.quantity);
- temp.quantity->flags |= BIGINT_KEEP_PREC;
- return temp;
- }
- else if (quantity->flags & BIGINT_KEEP_PREC) {
- return *this;
- }
-
- amount_t temp = *this;
- temp._dup();
- temp.quantity->flags |= BIGINT_KEEP_PREC;
-
- return temp;
+ double value = to_double(true);
+ return *this == amount_t(value);
}
-std::string amount_t::quantity_string() const
+bool amount_t::fits_in_long() const
{
- if (! quantity)
- return "0";
-
- std::ostringstream out;
-
- mpz_t quotient;
- mpz_t rquotient;
- mpz_t remainder;
-
- mpz_init(quotient);
- mpz_init(rquotient);
- mpz_init(remainder);
-
- bool negative = false;
-
- // Ensure the value is rounded to the commodity's precision before
- // outputting it. NOTE: `rquotient' is used here as a temp variable!
-
- commodity_t& comm(commodity());
- unsigned char precision;
-
- if (! comm || quantity->flags & BIGINT_KEEP_PREC) {
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
- precision = quantity->prec;
- }
- else if (comm.precision() < quantity->prec) {
- mpz_round(rquotient, MPZ(quantity), quantity->prec, comm.precision());
- mpz_ui_pow_ui(divisor, 10, comm.precision());
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision();
- }
- else if (comm.precision() > quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, comm.precision() - quantity->prec);
- mpz_mul(rquotient, MPZ(quantity), divisor);
- mpz_ui_pow_ui(divisor, 10, comm.precision());
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision();
- }
- else if (quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
- precision = quantity->prec;
- }
- else {
- mpz_set(quotient, MPZ(quantity));
- mpz_set_ui(remainder, 0);
- precision = 0;
- }
-
- if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
- negative = true;
+ long value = to_long(true);
+ return *this == amount_t(value);
+}
- mpz_abs(quotient, quotient);
- mpz_abs(remainder, remainder);
- }
- mpz_set(rquotient, remainder);
- if (mpz_sgn(quotient) == 0 && mpz_sgn(rquotient) == 0)
- return "0";
+void amount_t::annotate_commodity(const annotation_t& details)
+{
+ commodity_t * this_base;
+ annotated_commodity_t * this_ann = NULL;
- if (negative)
- out << "-";
+ if (! quantity)
+ throw_(amount_error, "Cannot annotate the commodity of an uninitialized amount");
+ else if (! has_commodity())
+ throw_(amount_error, "Cannot annotate an amount with no commodity");
- if (mpz_sgn(quotient) == 0) {
- out << '0';
+ if (commodity().annotated) {
+ this_ann = &as_annotated_commodity(commodity());
+ this_base = &this_ann->referent();
} else {
- char * p = mpz_get_str(NULL, 10, quotient);
- out << p;
- std::free(p);
+ this_base = &commodity();
}
+ assert(this_base);
- if (precision) {
- out << '.';
-
- out.width(precision);
- out.fill('0');
-
- char * p = mpz_get_str(NULL, 10, rquotient);
- out << p;
- std::free(p);
- }
+ DEBUG("amounts.commodities", "Annotating commodity for amount "
+ << *this << std::endl << details);
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
+ if (commodity_t * ann_comm =
+ this_base->parent().find_or_create(*this_base, details))
+ set_commodity(*ann_comm);
+#ifdef ASSERTS_ON
+ else
+ assert(false);
+#endif
- return out.str();
+ DEBUG("amounts.commodities", " Annotated amount is " << *this);
}
-std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
+bool amount_t::commodity_annotated() const
{
- if (! amt.quantity) {
- _out << "0";
- return _out;
- }
-
- amount_t base(amt);
- if (! amount_t::keep_base && amt.commodity().larger()) {
- amount_t last(amt);
- while (last.commodity().larger()) {
- last /= *last.commodity().larger();
- last.commodity_ = last.commodity().larger()->commodity_;
- if (ledger::abs(last) < 1)
- break;
- base = last.round();
- }
- }
-
- std::ostringstream out;
-
- mpz_t quotient;
- mpz_t rquotient;
- mpz_t remainder;
-
- mpz_init(quotient);
- mpz_init(rquotient);
- mpz_init(remainder);
-
- bool negative = false;
-
- // Ensure the value is rounded to the commodity's precision before
- // outputting it. NOTE: `rquotient' is used here as a temp variable!
-
- commodity_t& comm(base.commodity());
- unsigned char precision;
-
- if (! comm || base.quantity->flags & BIGINT_KEEP_PREC) {
- mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
- precision = base.quantity->prec;
- }
- else if (comm.precision() < base.quantity->prec) {
- mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec,
- comm.precision());
- mpz_ui_pow_ui(divisor, 10, comm.precision());
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision();
- }
- else if (comm.precision() > base.quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
- mpz_mul(rquotient, MPZ(base.quantity), divisor);
- mpz_ui_pow_ui(divisor, 10, comm.precision());
- mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
- precision = comm.precision();
- }
- else if (base.quantity->prec) {
- mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
- mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
- precision = base.quantity->prec;
- }
- else {
- mpz_set(quotient, MPZ(base.quantity));
- mpz_set_ui(remainder, 0);
- precision = 0;
- }
-
- if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
- negative = true;
-
- mpz_abs(quotient, quotient);
- mpz_abs(remainder, remainder);
- }
- mpz_set(rquotient, remainder);
-
- if (mpz_sgn(quotient) == 0 && mpz_sgn(rquotient) == 0) {
- _out << "0";
- return _out;
- }
-
- if (! (comm.flags() & COMMODITY_STYLE_SUFFIXED)) {
- comm.write(out);
-
- if (comm.flags() & COMMODITY_STYLE_SEPARATED)
- out << " ";
- }
-
- if (negative)
- out << "-";
-
- if (mpz_sgn(quotient) == 0) {
- out << '0';
- }
- else if (! (comm.flags() & COMMODITY_STYLE_THOUSANDS)) {
- char * p = mpz_get_str(NULL, 10, quotient);
- out << p;
- std::free(p);
- }
- else {
- std::list<std::string> strs;
- char buf[4];
-
- for (int powers = 0; true; powers += 3) {
- if (powers > 0) {
- mpz_ui_pow_ui(divisor, 10, powers);
- mpz_tdiv_q(temp, quotient, divisor);
- if (mpz_sgn(temp) == 0)
- break;
- mpz_tdiv_r_ui(temp, temp, 1000);
- } else {
- mpz_tdiv_r_ui(temp, quotient, 1000);
- }
- mpz_get_str(buf, 10, temp);
- strs.push_back(buf);
- }
-
- bool printed = false;
-
- for (std::list<std::string>::reverse_iterator i = strs.rbegin();
- i != strs.rend();
- i++) {
- if (printed) {
- out << (comm.flags() & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
- out.width(3);
- out.fill('0');
- }
- out << *i;
-
- printed = true;
- }
- }
-
- if (precision) {
- std::ostringstream final;
- final.width(precision);
- final.fill('0');
- char * p = mpz_get_str(NULL, 10, rquotient);
- final << p;
- std::free(p);
-
- const std::string& str(final.str());
- int i, len = str.length();
- const char * q = str.c_str();
- for (i = len; i > 0; i--)
- if (q[i - 1] != '0')
- break;
-
- std::string ender;
- if (i == len)
- ender = str;
- else if (i < comm.precision())
- ender = std::string(str, 0, comm.precision());
- else
- ender = std::string(str, 0, i);
-
- if (! ender.empty()) {
- out << ((comm.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
- out << ender;
- }
- }
-
- if (comm.flags() & COMMODITY_STYLE_SUFFIXED) {
- if (comm.flags() & COMMODITY_STYLE_SEPARATED)
- out << " ";
+ if (! quantity)
+ throw_(amount_error,
+ "Cannot determine if an uninitialized amount's commodity is annotated");
- comm.write(out);
- }
+ assert(! commodity().annotated || as_annotated_commodity(commodity()).details);
+ return commodity().annotated;
+}
- mpz_clear(quotient);
- mpz_clear(rquotient);
- mpz_clear(remainder);
+annotation_t amount_t::annotation_details() const
+{
+ if (! quantity)
+ throw_(amount_error,
+ "Cannot return commodity annotation details of an uninitialized amount");
- // If there are any annotations associated with this commodity,
- // output them now.
+ assert(! commodity().annotated || as_annotated_commodity(commodity()).details);
- if (comm.annotated) {
- annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
- assert(&ann.price != &amt);
- ann.write_annotations(out);
+ if (commodity().annotated) {
+ annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
+ return ann_comm.details;
}
-
- // Things are output to a string first, so that if anyone has
- // specified a width or fill for _out, it will be applied to the
- // entire amount string, and not just the first part.
-
- _out << out.str();
-
- return _out;
+ return annotation_t();
}
-void parse_quantity(std::istream& in, std::string& value)
+amount_t amount_t::strip_annotations(const bool _keep_price,
+ const bool _keep_date,
+ const bool _keep_tag) const
{
- char buf[256];
- char c = peek_next_nonws(in);
- READ_INTO(in, buf, 255, c,
- std::isdigit(c) || c == '-' || c == '.' || c == ',');
+ if (! quantity)
+ throw_(amount_error,
+ "Cannot strip commodity annotations from an uninitialized amount");
- int len = std::strlen(buf);
- while (len > 0 && ! std::isdigit(buf[len - 1])) {
- buf[--len] = '\0';
- in.unget();
- }
+ if (! commodity().annotated ||
+ (_keep_price && _keep_date && _keep_tag))
+ return *this;
- value = buf;
+ amount_t t(*this);
+ t.set_commodity(as_annotated_commodity(commodity()).
+ strip_annotations(_keep_price, _keep_date, _keep_tag));
+ return t;
}
-// Invalid commodity characters:
-// SPACE, TAB, NEWLINE, RETURN
-// 0-9 . , ; - + * / ^ ? : & | ! =
-// < > { } [ ] ( ) @
-
-int invalid_chars[256] = {
- /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
-/* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
-/* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
-/* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-/* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
-/* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
-/* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-void parse_commodity(std::istream& in, std::string& symbol)
-{
- char buf[256];
- char c = peek_next_nonws(in);
- if (c == '"') {
- in.get(c);
- READ_INTO(in, buf, 255, c, c != '"');
- if (c == '"')
- in.get(c);
- else
- throw new amount_error("Quoted commodity symbol lacks closing quote");
- } else {
- READ_INTO(in, buf, 255, c, ! invalid_chars[(unsigned char)c]);
- }
- symbol = buf;
-}
-
-void parse_annotations(std::istream& in, amount_t& price,
- datetime_t& date, std::string& tag)
-{
- do {
+namespace {
+ void parse_quantity(std::istream& in, string& value)
+ {
char buf[256];
char c = peek_next_nonws(in);
- if (c == '{') {
- if (price)
- throw new amount_error("Commodity specifies more than one price");
-
- in.get(c);
- READ_INTO(in, buf, 255, c, c != '}');
- if (c == '}')
- in.get(c);
- else
- throw new amount_error("Commodity price lacks closing brace");
-
- price.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
- price.reduce();
-
- // Since this price will maintain its own precision, make sure
- // it is at least as large as the base commodity, since the user
- // may have only specified {$1} or something similar.
-
- if (price.quantity->prec < price.commodity().precision())
- price = price.round(); // no need to retain individual precision
- }
- else if (c == '[') {
- if (date)
- throw new amount_error("Commodity specifies more than one date");
-
- in.get(c);
- READ_INTO(in, buf, 255, c, c != ']');
- if (c == ']')
- in.get(c);
- else
- throw new amount_error("Commodity date lacks closing bracket");
-
- date = buf;
- }
- else if (c == '(') {
- if (! tag.empty())
- throw new amount_error("Commodity specifies more than one tag");
-
- in.get(c);
- READ_INTO(in, buf, 255, c, c != ')');
- if (c == ')')
- in.get(c);
- else
- throw new amount_error("Commodity tag lacks closing parenthesis");
+ READ_INTO(in, buf, 255, c,
+ std::isdigit(c) || c == '-' || c == '.' || c == ',');
- tag = buf;
+ int len = std::strlen(buf);
+ while (len > 0 && ! std::isdigit(buf[len - 1])) {
+ buf[--len] = '\0';
+ in.unget();
}
- else {
- break;
- }
- } while (true);
- DEBUG_PRINT("amounts.commodities",
- "Parsed commodity annotations: "
- << " price " << price << " "
- << " date " << date << " "
- << " tag " << tag);
+ value = buf;
+ }
}
-void amount_t::parse(std::istream& in, unsigned char flags)
+void amount_t::parse(std::istream& in, flags_t flags)
{
// The possible syntax for an amount is:
//
// [-]NUM[ ]SYM [@ AMOUNT]
// SYM[ ][-]NUM [@ AMOUNT]
- std::string symbol;
- std::string quant;
- amount_t price;
- datetime_t date;
- std::string tag;
- unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
- bool negative = false;
+ string symbol;
+ string quant;
+ annotation_t details;
+ bool negative = false;
+
+ commodity_t::flags_t comm_flags = COMMODITY_STYLE_DEFAULTS;
char c = peek_next_nonws(in);
if (c == '-') {
@@ -1092,16 +881,16 @@ void amount_t::parse(std::istream& in, unsigned char flags)
if (std::isspace(n))
comm_flags |= COMMODITY_STYLE_SEPARATED;
- parse_commodity(in, symbol);
+ commodity_t::parse_symbol(in, symbol);
if (! symbol.empty())
comm_flags |= COMMODITY_STYLE_SUFFIXED;
if (! in.eof() && ((n = in.peek()) != '\n'))
- parse_annotations(in, price, date, tag);
+ details.parse(in);
}
} else {
- parse_commodity(in, symbol);
+ commodity_t::parse_symbol(in, symbol);
if (! in.eof() && ((n = in.peek()) != '\n')) {
if (std::isspace(in.peek()))
@@ -1110,14 +899,30 @@ void amount_t::parse(std::istream& in, unsigned char flags)
parse_quantity(in, quant);
if (! quant.empty() && ! in.eof() && ((n = in.peek()) != '\n'))
- parse_annotations(in, price, date, tag);
+ details.parse(in);
}
}
if (quant.empty())
- throw new amount_error("No quantity specified for amount");
+ throw_(amount_error, "No quantity specified for amount");
- _init();
+ // Allocate memory for the amount's quantity value. We have to
+ // monitor the allocation in an auto_ptr because this function gets
+ // called sometimes from amount_t's constructor; and if there is an
+ // exeception thrown by any of the function calls after this point,
+ // the destructor will never be called and the memory never freed.
+
+ std::auto_ptr<bigint_t> safe_holder;
+
+ if (! quantity) {
+ quantity = new bigint_t;
+ safe_holder.reset(quantity);
+ }
+ else if (quantity->ref > 1) {
+ _release();
+ quantity = new bigint_t;
+ safe_holder.reset(quantity);
+ }
// Create the commodity if has not already been seen, and update the
// precision if something greater was used for the quantity.
@@ -1125,27 +930,26 @@ void amount_t::parse(std::istream& in, unsigned char flags)
bool newly_created = false;
if (symbol.empty()) {
- commodity_ = commodity_t::null_commodity;
+ commodity_ = NULL;
} else {
- commodity_ = commodity_t::find(symbol);
+ commodity_ = current_pool->find(symbol);
if (! commodity_) {
- commodity_ = commodity_t::create(symbol);
+ commodity_ = current_pool->create(symbol);
newly_created = true;
}
assert(commodity_);
- if (! price.realzero() || date || ! tag.empty())
- commodity_ =
- annotated_commodity_t::find_or_create(*commodity_, price, date, tag);
+ if (details)
+ commodity_ = current_pool->find_or_create(*commodity_, details);
}
// Determine the precision of the amount, based on the usage of
// comma or period.
- std::string::size_type last_comma = quant.rfind(',');
- std::string::size_type last_period = quant.rfind('.');
+ string::size_type last_comma = quant.rfind(',');
+ string::size_type last_period = quant.rfind('.');
- if (last_comma != std::string::npos && last_period != std::string::npos) {
+ if (last_comma != string::npos && last_period != string::npos) {
comm_flags |= COMMODITY_STYLE_THOUSANDS;
if (last_comma > last_period) {
comm_flags |= COMMODITY_STYLE_EUROPEAN;
@@ -1154,14 +958,12 @@ void amount_t::parse(std::istream& in, unsigned char flags)
quantity->prec = quant.length() - last_period - 1;
}
}
- else if (last_comma != std::string::npos &&
- (! commodity_t::default_commodity ||
- commodity_t::default_commodity->flags() & COMMODITY_STYLE_EUROPEAN)) {
- comm_flags |= COMMODITY_STYLE_EUROPEAN;
+ else if (last_comma != string::npos &&
+ commodity().has_flags(COMMODITY_STYLE_EUROPEAN)) {
quantity->prec = quant.length() - last_comma - 1;
}
- else if (last_period != std::string::npos &&
- ! (commodity().flags() & COMMODITY_STYLE_EUROPEAN)) {
+ else if (last_period != string::npos &&
+ ! (commodity().has_flags(COMMODITY_STYLE_EUROPEAN))) {
quantity->prec = quant.length() - last_period - 1;
}
else {
@@ -1170,19 +972,22 @@ void amount_t::parse(std::istream& in, unsigned char flags)
// Set the commodity's flags and precision accordingly
- if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
+ if (commodity_ && (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
commodity().add_flags(comm_flags);
+
if (quantity->prec > commodity().precision())
commodity().set_precision(quantity->prec);
}
+ // Setup the amount's own flags
+
if (flags & AMOUNT_PARSE_NO_MIGRATE)
- quantity->flags |= BIGINT_KEEP_PREC;
+ quantity->add_flags(BIGINT_KEEP_PREC);
// Now we have the final number. Remove commas and periods, if
// necessary.
- if (last_comma != std::string::npos || last_period != std::string::npos) {
+ if (last_comma != string::npos || last_period != string::npos) {
int len = quant.length();
char * buf = new char[len + 1];
const char * p = quant.c_str();
@@ -1196,41 +1001,29 @@ void amount_t::parse(std::istream& in, unsigned char flags)
*t = '\0';
mpz_set_str(MPZ(quantity), buf, 10);
- delete[] buf;
+ checked_array_delete(buf);
} else {
mpz_set_str(MPZ(quantity), quant.c_str(), 10);
}
if (negative)
- negate();
+ in_place_negate();
if (! (flags & AMOUNT_PARSE_NO_REDUCE))
- reduce();
-}
+ in_place_reduce();
-void amount_t::reduce()
-{
- while (commodity_ && commodity().smaller()) {
- *this *= *commodity().smaller();
- commodity_ = commodity().smaller()->commodity_;
- }
-}
-
-void amount_t::parse(const std::string& str, unsigned char flags)
-{
- std::istringstream stream(str);
- parse(stream, flags);
+ safe_holder.release(); // `this->quantity' owns the pointer
}
-void parse_conversion(const std::string& larger_str,
- const std::string& smaller_str)
+void amount_t::parse_conversion(const string& larger_str,
+ const string& smaller_str)
{
amount_t larger, smaller;
- larger.parse(larger_str.c_str(), AMOUNT_PARSE_NO_REDUCE);
- smaller.parse(smaller_str.c_str(), AMOUNT_PARSE_NO_REDUCE);
+ larger.parse(larger_str, AMOUNT_PARSE_NO_REDUCE);
+ smaller.parse(smaller_str, AMOUNT_PARSE_NO_REDUCE);
- larger *= smaller;
+ larger *= smaller.number();
if (larger.commodity()) {
larger.commodity().set_smaller(smaller);
@@ -1242,774 +1035,366 @@ void parse_conversion(const std::string& larger_str,
}
-char * bigints;
-char * bigints_next;
-unsigned int bigints_index;
-unsigned int bigints_count;
-
-void amount_t::read_quantity(char *& data)
+void amount_t::print(std::ostream& _out, bool omit_commodity,
+ bool full_precision) const
{
- char byte = *data++;;
+ if (! quantity)
+ throw_(amount_error, "Cannot write out an uninitialized amount");
- if (byte == 0) {
- quantity = NULL;
- }
- else if (byte == 1) {
- quantity = new((bigint_t *)bigints_next) bigint_t;
- bigints_next += sizeof(bigint_t);
+ amount_t base(*this);
+ if (! amount_t::keep_base)
+ base.in_place_unreduce();
- unsigned short len = *((unsigned short *) data);
- data += sizeof(unsigned short);
- mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
- 0, 0, data);
- data += len;
-
- char negative = *data++;
- if (negative)
- mpz_neg(MPZ(quantity), MPZ(quantity));
+ std::ostringstream out;
- quantity->prec = *((unsigned char *) data);
- data += sizeof(unsigned char);
- quantity->flags = *((unsigned char *) data);
- data += sizeof(unsigned char);
- quantity->flags |= BIGINT_BULK_ALLOC;
- } else {
- unsigned int index = *((unsigned int *) data);
- data += sizeof(unsigned int);
+ mpz_t quotient;
+ mpz_t rquotient;
+ mpz_t remainder;
- quantity = (bigint_t *) (bigints + (index - 1) * sizeof(bigint_t));
- DEBUG_PRINT("amounts.refs",
- quantity << " ref++, now " << (quantity->ref + 1));
- quantity->ref++;
- }
-}
+ mpz_init(quotient);
+ mpz_init(rquotient);
+ mpz_init(remainder);
-static char buf[4096];
+ bool negative = false;
-void amount_t::read_quantity(std::istream& in)
-{
- char byte;
- in.read(&byte, sizeof(byte));
+ // Ensure the value is rounded to the commodity's precision before
+ // outputting it. NOTE: `rquotient' is used here as a temp variable!
- if (byte == 0) {
- quantity = NULL;
- }
- else if (byte == 1) {
- quantity = new bigint_t;
+ commodity_t& comm(base.commodity());
+ precision_t precision = 0;
- unsigned short len;
- in.read((char *)&len, sizeof(len));
- assert(len < 4096);
- in.read(buf, len);
- mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
- 0, 0, buf);
+ if (quantity) {
+ if (! comm || full_precision || base.quantity->has_flags(BIGINT_KEEP_PREC)) {
+ mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
+ mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
+ precision = base.quantity->prec;
+ }
+ else if (comm.precision() < base.quantity->prec) {
+ mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec,
+ comm.precision());
+ mpz_ui_pow_ui(divisor, 10, comm.precision());
+ mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
+ precision = comm.precision();
+ }
+ else if (comm.precision() > base.quantity->prec) {
+ mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
+ mpz_mul(rquotient, MPZ(base.quantity), divisor);
+ mpz_ui_pow_ui(divisor, 10, comm.precision());
+ mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
+ precision = comm.precision();
+ }
+ else if (base.quantity->prec) {
+ mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
+ mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
+ precision = base.quantity->prec;
+ }
+ else {
+ mpz_set(quotient, MPZ(base.quantity));
+ mpz_set_ui(remainder, 0);
+ precision = 0;
+ }
- char negative;
- in.read(&negative, sizeof(negative));
- if (negative)
- mpz_neg(MPZ(quantity), MPZ(quantity));
+ if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
+ negative = true;
- in.read((char *)&quantity->prec, sizeof(quantity->prec));
- in.read((char *)&quantity->flags, sizeof(quantity->flags));
- }
- else {
- assert(0);
+ mpz_abs(quotient, quotient);
+ mpz_abs(remainder, remainder);
+ }
+ mpz_set(rquotient, remainder);
}
-}
-
-void amount_t::write_quantity(std::ostream& out) const
-{
- char byte;
- if (! quantity) {
- byte = 0;
- out.write(&byte, sizeof(byte));
- return;
+ if (! omit_commodity && ! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
+ comm.print(out);
+ if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
+ out << " ";
}
- if (quantity->index == 0) {
- quantity->index = ++bigints_index;
- bigints_count++;
+ if (negative)
+ out << "-";
- byte = 1;
- out.write(&byte, sizeof(byte));
+ if (! quantity || mpz_sgn(quotient) == 0) {
+ out << '0';
+ }
+ else if (omit_commodity || ! comm.has_flags(COMMODITY_STYLE_THOUSANDS)) {
+ char * p = mpz_get_str(NULL, 10, quotient);
+ out << p;
+ std::free(p);
+ }
+ else {
+ std::list<string> strs;
+ char buf[4];
- std::size_t size;
- mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity));
- unsigned short len = size * sizeof(short);
- out.write((char *)&len, sizeof(len));
- if (len) {
- assert(len < 4096);
- out.write(buf, len);
+ for (int powers = 0; true; powers += 3) {
+ if (powers > 0) {
+ mpz_ui_pow_ui(divisor, 10, powers);
+ mpz_tdiv_q(temp, quotient, divisor);
+ if (mpz_sgn(temp) == 0)
+ break;
+ mpz_tdiv_r_ui(temp, temp, 1000);
+ } else {
+ mpz_tdiv_r_ui(temp, quotient, 1000);
+ }
+ mpz_get_str(buf, 10, temp);
+ strs.push_back(buf);
}
- byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
- out.write(&byte, sizeof(byte));
-
- out.write((char *)&quantity->prec, sizeof(quantity->prec));
- unsigned char flags = quantity->flags & ~BIGINT_BULK_ALLOC;
- assert(sizeof(flags) == sizeof(quantity->flags));
- out.write((char *)&flags, sizeof(flags));
- } else {
- assert(quantity->ref > 1);
+ bool printed = false;
- // Since this value has already been written, we simply write
- // out a reference to which one it was.
- byte = 2;
- out.write(&byte, sizeof(byte));
- out.write((char *)&quantity->index, sizeof(quantity->index));
- }
-}
+ for (std::list<string>::reverse_iterator i = strs.rbegin();
+ i != strs.rend();
+ i++) {
+ if (printed) {
+ out << (comm.has_flags(COMMODITY_STYLE_EUROPEAN) ? '.' : ',');
+ out.width(3);
+ out.fill('0');
+ }
+ out << *i;
-bool amount_t::valid() const
-{
- if (quantity) {
- if (quantity->ref == 0) {
- DEBUG_PRINT("ledger.validate", "amount_t: quantity->ref == 0");
- return false;
+ printed = true;
}
}
- else if (commodity_) {
- DEBUG_PRINT("ledger.validate", "amount_t: commodity_ != NULL");
- return false;
- }
- return true;
-}
-
-void amount_t::annotate_commodity(const amount_t& price,
- const datetime_t& date,
- const std::string& tag)
-{
- const commodity_t * this_base;
- annotated_commodity_t * this_ann = NULL;
- if (commodity().annotated) {
- this_ann = &static_cast<annotated_commodity_t&>(commodity());
- this_base = this_ann->ptr;
- } else {
- this_base = &commodity();
- }
- assert(this_base);
+ if (quantity && precision) {
+ std::ostringstream final;
+ final.width(precision);
+ final.fill('0');
+ char * p = mpz_get_str(NULL, 10, rquotient);
+ final << p;
+ std::free(p);
- DEBUG_PRINT("amounts.commodities", "Annotating commodity for amount "
- << *this << std::endl
- << " price " << price << " "
- << " date " << date << " "
- << " tag " << tag);
-
- commodity_t * ann_comm =
- annotated_commodity_t::find_or_create
- (*this_base, ! price && this_ann ? this_ann->price : price,
- ! date && this_ann ? this_ann->date : date,
- tag.empty() && this_ann ? this_ann->tag : tag);
- if (ann_comm)
- set_commodity(*ann_comm);
+ const string& str(final.str());
+ int i, len = str.length();
+ const char * q = str.c_str();
+ for (i = len; i > 0; i--)
+ if (q[i - 1] != '0')
+ break;
- DEBUG_PRINT("amounts.commodities", " Annotated amount is " << *this);
-}
+ string ender;
+ if (i == len)
+ ender = str;
+ else if (i < comm.precision())
+ ender = string(str, 0, comm.precision());
+ else
+ ender = string(str, 0, i);
-amount_t amount_t::strip_annotations(const bool _keep_price,
- const bool _keep_date,
- const bool _keep_tag) const
-{
- if (! commodity().annotated ||
- (_keep_price && _keep_date && _keep_tag))
- return *this;
+ if (! ender.empty()) {
+ if (omit_commodity)
+ out << '.';
+ else
+ out << (comm.has_flags(COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
+ out << ender;
+ }
+ }
- DEBUG_PRINT("amounts.commodities", "Reducing commodity for amount "
- << *this << std::endl
- << " keep price " << _keep_price << " "
- << " keep date " << _keep_date << " "
- << " keep tag " << _keep_tag);
+ if (! omit_commodity && comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
+ if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
+ out << " ";
+ comm.print(out);
+ }
- annotated_commodity_t&
- ann_comm(static_cast<annotated_commodity_t&>(commodity()));
- assert(ann_comm.base);
+ mpz_clear(quotient);
+ mpz_clear(rquotient);
+ mpz_clear(remainder);
- commodity_t * new_comm;
+ // If there are any annotations associated with this commodity,
+ // output them now.
- if ((_keep_price && ann_comm.price) ||
- (_keep_date && ann_comm.date) ||
- (_keep_tag && ! ann_comm.tag.empty()))
- {
- new_comm = annotated_commodity_t::find_or_create
- (*ann_comm.ptr, _keep_price ? ann_comm.price : amount_t(),
- _keep_date ? ann_comm.date : datetime_t(),
- _keep_tag ? ann_comm.tag : "");
- } else {
- new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
+ if (! omit_commodity && comm.annotated) {
+ annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
+ assert(&*ann.details.price != this);
+ ann.write_annotations(out);
}
- assert(new_comm);
- amount_t temp(*this);
- temp.set_commodity(*new_comm);
-
- DEBUG_PRINT("amounts.commodities", " Reduced amount is " << temp);
+ // Things are output to a string first, so that if anyone has
+ // specified a width or fill for _out, it will be applied to the
+ // entire amount string, and not just the first part.
- return temp;
+ _out << out.str();
}
-amount_t amount_t::price() const
-{
- if (commodity_ && commodity_->annotated) {
- amount_t temp(((annotated_commodity_t *)commodity_)->price);
- temp *= *this;
- DEBUG_PRINT("amounts.commodities",
- "Returning price of " << *this << " = " << temp);
- return temp;
- }
- return *this;
-}
-datetime_t amount_t::date() const
-{
- if (commodity_ && commodity_->annotated) {
- DEBUG_PRINT("amounts.commodities",
- "Returning date of " << *this << " = "
- << ((annotated_commodity_t *)commodity_)->date);
- return ((annotated_commodity_t *)commodity_)->date;
- }
- return 0L;
+namespace {
+ char * bigints;
+ char * bigints_next;
+ uint_fast32_t bigints_index;
+ uint_fast32_t bigints_count;
+ char buf[4096];
}
-
-void commodity_base_t::add_price(const datetime_t& date,
- const amount_t& price)
+void amount_t::read(std::istream& in)
{
- if (! history)
- history = new history_t;
+ using namespace ledger::binary;
- history_map::iterator i = history->prices.find(date);
- if (i != history->prices.end()) {
- (*i).second = price;
- } else {
- std::pair<history_map::iterator, bool> result
- = history->prices.insert(history_pair(date, price));
- assert(result.second);
- }
-}
+ // Read in the commodity for this amount
-bool commodity_base_t::remove_price(const datetime_t& date)
-{
- if (history) {
- history_map::size_type n = history->prices.erase(date);
- if (n > 0) {
- if (history->prices.empty())
- history = NULL;
- return true;
- }
+ commodity_t::ident_t ident;
+ read_long(in, ident);
+ if (ident == 0xffffffff)
+ commodity_ = NULL;
+ else if (ident == 0)
+ commodity_ = current_pool->null_commodity;
+ else {
+ commodity_ = current_pool->find(ident);
+ assert(commodity_);
}
- return false;
-}
-commodity_base_t * commodity_base_t::create(const std::string& symbol)
-{
- commodity_base_t * commodity = new commodity_base_t(symbol);
-
- DEBUG_PRINT("amounts.commodities", "Creating base commodity " << symbol);
+ // Read in the quantity
- std::pair<base_commodities_map::iterator, bool> result
- = commodities.insert(base_commodities_pair(symbol, commodity));
- assert(result.second);
+ char byte;
+ in.read(&byte, sizeof(byte));
- return commodity;
-}
+ if (byte < 3) {
+ quantity = new bigint_t;
-bool commodity_t::needs_quotes(const std::string& symbol)
-{
- for (const char * p = symbol.c_str(); *p; p++)
- if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
- return true;
+ unsigned short len;
+ in.read((char *)&len, sizeof(len));
+ assert(len < 4096);
+ in.read(buf, len);
+ mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
+ 0, 0, buf);
- return false;
-}
+ char negative;
+ in.read(&negative, sizeof(negative));
+ if (negative)
+ mpz_neg(MPZ(quantity), MPZ(quantity));
-bool commodity_t::valid() const
-{
- if (symbol().empty() && this != null_commodity) {
- DEBUG_PRINT("ledger.validate",
- "commodity_t: symbol().empty() && this != null_commodity");
- return false;
- }
+ in.read((char *)&quantity->prec, sizeof(quantity->prec));
- if (annotated && ! base) {
- DEBUG_PRINT("ledger.validate", "commodity_t: annotated && ! base");
- return false;
+ bigint_t::flags_t tflags;
+ in.read((char *)&tflags, sizeof(tflags));
+ quantity->set_flags(tflags);
}
-
- if (precision() > 16) {
- DEBUG_PRINT("ledger.validate", "commodity_t: precision() > 16");
- return false;
+ else {
+ assert(false);
}
-
- return true;
}
-commodity_t * commodity_t::create(const std::string& symbol)
+void amount_t::read(const char *& data)
{
- std::auto_ptr<commodity_t> commodity(new commodity_t);
+ using namespace ledger::binary;
- commodity->base = commodity_base_t::create(symbol);
+ // Read in the commodity for this amount
- if (needs_quotes(symbol)) {
- commodity->qualified_symbol = "\"";
- commodity->qualified_symbol += symbol;
- commodity->qualified_symbol += "\"";
- } else {
- commodity->qualified_symbol = symbol;
+ commodity_t::ident_t ident;
+ read_long(data, ident);
+ if (ident == 0xffffffff)
+ commodity_ = NULL;
+ else if (ident == 0)
+ commodity_ = current_pool->null_commodity;
+ else {
+ commodity_ = current_pool->find(ident);
+ assert(commodity_);
}
- DEBUG_PRINT("amounts.commodities",
- "Creating commodity " << commodity->qualified_symbol);
-
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_pair(symbol, commodity.get()));
- if (! result.second)
- return NULL;
-
- // Start out the new commodity with the default commodity's flags
- // and precision, if one has been defined.
- if (default_commodity)
- commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
- COMMODITY_STYLE_NOMARKET);
-
- return commodity.release();
-}
-
-commodity_t * commodity_t::find_or_create(const std::string& symbol)
-{
- DEBUG_PRINT("amounts.commodities", "Find-or-create commodity " << symbol);
-
- commodity_t * commodity = find(symbol);
- if (commodity)
- return commodity;
- return create(symbol);
-}
-
-commodity_t * commodity_t::find(const std::string& symbol)
-{
- DEBUG_PRINT("amounts.commodities", "Find commodity " << symbol);
-
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
- return NULL;
-}
-
-amount_t commodity_base_t::value(const datetime_t& moment)
-{
- datetime_t age;
- amount_t price;
+ // Read in the quantity
- if (history) {
- assert(history->prices.size() > 0);
+ char byte = *data++;;
- if (! moment) {
- history_map::reverse_iterator r = history->prices.rbegin();
- age = (*r).first;
- price = (*r).second;
+ if (byte < 3) {
+ if (byte == 2) {
+ quantity = new((bigint_t *)bigints_next) bigint_t;
+ bigints_next += sizeof(bigint_t);
} else {
- history_map::iterator i = history->prices.lower_bound(moment);
- if (i == history->prices.end()) {
- history_map::reverse_iterator r = history->prices.rbegin();
- age = (*r).first;
- price = (*r).second;
- } else {
- age = (*i).first;
- if (moment != age) {
- if (i != history->prices.begin()) {
- --i;
- age = (*i).first;
- price = (*i).second;
- } else {
- age = 0;
- }
- } else {
- price = (*i).second;
- }
- }
+ quantity = new bigint_t;
}
- }
- if (updater && ! (flags & COMMODITY_STYLE_NOMARKET))
- (*updater)(*this, moment, age,
- (history && history->prices.size() > 0 ?
- (*history->prices.rbegin()).first : datetime_t()), price);
-
- return price;
-}
-
-bool annotated_commodity_t::operator==(const commodity_t& comm) const
-{
- // If the base commodities don't match, the game's up.
- if (base != comm.base)
- return false;
-
- if (price &&
- (! comm.annotated ||
- price != static_cast<const annotated_commodity_t&>(comm).price))
- return false;
-
- if (date &&
- (! comm.annotated ||
- date != static_cast<const annotated_commodity_t&>(comm).date))
- return false;
-
- if (! tag.empty() &&
- (! comm.annotated ||
- tag != static_cast<const annotated_commodity_t&>(comm).tag))
- return false;
-
- return true;
-}
-
-void
-annotated_commodity_t::write_annotations(std::ostream& out,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag)
-{
- if (price)
- out << " {" << price << '}';
-
- if (date)
- out << " [" << date << ']';
-
- if (! tag.empty())
- out << " (" << tag << ')';
-}
-
-commodity_t *
-annotated_commodity_t::create(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag,
- const std::string& mapping_key)
-{
- std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
-
- // Set the annotated bits
- commodity->price = price;
- commodity->date = date;
- commodity->tag = tag;
-
- commodity->ptr = &comm;
- assert(commodity->ptr);
- commodity->base = comm.base;
- assert(commodity->base);
-
- commodity->qualified_symbol = comm.symbol();
-
- DEBUG_PRINT("amounts.commodities", "Creating annotated commodity "
- << "symbol " << commodity->symbol()
- << " key " << mapping_key << std::endl
- << " price " << price << " "
- << " date " << date << " "
- << " tag " << tag);
-
- // Add the fully annotated name to the map, so that this symbol may
- // quickly be found again.
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_pair(mapping_key, commodity.get()));
- if (! result.second)
- return NULL;
-
- return commodity.release();
-}
-
-namespace {
- std::string make_qualified_name(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag)
- {
- if (price < 0)
- throw new amount_error("A commodity's price may not be negative");
-
- std::ostringstream name;
+ unsigned short len = *((unsigned short *) data);
+ data += sizeof(unsigned short);
+ mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short),
+ 0, 0, data);
+ data += len;
- comm.write(name);
- annotated_commodity_t::write_annotations(name, price, date, tag);
+ char negative = *data++;
+ if (negative)
+ mpz_neg(MPZ(quantity), MPZ(quantity));
- DEBUG_PRINT("amounts.commodities", "make_qualified_name for "
- << comm.qualified_symbol << std::endl
- << " price " << price << " "
- << " date " << date << " "
- << " tag " << tag);
+ quantity->prec = *((precision_t *) data);
+ data += sizeof(precision_t);
+ quantity->set_flags(*((flags_t *) data));
+ data += sizeof(flags_t);
- DEBUG_PRINT("amounts.commodities", "qualified_name is " << name.str());
+ if (byte == 2)
+ quantity->add_flags(BIGINT_BULK_ALLOC);
+ } else {
+ uint_fast32_t index = *((uint_fast32_t *) data);
+ data += sizeof(uint_fast32_t);
- return name.str();
+ quantity = (bigint_t *) (bigints + (index - 1) * sizeof(bigint_t));
+ DEBUG("amounts.refs",
+ quantity << " ref++, now " << (quantity->ref + 1));
+ quantity->ref++;
}
}
-commodity_t *
-annotated_commodity_t::find_or_create(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag)
+void amount_t::write(std::ostream& out, bool optimized) const
{
- std::string name = make_qualified_name(comm, price, date, tag);
+ using namespace ledger::binary;
- commodity_t * ann_comm = commodity_t::find(name);
- if (ann_comm) {
- assert(ann_comm->annotated);
- return ann_comm;
- }
- return create(comm, price, date, tag, name);
-}
+ // Write out the commodity for this amount
-bool compare_amount_commodities::operator()(const amount_t * left,
- const amount_t * right) const
-{
- commodity_t& leftcomm(left->commodity());
- commodity_t& rightcomm(right->commodity());
-
- int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol());
- if (cmp != 0)
- return cmp < 0;
+ if (! quantity)
+ throw_(amount_error, "Cannot serialize an uninitialized amount");
- if (! leftcomm.annotated) {
- assert(rightcomm.annotated);
- return true;
- }
- else if (! rightcomm.annotated) {
- assert(leftcomm.annotated);
- return false;
- }
- else {
- annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
- annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
+ if (commodity_)
+ write_long(out, commodity_->ident);
+ else
+ write_long<commodity_t::ident_t>(out, 0xffffffff);
- if (! aleftcomm.price && arightcomm.price)
- return true;
- if (aleftcomm.price && ! arightcomm.price)
- return false;
+ // Write out the quantity
- if (aleftcomm.price && arightcomm.price) {
- amount_t leftprice(aleftcomm.price);
- leftprice.reduce();
- amount_t rightprice(arightcomm.price);
- rightprice.reduce();
+ char byte;
- if (leftprice.commodity() == rightprice.commodity()) {
- int diff = leftprice.compare(rightprice);
- if (diff)
- return diff;
- } else {
- // Since we have two different amounts, there's really no way
- // to establish a true sorting order; we'll just do it based
- // on the numerical values.
- leftprice.clear_commodity();
- rightprice.clear_commodity();
-
- int diff = leftprice.compare(rightprice);
- if (diff)
- return diff;
- }
+ if (! optimized || quantity->index == 0) {
+ if (optimized) {
+ quantity->index = ++bigints_index; // if !optimized, this is garbage
+ bigints_count++;
+ byte = 2;
+ } else {
+ byte = 1;
}
+ out.write(&byte, sizeof(byte));
- if (! aleftcomm.date && arightcomm.date)
- return true;
- if (aleftcomm.date && ! arightcomm.date)
- return false;
-
- if (aleftcomm.date && arightcomm.date) {
- int diff = aleftcomm.date - arightcomm.date;
- if (diff)
- return diff < 0;
+ std::size_t size;
+ mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity));
+ unsigned short len = size * sizeof(short);
+ out.write((char *)&len, sizeof(len));
+ if (len) {
+ assert(len < 4096);
+ out.write(buf, len);
}
- if (aleftcomm.tag.empty() && ! arightcomm.tag.empty())
- return true;
- if (! aleftcomm.tag.empty() && arightcomm.tag.empty())
- return false;
+ byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
+ out.write(&byte, sizeof(byte));
- if (! aleftcomm.tag.empty() && ! arightcomm.tag.empty())
- return aleftcomm.tag < arightcomm.tag;
+ out.write((char *)&quantity->prec, sizeof(quantity->prec));
+ bigint_t::flags_t tflags = quantity->flags() & ~BIGINT_BULK_ALLOC;
+ assert(sizeof(tflags) == sizeof(bigint_t::flags_t));
+ out.write((char *)&tflags, sizeof(tflags));
+ } else {
+ assert(quantity->ref > 1);
- // The two annotated commodities don't differ enough to matter. This
- // should make this identical.
- assert(0);
- return true;
+ // Since this value has already been written, we simply write
+ // out a reference to which one it was.
+ byte = 3;
+ out.write(&byte, sizeof(byte));
+ out.write((char *)&quantity->index, sizeof(quantity->index));
}
}
-} // namespace ledger
-
-#ifdef USE_BOOST_PYTHON
-
-#include <boost/python.hpp>
-#include <Python.h>
-
-using namespace boost::python;
-using namespace ledger;
-
-int py_amount_quantity(amount_t& amount)
-{
- std::string quant = amount.quantity_string();
- return std::atol(quant.c_str());
-}
-void py_parse_1(amount_t& amount, const std::string& str,
- unsigned char flags) {
- amount.parse(str, flags);
-}
-void py_parse_2(amount_t& amount, const std::string& str) {
- amount.parse(str);
-}
-
-struct commodity_updater_wrap : public commodity_base_t::updater_t
+bool amount_t::valid() const
{
- PyObject * self;
- commodity_updater_wrap(PyObject * self_) : self(self_) {}
-
- virtual void operator()(commodity_base_t& commodity,
- const datetime_t& moment,
- const datetime_t& date,
- const datetime_t& last,
- amount_t& price) {
- call_method<void>(self, "__call__", commodity, moment, date, last, price);
+ if (quantity) {
+ if (quantity->ref == 0) {
+ DEBUG("ledger.validate", "amount_t: quantity->ref == 0");
+ return false;
+ }
}
-};
-
-commodity_t * py_find_commodity(const std::string& symbol)
-{
- return commodity_t::find(symbol);
-}
-
-#define EXC_TRANSLATOR(type) \
- void exc_translate_ ## type(const type& err) { \
- PyErr_SetString(PyExc_RuntimeError, err.what()); \
+ else if (commodity_) {
+ DEBUG("ledger.validate", "amount_t: commodity_ != NULL");
+ return false;
}
-
-EXC_TRANSLATOR(amount_error)
-
-void export_amount()
-{
- scope().attr("AMOUNT_PARSE_NO_MIGRATE") = AMOUNT_PARSE_NO_MIGRATE;
- scope().attr("AMOUNT_PARSE_NO_REDUCE") = AMOUNT_PARSE_NO_REDUCE;
-
- class_< amount_t > ("Amount")
- .def(init<amount_t>())
- .def(init<std::string>())
- .def(init<char *>())
- .def(init<bool>())
- .def(init<long>())
- .def(init<unsigned long>())
- .def(init<double>())
-
- .def(self += self)
- .def(self += long())
- .def(self + self)
- .def(self + long())
- .def(self -= self)
- .def(self -= long())
- .def(self - self)
- .def(self - long())
- .def(self *= self)
- .def(self *= long())
- .def(self * self)
- .def(self * long())
- .def(self /= self)
- .def(self /= long())
- .def(self / self)
- .def(self / long())
- .def(- self)
-
- .def(self < self)
- .def(self < long())
- .def(self <= self)
- .def(self <= long())
- .def(self > self)
- .def(self > long())
- .def(self >= self)
- .def(self >= long())
- .def(self == self)
- .def(self == long())
- .def(self != self)
- .def(self != long())
- .def(! self)
-
- .def(self_ns::int_(self))
- .def(self_ns::float_(self))
- .def(self_ns::str(self))
- .def(abs(self))
-
- .add_property("commodity",
- make_function(&amount_t::commodity,
- return_value_policy<reference_existing_object>()),
- make_function(&amount_t::set_commodity,
- with_custodian_and_ward<1, 2>()))
-
- .def("strip_annotations", &amount_t::strip_annotations)
-
- .def("negate", &amount_t::negate)
- .def("negated", &amount_t::negated)
- .def("parse", py_parse_1)
- .def("parse", py_parse_2)
- .def("reduce", &amount_t::reduce)
-
- .def("valid", &amount_t::valid)
- ;
-
- class_< commodity_base_t::updater_t, commodity_updater_wrap,
- boost::noncopyable >
- ("Updater")
- ;
-
- scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS;
- scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
- scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
- scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN;
- scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS;
- scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
- scope().attr("COMMODITY_STYLE_BUILTIN") = COMMODITY_STYLE_BUILTIN;
-
- class_< commodity_t > ("Commodity")
- .add_property("symbol", &commodity_t::symbol)
-
-#if 0
- .add_property("name", &commodity_t::name, &commodity_t::set_name)
- .add_property("note", &commodity_t::note, &commodity_t::set_note)
- .add_property("precision", &commodity_t::precision,
- &commodity_t::set_precision)
- .add_property("flags", &commodity_t::flags, &commodity_t::set_flags)
- .add_property("add_flags", &commodity_t::add_flags)
- .add_property("drop_flags", &commodity_t::drop_flags)
-#if 0
- .add_property("updater", &commodity_t::updater)
-#endif
-
- .add_property("smaller",
- make_getter(&commodity_t::smaller,
- return_value_policy<reference_existing_object>()),
- make_setter(&commodity_t::smaller,
- return_value_policy<reference_existing_object>()))
- .add_property("larger",
- make_getter(&commodity_t::larger,
- return_value_policy<reference_existing_object>()),
- make_setter(&commodity_t::larger,
- return_value_policy<reference_existing_object>()))
-
- .def(self_ns::str(self))
-
- .def("find", py_find_commodity,
- return_value_policy<reference_existing_object>())
- .staticmethod("find")
-#endif
-
- .def("add_price", &commodity_t::add_price)
- .def("remove_price", &commodity_t::remove_price)
- .def("value", &commodity_t::value)
-
- .def("valid", &commodity_t::valid)
- ;
-
-#define EXC_TRANSLATE(type) \
- register_exception_translator<type>(&exc_translate_ ## type);
-
- EXC_TRANSLATE(amount_error);
+ return true;
}
-#endif // USE_BOOST_PYTHON
+} // namespace ledger
diff --git a/amount.h b/amount.h
index dfe8d2df..cd011675 100644
--- a/amount.h
+++ b/amount.h
@@ -1,628 +1,712 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file amount.h
+ * @author John Wiegley
+ * @date Wed Apr 18 22:05:53 2007
+ *
+ * @brief Basic type for handling commoditized math: amount_t.
+ *
+ * This file contains the most basic numerical type in Ledger:
+ * amount_t, which relies upon commodity.h (commodity_t) for handling
+ * commoditized amounts. This class allows Ledger to handle
+ * mathematical expressions involving differing commodities, as well
+ * as math using no commodities at all (such as increasing a dollar
+ * amount by a multiplier).
+ */
#ifndef _AMOUNT_H
#define _AMOUNT_H
-#include <map>
-#include <stack>
-#include <string>
-#include <cctype>
-#include <iostream>
-#include <sstream>
-#include <cassert>
-#include <exception>
-
-#include "datetime.h"
-#include "debug.h"
-#include "error.h"
+#include "utils.h"
namespace ledger {
-extern bool do_cleanup;
-
class commodity_t;
-
+class annotation_t;
+class commodity_pool_t;
+
+DECLARE_EXCEPTION(error, amount_error);
+
+/**
+ * @class amount_t
+ *
+ * @brief Encapsulates infinite-precision commoditized amounts.
+ *
+ * The amount_t class can be used for commoditized infinite-precision
+ * math, and also for uncommoditized math. In the commoditized case,
+ * commodities keep track of how they are used, and will always
+ * display back to the user after the same fashion. For
+ * uncommoditized numbers, no display truncation is ever done. In
+ * both cases, internal precision is always kept to an excessive
+ * degree.
+ */
class amount_t
+ : public ordered_field_operators<amount_t,
+ ordered_field_operators<amount_t, double,
+ ordered_field_operators<amount_t, unsigned long,
+ ordered_field_operators<amount_t, long> > > >
{
- public:
- class bigint_t;
+ // jww (2007-05-03): Make this private, and then make
+ // ledger::initialize into a member function of session_t.
+public:
+ /**
+ * The initialize and shutdown methods ready the amount subsystem
+ * for use. Normally they are called by `ledger::initialize' and
+ * `ledger::shutdown'.
+ */
+ static void initialize();
+ static void shutdown();
+
+public:
+ typedef uint_least16_t precision_t;
+
+ /**
+ * The current_pool is a static variable indicating which commodity
+ * pool should be used.
+ */
+ static commodity_pool_t * current_pool;
+
+ /**
+ * The `keep_base' member determines whether scalable commodities
+ * are automatically converted to their most reduced form when
+ * printing. The default is true.
+ *
+ * For example, Ledger supports time values specified in seconds
+ * (10s), hours (5.2h) or minutes. Internally, such amounts are
+ * always kept as quantities of seconds. However, when streaming
+ * the amount Ledger will convert it to its "least representation",
+ * which is "5.2h" in the second case. If `keep_base' is true, this
+ * amount is displayed as "18720s".
+ */
+ static bool keep_base;
+ /**
+ * The following three members determine whether lot details are
+ * maintained when working with commoditized values. The default is
+ * false for all three.
+ *
+ * Let's say a user adds two values of the following form:
+ * 10 AAPL + 10 AAPL {$20}
+ *
+ * This expression adds ten shares of Apple stock with another ten
+ * shares that were purchased for $20 a share. If `keep_price' is
+ * false, the result of this expression will be an amount equal to
+ * 20 AAPL. If `keep_price' is true, the expression yields an
+ * exception for adding amounts with different commodities. In that
+ * case, a balance_t object must be used to store the combined sum.
+ */
static bool keep_price;
static bool keep_date;
static bool keep_tag;
- static bool keep_base;
- protected:
- void _init();
+ /**
+ * The `stream_fullstrings' static member is currently only used by
+ * the unit testing code. It causes amounts written to streams to
+ * use the `to_fullstring' method rather than the `to_string'
+ * method, so that complete precision is always displayed, no matter
+ * what the precision of an individual commodity might be.
+ * @see to_string
+ * @see to_fullstring
+ */
+ static bool stream_fullstrings;
+
+protected:
void _copy(const amount_t& amt);
- void _release();
void _dup();
- void _resize(unsigned int prec);
+ void _resize(precision_t prec);
+ void _clear();
+ void _release();
- void _clear() {
- if (quantity) {
- assert(commodity_);
- _release();
- quantity = NULL;
- commodity_ = NULL;
- } else {
- assert(! commodity_);
- }
- }
+ struct bigint_t;
bigint_t * quantity;
commodity_t * commodity_;
- public:
- // constructors
- amount_t() : quantity(NULL), commodity_(NULL) {}
+public:
+ /**
+ * Constructors. amount_t supports several forms of construction:
+ *
+ * amount_t() creates a value for which `is_null' is true, and which
+ * has no value or commodity. If used in value situations it will
+ * be zero, and its commodity equals `commodity_t::null_commodity'.
+ *
+ * amount_t(double), amount_t(unsigned long), amount_t(long) all
+ * convert from the respective numerics type to an amount. No
+ * precision or sign is lost in any of these conversions. The
+ * resulting commodity is always `commodity_t::null_commodity'.
+ *
+ * amount_t(string), amount_t(const char *) both convert from a
+ * string representation of an amount, which may or may not include
+ * a commodity. This is the proper way to initialize an amount like
+ * '$100.00'.
+ */
+ amount_t() : quantity(NULL), commodity_(NULL) {
+ TRACE_CTOR(amount_t, "");
+ }
+ amount_t(const double val);
+ amount_t(const unsigned long val);
+ amount_t(const long val);
+
+ explicit amount_t(const string& val) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "const string&");
+ parse(val);
+ }
+ explicit amount_t(const char * val) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "const char *");
+ parse(val);
+ }
+
+ /**
+ * Static creator function. Calling amount_t::exact(string) will
+ * create an amount whose display precision is never truncated, even
+ * if the amount uses a commodity (which normally causes "round on
+ * streaming" to occur). This function is mostly used by the
+ * debugging code. It is the proper way to initialize '$100.005',
+ * where display of the extra precision is required. If a regular
+ * constructor is used, this amount will stream as '$100.01', even
+ * though its internal value always equals $100.005.
+ */
+ static amount_t exact(const string& value);
+
+ /**
+ * Destructor. Releases the reference count held for the underlying
+ * bigint_t object pointed to be `quantity'.
+ */
+ ~amount_t() {
+ TRACE_DTOR(amount_t);
+ if (quantity)
+ _release();
+ }
+
+ /**
+ * Assignment and copy operators. An amount may be assigned or
+ * copied. If a double, long or unsigned long is assigned to an
+ * amount, a temporary is constructed, and then the temporary is
+ * assigned to `this'. Both the value and the commodity are copied,
+ * causing the result to compare equal to the reference amount.
+ *
+ * Note: `quantity' must be initialized to NULL first, otherwise the
+ * `_copy' function will attempt to release the uninitialized pointer.
+ */
amount_t(const amount_t& amt) : quantity(NULL) {
+ TRACE_CTOR(amount_t, "copy");
if (amt.quantity)
_copy(amt);
else
commodity_ = NULL;
}
- amount_t(const std::string& value) : quantity(NULL) {
- parse(value);
- }
- amount_t(const char * value) : quantity(NULL) {
- parse(value);
- }
- amount_t(const bool value);
- amount_t(const long value);
- amount_t(const unsigned long value);
- amount_t(const double value);
+ amount_t& operator=(const amount_t& amt);
- // destructor
- ~amount_t() {
- if (quantity)
- _release();
- }
+ amount_t& operator=(const string& str) {
+ return *this = amount_t(str);
+ }
+ amount_t& operator=(const char * str) {
+ return *this = amount_t(str);
+ }
+
+ /**
+ * Comparison operators. The fundamental comparison operation for
+ * amounts is `compare', which returns a value less than, greater
+ * than or equal to zero. All the other comparison operators are
+ * defined in terms of this method. The only special detail is that
+ * `operator==' will fail immediately if amounts with different
+ * commodities are being compared. Otherwise, if the commodities
+ * are equivalent (@see keep_price, et al), then the amount
+ * quantities are compared numerically.
+ *
+ * Comparison between an amount and a double, long or unsigned long
+ * is allowed. In such cases the non-amount value is constructed as
+ * an amount temporary, which is then compared to `this'.
+ */
+ int compare(const amount_t& amt) const;
- commodity_t& commodity() const;
- void set_commodity(commodity_t& comm) {
- commodity_ = &comm;
+ bool operator==(const amount_t& amt) const;
+
+ template <typename T>
+ bool operator==(const T& val) const {
+ return compare(val) == 0;
}
- void annotate_commodity(const amount_t& price,
- const datetime_t& date = datetime_t(),
- const std::string& tag = "");
- amount_t strip_annotations(const bool _keep_price = keep_price,
- const bool _keep_date = keep_date,
- const bool _keep_tag = keep_tag) const;
- void clear_commodity() {
- commodity_ = NULL;
+ template <typename T>
+ bool operator<(const T& amt) const {
+ return compare(amt) < 0;
}
- amount_t price() const;
- datetime_t date() const;
-
- bool null() const {
- return ! quantity && ! commodity_;
+ template <typename T>
+ bool operator>(const T& amt) const {
+ return compare(amt) > 0;
}
- std::string quantity_string() const;
-
- // assignment operator
- amount_t& operator=(const amount_t& amt);
- amount_t& operator=(const std::string& value);
- amount_t& operator=(const char * value);
- amount_t& operator=(const bool value);
- amount_t& operator=(const long value);
- amount_t& operator=(const unsigned long value);
- amount_t& operator=(const double value);
-
- // general methods
- amount_t round(unsigned int prec) const;
- amount_t round() const;
- amount_t unround() const;
-
- // in-place arithmetic
+ /**
+ * Binary arithmetic operators. Amounts support addition,
+ * subtraction, multiplication and division -- but not modulus,
+ * bitwise operations, or shifting. Arithmetic is also supported
+ * between amounts, double, long and unsigned long, in which case
+ * temporary amount are constructed for the life of the expression.
+ *
+ * Although only in-place operators are defined here, the remainder
+ * are provided by `boost::ordered_field_operators<>'.
+ */
amount_t& operator+=(const amount_t& amt);
amount_t& operator-=(const amount_t& amt);
amount_t& operator*=(const amount_t& amt);
amount_t& operator/=(const amount_t& amt);
- template <typename T>
- amount_t& operator+=(T value) {
- return *this += amount_t(value);
- }
- template <typename T>
- amount_t& operator-=(T value) {
- return *this -= amount_t(value);
- }
- template <typename T>
- amount_t& operator*=(T value) {
- return *this *= amount_t(value);
- }
- template <typename T>
- amount_t& operator/=(T value) {
- return *this /= amount_t(value);
- }
-
- // simple arithmetic
- amount_t operator+(const amount_t& amt) const {
- amount_t temp = *this;
- temp += amt;
- return temp;
- }
- amount_t operator-(const amount_t& amt) const {
- amount_t temp = *this;
- temp -= amt;
- return temp;
- }
- amount_t operator*(const amount_t& amt) const {
- amount_t temp = *this;
- temp *= amt;
- return temp;
- }
- amount_t operator/(const amount_t& amt) const {
- amount_t temp = *this;
- temp /= amt;
+ /**
+ * Unary arithmetic operators. There are several unary methods
+ * support on amounts:
+ *
+ * precision() return an amount's current, internal precision. To
+ * find the precision it will be displayed at -- assuming it was not
+ * created using the static method `amount_t::exact' -- refer to
+ * commodity().precision.
+ *
+ * negate(), also unary minus (- x), returns the negated value of an
+ * amount.
+ *
+ * abs() returns the absolute value of an amount. It is equivalent
+ * to: `(x < 0) ? - x : x'.
+ *
+ * round(precision_t) and round() round an amount's internal value
+ * to the given precision, or to the commodity's current display
+ * precision if no precision value is given. This method changes
+ * the internal value of the amount, if it's internal precision was
+ * greater than the rounding precision.
+ *
+ * unround() yields an amount whose display precision is never
+ * truncated, even though its commodity normally displays only
+ * rounded values.
+ *
+ * reduce() reduces a value to its most basic commodity form, for
+ * amounts that utilize "scaling commodities". For example, an
+ * amount of 1h after reduction will be 3600s.
+ *
+ * unreduce(), if used with a "scaling commodity", yields the most
+ * compact form greater than 1.0. That is, 3599s will unreduce to
+ * 59.98m, while 3601 unreduces to 1h.
+ *
+ * value(optional<moment_t>) returns the historical value for an
+ * amount -- the default moment returns the most recently known
+ * price -- based on the price history of its commodity. For
+ * example, if the amount were 10 AAPL, and on Apr 10, 2000 each
+ * share of AAPL was worth $10, then call value() for that moment in
+ * time would yield the amount $100.00.
+ *
+ * Further, for the sake of efficiency and avoiding temporary
+ * objects, the following methods support "in-place" variants that
+ * act on the amount itself and return a reference to the result
+ * (`*this'):
+ *
+ * in_place_negate()
+ * in_place_reduce()
+ * in_place_unreduce()
+ */
+ precision_t precision() const;
+
+ amount_t negate() const {
+ amount_t temp(*this);
+ temp.in_place_negate();
return temp;
}
+ amount_t& in_place_negate();
- template <typename T>
- amount_t operator+(T value) const {
- amount_t temp = *this;
- temp += value;
- return temp;
- }
- template <typename T>
- amount_t operator-(T value) const {
- amount_t temp = *this;
- temp -= value;
- return temp;
+ amount_t operator-() const {
+ return negate();
}
- template <typename T>
- amount_t operator*(T value) const {
- amount_t temp = *this;
- temp *= value;
- return temp;
+
+ amount_t abs() const {
+ if (sign() < 0)
+ return negate();
+ return *this;
}
- template <typename T>
- amount_t operator/(T value) const {
- amount_t temp = *this;
- temp /= value;
+
+ amount_t round() const;
+ amount_t round(precision_t prec) const;
+ amount_t unround() const;
+
+ amount_t reduce() const {
+ amount_t temp(*this);
+ temp.in_place_reduce();
return temp;
}
+ amount_t& in_place_reduce();
- // unary negation
- void negate();
- amount_t negated() const {
- amount_t temp = *this;
- temp.negate();
+ amount_t unreduce() const {
+ amount_t temp(*this);
+ temp.in_place_unreduce();
return temp;
}
- amount_t operator-() const {
- return negated();
- }
-
- // test for non-zero (use ! for zero)
- operator bool() const;
- operator long() const;
- operator double() const;
-
- bool realzero() const;
-
- // comparisons between amounts
- int compare(const amount_t& amt) const;
+ amount_t& in_place_unreduce();
+
+ optional<amount_t> value(const optional<moment_t>& moment = none) const;
+
+ /**
+ * Truth tests. An amount may be truth test in several ways:
+ *
+ * sign() returns an integer less than, greater than, or equal to
+ * zero depending on whether the amount is negative, zero, or
+ * greater than zero. Note that this function tests the actual
+ * value of the amount -- using its internal precision -- and not
+ * the display value. To test its display value, use:
+ * `round().sign()'.
+ *
+ * is_nonzero(), or operator bool, returns true if an amount's
+ * display value is not zero.
+ *
+ * is_zero() returns true if an amount's display value is zero.
+ * Thus, $0.0001 is considered zero if the current display precision
+ * for dollars is two decimal places.
+ *
+ * is_realzero() returns true if an amount's actual value is zero.
+ * Thus, $0.0001 is never considered realzero.
+ *
+ * is_null() returns true if an amount has no value and no
+ * commodity. This only occurs if an uninitialized amount has never
+ * been assigned a value.
+ */
+ int sign() const;
- bool operator<(const amount_t& amt) const {
- return compare(amt) < 0;
- }
- bool operator<=(const amount_t& amt) const {
- return compare(amt) <= 0;
- }
- bool operator>(const amount_t& amt) const {
- return compare(amt) > 0;
+ operator bool() const {
+ return is_nonzero();
}
- bool operator>=(const amount_t& amt) const {
- return compare(amt) >= 0;
+ bool is_nonzero() const {
+ return ! is_zero();
}
- bool operator==(const amount_t& amt) const;
- bool operator!=(const amount_t& amt) const;
- template <typename T>
- void parse_num(T num) {
- std::ostringstream temp;
- temp << num;
- std::istringstream in(temp.str());
- parse(in);
+ bool is_zero() const;
+ bool is_realzero() const {
+ return sign() == 0;
}
- int sign() const;
+ bool is_null() const {
+ if (! quantity) {
+ assert(! commodity_);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Conversion methods. An amount may be converted to the same types
+ * it can be constructed from -- with the exception of unsigned
+ * long. Implicit conversions are not allowed in C++ (though they
+ * are in Python), rather the following conversion methods must be
+ * called explicitly:
+ *
+ * to_double([bool]) returns an amount as a double. If the optional
+ * boolean argument is true (the default), an exception is thrown if
+ * the conversion would lose information.
+ *
+ * to_long([bool]) returns an amount as a long integer. If the
+ * optional boolean argument is true (the default), an exception is
+ * thrown if the conversion would lose information.
+ *
+ * fits_in_double() returns true if to_double() would not lose
+ * precision.
+ *
+ * fits_in_long() returns true if to_long() would not lose
+ * precision.
+ *
+ * to_string() returns an amount'ss "display value" as a string --
+ * after rounding the value according to the commodity's default
+ * precision. It is equivalent to: `round().to_fullstring()'.
+ *
+ * to_fullstring() returns an amount's "internal value" as a string,
+ * without any rounding.
+ *
+ * quantity_string() returns an amount's "display value", but
+ * without any commodity. Note that this is different from
+ * `number().to_string()', because in that case the commodity has
+ * been stripped and the full, internal precision of the amount
+ * would be displayed.
+ */
+ double to_double(bool no_check = false) const;
+ long to_long(bool no_check = false) const;
+ string to_string() const;
+ string to_fullstring() const;
+ string quantity_string() const;
+
+ bool fits_in_double() const;
+ bool fits_in_long() const;
+
+ /**
+ * Commodity-related methods. The following methods relate to an
+ * amount's commodity:
+ *
+ * commodity() returns an amount's commodity. If the amount has no
+ * commodity, the value returned is `current_pool->null_commodity'.
+ *
+ * has_commodity() returns true if the amount has a commodity.
+ *
+ * set_commodity(commodity_t) sets an amount's commodity to the
+ * given value. Note that this merely sets the current amount to
+ * that commodity, it does not "observe" the amount for possible
+ * changes in the maximum display precision of the commodity, the
+ * way that `parse' does.
+ *
+ * clear_commodity() sets an amount's commodity to null, such that
+ * has_commodity() afterwards returns false.
+ *
+ * number() returns a commodity-less version of an amount. This is
+ * useful for accessing just the numeric portion of an amount.
+ */
+ commodity_t& commodity() const;
- // POD comparisons
-#define AMOUNT_CMP_INT(OP) \
- template <typename T> \
- bool operator OP (T num) const { \
- if (num == 0) { \
- return sign() OP 0; \
- } else { \
- amount_t amt; \
- amt.parse_num(num); \
- return *this OP amt; \
- } \
+ bool has_commodity() const;
+ void set_commodity(commodity_t& comm) {
+ if (! quantity)
+ *this = 0L;
+ commodity_ = &comm;
}
-
- AMOUNT_CMP_INT(<)
- AMOUNT_CMP_INT(<=)
- AMOUNT_CMP_INT(>)
- AMOUNT_CMP_INT(>=)
- AMOUNT_CMP_INT(==)
-
- template <typename T>
- bool operator!=(T num) const {
- return ! (*this == num);
+ void clear_commodity() {
+ commodity_ = NULL;
}
- amount_t value(const datetime_t& moment) const;
+ amount_t number() const {
+ if (! has_commodity())
+ return *this;
- void abs() {
- if (*this < 0)
- negate();
+ amount_t temp(*this);
+ temp.clear_commodity();
+ return temp;
}
+ /**
+ * Annotated commodity methods. An amount's commodity may be
+ * annotated with special details, such as the price it was
+ * purchased for, when it was acquired, or an arbitrary note,
+ * identifying perhaps the lot number of an item.
+ *
+ * annotate_commodity(amount_t price, [moment_t date, string tag])
+ * sets the annotations for the current amount's commodity. Only
+ * the price argument is required, although it can be passed as
+ * `none' if no price is desired.
+ *
+ * commodity_annotated() returns true if an amount's commodity has
+ * any annotation details associated with it.
+ *
+ * annotation_details() returns all of the details of an annotated
+ * commodity's annotations. The structure returns will evaluate as
+ * boolean false if there are no details.
+ *
+ * strip_annotations([keep_price, keep_date, keep_tag]) returns an
+ * amount whose commodity's annotations have been stripped. The
+ * three `keep_' arguments determine which annotation detailed are
+ * kept, meaning that the default is to follow whatever
+ * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
+ * have been set to (which all default to false).
+ */
+ void annotate_commodity(const annotation_t& details);
+ bool commodity_annotated() const;
+ annotation_t annotation_details() const;
+ amount_t strip_annotations(const bool _keep_price = keep_price,
+ const bool _keep_date = keep_date,
+ const bool _keep_tag = keep_tag) const;
+
+ /**
+ * Parsing methods. The method `parse' is used to parse an amount
+ * from an input stream or a string. A global operator>> is also
+ * defined which simply calls parse on the input stream. The
+ * `parse' method has two forms:
+ *
+ * parse(istream, flags_t) parses an amount from the given input
+ * stream.
+ *
+ * parse(string, flags_t) parses an amount from the given string.
+ *
+ * parse(string, flags_t) also parses an amount from a string.
+ *
+ * The `flags' argument of both parsing may be one or more of the
+ * following:
+ *
+ * AMOUNT_PARSE_NO_MIGRATE means to not pay attention to the way an
+ * amount is used. Ordinarily, if an amount were $100.001, for
+ * example, it would cause the default display precision for $ to be
+ * "widened" to three decimal places. If AMOUNT_PARSE_NO_MIGRATE is
+ * used, the commodity's default display precision is not changed.
+ *
+ * AMOUNT_PARSE_NO_REDUCE means not to call in_place_reduce() on the
+ * resulting amount after it is parsed.
+ *
+ * These parsing methods observe the amounts they parse (unless
+ * AMOUNT_PARSE_NO_MIGRATE is true), and set the display details of
+ * the corresponding commodity accordingly. This way, amounts do
+ * not require commodities to be pre-defined in any way, but merely
+ * displays them back to the user in the same fashion as it saw them
+ * used.
+ *
+ * There is also a static convenience method called
+ * `parse_conversion' which can be used to define a relationship
+ * between scaling commodity values. For example, Ledger uses it to
+ * define the relationships among various time values:
+ *
+ * amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
+ * amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
+ */
#define AMOUNT_PARSE_NO_MIGRATE 0x01
#define AMOUNT_PARSE_NO_REDUCE 0x02
- void parse(std::istream& in, unsigned char flags = 0);
- void parse(const std::string& str, unsigned char flags = 0);
- void reduce();
-
- amount_t reduced() const {
- amount_t temp(*this);
- temp.reduce();
- return temp;
+ typedef uint_least8_t flags_t;
+
+ void parse(std::istream& in, flags_t flags = 0);
+ void parse(const string& str, flags_t flags = 0) {
+ std::istringstream stream(str);
+ parse(stream, flags);
+ assert(stream.eof());
+ }
+
+ static void parse_conversion(const string& larger_str,
+ const string& smaller_str);
+
+ /**
+ * Printing methods. An amount may be output to a stream using the
+ * `print' method. There is also a global operator<< defined which
+ * simply calls print for an amount on the given stream. There is
+ * one form of the print method, which takes one required argument
+ * and two arguments with default values:
+ *
+ * print(ostream, bool omit_commodity = false, bool full_precision =
+ * false) prints an amounts to the given output stream, using its
+ * commodity's default display characteristics. If `omit_commodity'
+ * is true, the commodity will not be displayed, only the amount
+ * (although the commodity's display precision is still used). If
+ * `full_precision' is true, the full internal precision of the
+ * amount is displayed, regardless of its commodity's display
+ * precision.
+ */
+ void print(std::ostream& out, bool omit_commodity = false,
+ bool full_precision = false) const;
+
+ /**
+ * Serialization methods. An amount may be deserialized from an
+ * input stream or a character pointer, and it may be serialized to
+ * an output stream. The methods used are:
+ *
+ * read(istream) reads an amount from the given input stream. It
+ * must have been put there using `write(ostream)'. The required
+ * flow of logic is:
+ * amount_t::current_pool->write(out)
+ * amount.write(out) // write out all amounts
+ * amount_t::current_pool->read(in)
+ * amount.read(in)
+ *
+ * read(char *&) reads an amount from data which has been read from
+ * an input stream into a buffer. It advances the pointer passed in
+ * to the end of the deserialized amount.
+ *
+ * write(ostream, [bool]) writes an amount to an output stream in a
+ * compact binary format. If the second parameter is true,
+ * quantities with multiple reference counts will be written in an
+ * optimized fashion. NOTE: This form of usage is valid only for
+ * the binary journal writer, it should not be used otherwise, as it
+ * has strict requirements for reading that only the binary reader
+ * knows about.
+ */
+ void read(std::istream& in);
+ void read(const char *& data);
+ void write(std::ostream& out, bool optimize = false) const;
+
+ /**
+ * Debugging methods. There are two methods defined to help with
+ * debugging:
+ *
+ * dump(ostream) dumps an amount to an output stream. There is
+ * little different from print(), it simply surrounds the display
+ * value with a marker, for example "AMOUNT($1.00)". This code is
+ * used by other dumping code elsewhere in Ledger.
+ *
+ * valid() returns true if an amount is valid. This ensures that if
+ * an amount has a commodity, it has a valid value pointer, for
+ * example, even if that pointer simply points to a zero value.
+ */
+ void dump(std::ostream& out) const {
+ out << "AMOUNT(";
+ print(out);
+ out << ")";
}
- void read_quantity(char *& data);
- void read_quantity(std::istream& in);
- void write_quantity(std::ostream& out) const;
-
bool valid() const;
-
- // Classes that are friends, and help to implement this class
-
- friend std::ostream& operator<<(std::ostream& out, const amount_t& amt);
- friend std::istream& operator>>(std::istream& in, amount_t& amt);
-
- friend unsigned int sizeof_bigint_t();
-
- friend void read_binary_amount(char *& data, amount_t& amt);
- friend void write_binary_amount(std::ostream& out, const amount_t& amt);
-
- // This function is special, and exists only to support a custom
- // optimization in binary.cc (which offers a significant enough gain
- // to be worth the trouble).
-
- friend void clean_commodity_history(char * item_pool,
- char * item_pool_end);
-
- friend void parse_annotations(std::istream& in, amount_t& price,
- datetime_t& date, std::string& tag);
};
-unsigned int sizeof_bigint_t();
-
-void parse_quantity(std::istream& in, std::string& value);
-void parse_commodity(std::istream& in, std::string& symbol);
-void parse_annotations(std::istream& in, const std::string& symbol,
- std::string& name, std::string& price,
- std::string& date, std::string& tag);
-void parse_conversion(const std::string& larger,
- const std::string& smaller);
-
-inline bool is_quote_or_paren(char * p) {
- return *p == '"' || *p == '{' || *p == '[' || *p == '(';
+inline amount_t amount_t::exact(const string& value) {
+ amount_t temp;
+ temp.parse(value, AMOUNT_PARSE_NO_MIGRATE);
+ return temp;
}
-inline char * scan_past_quotes_and_parens(char * expr)
-{
- std::stack<char> paren_stack;
-
- char * p;
- for (p = expr; *p; p++) {
- if (*p == '"' ||
- ((*p == '(' || ((*p == '{' || *p == '[') &&
- paren_stack.top() != '(')) &&
- paren_stack.top() != '"')) {
- paren_stack.push(*p);
- }
- else if ((*p == ')' && paren_stack.top() == '(') ||
- (*p == '}' && paren_stack.top() == '{') ||
- (*p == ']' && paren_stack.top() == '[') ||
- (*p == '"' && paren_stack.top() == '"')) {
- paren_stack.pop();
- if (paren_stack.size() == 0)
- break;
- }
- }
- return p;
+inline string amount_t::to_string() const {
+ std::ostringstream bufstream;
+ print(bufstream);
+ return bufstream.str();
}
-inline amount_t abs(const amount_t& amt) {
- return amt < 0 ? amt.negated() : amt;
+inline string amount_t::to_fullstring() const {
+ std::ostringstream bufstream;
+ print(bufstream, false, true);
+ return bufstream.str();
}
-std::ostream& operator<<(std::ostream& out, const amount_t& amt);
+inline string amount_t::quantity_string() const {
+ std::ostringstream bufstream;
+ print(bufstream, true);
+ return bufstream.str();
+}
+inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) {
+ amt.print(out, false, amount_t::stream_fullstrings);
+ return out;
+}
inline std::istream& operator>>(std::istream& in, amount_t& amt) {
amt.parse(in);
return in;
}
+} // namespace ledger
-#define COMMODITY_STYLE_DEFAULTS 0x0000
-#define COMMODITY_STYLE_SUFFIXED 0x0001
-#define COMMODITY_STYLE_SEPARATED 0x0002
-#define COMMODITY_STYLE_EUROPEAN 0x0004
-#define COMMODITY_STYLE_THOUSANDS 0x0008
-#define COMMODITY_STYLE_NOMARKET 0x0010
-#define COMMODITY_STYLE_BUILTIN 0x0020
-
-typedef std::map<const datetime_t, amount_t> history_map;
-typedef std::pair<const datetime_t, amount_t> history_pair;
-
-class commodity_base_t;
-
-typedef std::map<const std::string, commodity_base_t *> base_commodities_map;
-typedef std::pair<const std::string, commodity_base_t *> base_commodities_pair;
-
-class commodity_base_t
-{
- public:
- friend class commodity_t;
- friend class annotated_commodity_t;
-
- typedef unsigned long ident_t;
-
- ident_t ident;
- std::string name;
- std::string note;
- unsigned char precision;
- unsigned char flags;
- amount_t * smaller;
- amount_t * larger;
-
- commodity_base_t()
- : precision(0), flags(COMMODITY_STYLE_DEFAULTS),
- smaller(NULL), larger(NULL), history(NULL) {}
-
- commodity_base_t(const std::string& _symbol,
- unsigned int _precision = 0,
- unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
- : precision(_precision), flags(_flags),
- smaller(NULL), larger(NULL), symbol(_symbol), history(NULL) {}
-
- ~commodity_base_t() {
- if (history) delete history;
- if (smaller) delete smaller;
- if (larger) delete larger;
- }
-
- static base_commodities_map commodities;
- static commodity_base_t * create(const std::string& symbol);
-
- std::string symbol;
-
- struct history_t {
- history_map prices;
- datetime_t last_lookup;
- datetime_t bogus_time;
- history_t() : last_lookup(0), bogus_time(0) {}
- };
- history_t * history;
-
- void add_price(const datetime_t& date, const amount_t& price);
- bool remove_price(const datetime_t& date);
- amount_t value(const datetime_t& moment = datetime_t::now);
-
- class updater_t {
- public:
- virtual ~updater_t() {}
- virtual void operator()(commodity_base_t& commodity,
- const datetime_t& moment,
- const datetime_t& date,
- const datetime_t& last,
- amount_t& price) = 0;
- };
- friend class updater_t;
-
- static updater_t * updater;
-};
-
-typedef std::map<const std::string, commodity_t *> commodities_map;
-typedef std::pair<const std::string, commodity_t *> commodities_pair;
-
-class commodity_t
-{
- friend class annotated_commodity_t;
-
- public:
- // This map remembers all commodities that have been defined.
-
- static commodities_map commodities;
- static bool commodities_sorted;
- static commodity_t * null_commodity;
- static commodity_t * default_commodity;
-
- static commodity_t * create(const std::string& symbol);
- static commodity_t * find(const std::string& name);
- static commodity_t * find_or_create(const std::string& symbol);
-
- static bool needs_quotes(const std::string& symbol);
-
- static void make_alias(const std::string& symbol,
- commodity_t * commodity);
-
- // These are specific to each commodity reference
-
- typedef unsigned long ident_t;
-
- ident_t ident;
- commodity_base_t * base;
- std::string qualified_symbol;
- bool annotated;
-
- public:
- explicit commodity_t() : base(NULL), annotated(false) {}
- virtual ~commodity_t() {}
-
- operator bool() const {
- return this != null_commodity;
- }
- virtual bool operator==(const commodity_t& comm) const {
- if (comm.annotated)
- return comm == *this;
- return base == comm.base;
- }
- bool operator!=(const commodity_t& comm) const {
- return ! (*this == comm);
- }
-
- std::string base_symbol() const {
- return base->symbol;
- }
- std::string symbol() const {
- return qualified_symbol;
- }
-
- void write(std::ostream& out) const {
- out << symbol();
- }
-
- std::string name() const {
- return base->name;
- }
- void set_name(const std::string& arg) {
- base->name = arg;
- }
-
- std::string note() const {
- return base->note;
- }
- void set_note(const std::string& arg) {
- base->note = arg;
- }
-
- unsigned char precision() const {
- return base->precision;
- }
- void set_precision(unsigned char arg) {
- base->precision = arg;
- }
-
- unsigned char flags() const {
- return base->flags;
- }
- void set_flags(unsigned char arg) {
- base->flags = arg;
- }
- void add_flags(unsigned char arg) {
- base->flags |= arg;
- }
- void drop_flags(unsigned char arg) {
- base->flags &= ~arg;
- }
-
- amount_t * smaller() const {
- return base->smaller;
- }
- void set_smaller(const amount_t& arg) {
- if (base->smaller)
- delete base->smaller;
- base->smaller = new amount_t(arg);
- }
-
- amount_t * larger() const {
- return base->larger;
- }
- void set_larger(const amount_t& arg) {
- if (base->larger)
- delete base->larger;
- base->larger = new amount_t(arg);
- }
-
- commodity_base_t::history_t * history() const {
- return base->history;
- }
-
- void add_price(const datetime_t& date, const amount_t& price) {
- return base->add_price(date, price);
- }
- bool remove_price(const datetime_t& date) {
- return base->remove_price(date);
- }
- amount_t value(const datetime_t& moment = datetime_t::now) const {
- return base->value(moment);
- }
-
- bool valid() const;
-};
-
-class annotated_commodity_t : public commodity_t
-{
- public:
- const commodity_t * ptr;
-
- amount_t price;
- datetime_t date;
- std::string tag;
-
- explicit annotated_commodity_t() {
- annotated = true;
- }
-
- virtual bool operator==(const commodity_t& comm) const;
-
- void write_annotations(std::ostream& out) const {
- annotated_commodity_t::write_annotations(out, price, date, tag);
- }
-
- static void write_annotations(std::ostream& out,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag);
-
- private:
- static commodity_t * create(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag,
- const std::string& mapping_key);
-
- static commodity_t * find_or_create(const commodity_t& comm,
- const amount_t& price,
- const datetime_t& date,
- const std::string& tag);
-
- friend class amount_t;
-};
+#include "commodity.h"
-inline std::ostream& operator<<(std::ostream& out,
- const commodity_t& comm) {
- out << comm.symbol();
- return out;
-}
+namespace ledger {
-inline amount_t amount_t::round() const {
- return round(commodity().precision());
+inline bool amount_t::operator==(const amount_t& amt) const {
+ if (commodity() != amt.commodity())
+ return false;
+ return compare(amt) == 0;
}
inline commodity_t& amount_t::commodity() const {
- if (! commodity_)
- return *commodity_t::null_commodity;
- else
- return *commodity_;
+ return has_commodity() ? *commodity_ : *current_pool->null_commodity;
}
-class amount_error : public error {
- public:
- amount_error(const std::string& reason) throw() : error(reason) {}
- virtual ~amount_error() throw() {}
-};
-
-struct compare_amount_commodities {
- bool operator()(const amount_t * left, const amount_t * right) const;
-};
+inline bool amount_t::has_commodity() const {
+ return commodity_ && commodity_ != commodity_->parent().null_commodity;
+}
} // namespace ledger
diff --git a/balance.cc b/balance.cc
index 9e516736..80637221 100644
--- a/balance.cc
+++ b/balance.cc
@@ -1,529 +1,272 @@
-#include "balance.h"
-#include "util.h"
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
-#include <deque>
-#include <algorithm>
+#include "balance.h"
namespace ledger {
-amount_t balance_t::amount(const commodity_t& commodity) const
-{
- if (! commodity) {
- if (amounts.size() == 1) {
- amounts_map::const_iterator i = amounts.begin();
- return (*i).second;
- }
- else if (amounts.size() > 1) {
- // Try stripping annotations before giving an error.
- balance_t temp(strip_annotations());
- if (temp.amounts.size() == 1)
- return temp.amount(commodity);
-
- std::ostringstream errmsg;
- errmsg << "Requested amount of a balance with multiple commodities: "
- << temp;
- throw new amount_error(errmsg.str());
- }
- }
- else if (amounts.size() > 0) {
- amounts_map::const_iterator i = amounts.find(&commodity);
- if (i != amounts.end())
- return (*i).second;
- }
- return amount_t();
-}
-
-balance_t balance_t::value(const datetime_t& moment) const
+balance_t& balance_t::operator+=(const balance_t& bal)
{
- balance_t temp;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
+ for (amounts_map::const_iterator i = bal.amounts.begin();
+ i != bal.amounts.end();
i++)
- temp += (*i).second.value(moment);
-
- return temp;
+ *this += i->second;
+ return *this;
}
-balance_t balance_t::price() const
+balance_t& balance_t::operator+=(const amount_t& amt)
{
- balance_t temp;
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot add an uninitialized amount to a balance");
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- temp += (*i).second.price();
+ if (amt.is_realzero())
+ return *this;
- return temp;
-}
+ amounts_map::iterator i = amounts.find(&amt.commodity());
+ if (i != amounts.end())
+ i->second += amt;
+ else
+ amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
-datetime_t balance_t::date() const
-{
- datetime_t temp;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++) {
- datetime_t date = (*i).second.date();
- if (! temp && date)
- temp = date;
- else if (temp != date)
- return datetime_t();
- }
-
- return temp;
+ return *this;
}
-balance_t balance_t::strip_annotations(const bool keep_price,
- const bool keep_date,
- const bool keep_tag) const
+balance_t& balance_t::operator-=(const balance_t& bal)
{
- balance_t temp;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
+ for (amounts_map::const_iterator i = bal.amounts.begin();
+ i != bal.amounts.end();
i++)
- temp += (*i).second.strip_annotations(keep_price, keep_date, keep_tag);
-
- return temp;
+ *this -= i->second;
+ return *this;
}
-void balance_t::write(std::ostream& out,
- const int first_width,
- const int latter_width) const
+balance_t& balance_t::operator-=(const amount_t& amt)
{
- bool first = true;
- int lwidth = latter_width;
-
- if (lwidth == -1)
- lwidth = first_width;
-
- if (commodity_t::commodities_sorted) {
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++) {
- int width;
- if (! first) {
- out << std::endl;
- width = lwidth;
- } else {
- first = false;
- width = first_width;
- }
-
- out.width(width);
- out.fill(' ');
- out << std::right << (*i).second;
- }
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot subtract an uninitialized amount from a balance");
+
+ if (amt.is_realzero())
+ return *this;
+
+ amounts_map::iterator i = amounts.find(&amt.commodity());
+ if (i != amounts.end()) {
+ i->second -= amt;
+ if (i->second.is_realzero())
+ amounts.erase(i);
} else {
- typedef std::deque<const amount_t *> amounts_deque;
- amounts_deque sorted;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second)
- sorted.push_back(&(*i).second);
-
- std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities());
-
- for (amounts_deque::const_iterator i = sorted.begin();
- i != sorted.end();
- i++) {
- int width;
- if (! first) {
- out << std::endl;
- width = lwidth;
- } else {
- first = false;
- width = first_width;
- }
-
- out.width(width);
- out.fill(' ');
- out << std::right << **i;
- }
- }
-
- if (first) {
- out.width(first_width);
- out.fill(' ');
- out << std::right << "0";
- }
-}
-
-balance_t& balance_t::operator*=(const balance_t& bal)
-{
- if (realzero() || bal.realzero()) {
- return *this = 0L;
- }
- else if (bal.amounts.size() == 1) {
- return *this *= (*bal.amounts.begin()).second;
- }
- else if (amounts.size() == 1) {
- return *this = bal * *this;
- }
- else {
- // Since we would fail with an error at this point otherwise, try
- // stripping annotations to see if we can come up with a
- // reasonable result. The user will not notice any annotations
- // missing (since they are viewing a stripped report anyway), only
- // that some of their value expression may not see any pricing or
- // date data because of this operation.
-
- balance_t temp(bal.strip_annotations());
- if (temp.amounts.size() == 1)
- return *this *= temp;
- temp = strip_annotations();
- if (temp.amounts.size() == 1)
- return *this = bal * temp;
-
- std::ostringstream errmsg;
- errmsg << "Cannot multiply two balances: " << temp << " * " << bal;
- throw new amount_error(errmsg.str());
+ amounts.insert(amounts_map::value_type(&amt.commodity(), amt.negate()));
}
+ return *this;
}
balance_t& balance_t::operator*=(const amount_t& amt)
{
- if (realzero() || amt.realzero()) {
- return *this = 0L;
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot multiply a balance by an uninitialized amount");
+
+ if (is_realzero()) {
+ ;
+ }
+ else if (amt.is_realzero()) {
+ *this = amt;
}
else if (! amt.commodity()) {
- // Multiplying by the null commodity causes all amounts to be
- // increased by the same factor.
+ // Multiplying by an amount with no commodity causes all the
+ // component amounts to be increased by the same factor.
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
- (*i).second *= amt;
+ i->second *= amt;
}
else if (amounts.size() == 1) {
- *this = (*amounts.begin()).second * amt;
+ // Multiplying by a commoditized amount is only valid if the sole
+ // commodity in the balance is of the same kind as the amount's
+ // commodity.
+ if (*amounts.begin()->first == amt.commodity())
+ amounts.begin()->second *= amt;
+ else
+ throw_(balance_error,
+ "Cannot multiply a balance with annotated commodities by a commoditized amount");
}
else {
- amounts_map::iterator i = amounts.find(&amt.commodity());
- if (i != amounts.end()) {
- (*i).second *= amt;
- } else {
- // Try stripping annotations before giving an error.
- balance_t temp(strip_annotations());
- if (temp.amounts.size() == 1) {
- return *this = (*temp.amounts.begin()).second * amt;
- } else {
- i = temp.amounts.find(&amt.commodity());
- if (i != temp.amounts.end())
- return *this = temp * amt;
- }
-
- std::ostringstream errmsg;
- errmsg << "Attempt to multiply balance by a commodity"
- << " not found in that balance: "
- << temp << " * " << amt;
- throw new amount_error(errmsg.str());
- }
+ assert(amounts.size() > 1);
+ throw_(balance_error,
+ "Cannot multiply a multi-commodity balance by a commoditized amount");
}
return *this;
}
-balance_t& balance_t::operator/=(const balance_t& bal)
-{
- if (bal.realzero()) {
- std::ostringstream errmsg;
- errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
- throw new amount_error(errmsg.str());
- }
- else if (realzero()) {
- return *this = 0L;
- }
- else if (bal.amounts.size() == 1) {
- return *this /= (*bal.amounts.begin()).second;
- }
- else if (*this == bal) {
- return *this = 1L;
- }
- else {
- // Try stripping annotations before giving an error.
- balance_t temp(bal.strip_annotations());
- if (temp.amounts.size() == 1)
- return *this /= temp;
-
- std::ostringstream errmsg;
- errmsg << "Cannot divide between two balances: " << temp << " / " << bal;
- throw new amount_error(errmsg.str());
- }
-}
-
balance_t& balance_t::operator/=(const amount_t& amt)
{
- if (amt.realzero()) {
- std::ostringstream errmsg;
- errmsg << "Attempt to divide by zero: " << *this << " / " << amt;
- throw new amount_error(errmsg.str());
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot divide a balance by an uninitialized amount");
+
+ if (is_realzero()) {
+ ;
}
- else if (realzero()) {
- return *this = 0L;
+ else if (amt.is_realzero()) {
+ throw_(balance_error, "Divide by zero");
}
else if (! amt.commodity()) {
- // Dividing by the null commodity causes all amounts to be
- // decreased by the same factor.
+ // Dividing by an amount with no commodity causes all the
+ // component amounts to be divided by the same factor.
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
- (*i).second /= amt;
+ i->second /= amt;
}
- else if (amounts.size() == 1 &&
- (*amounts.begin()).first == &amt.commodity()) {
- (*amounts.begin()).second /= amt;
+ else if (amounts.size() == 1) {
+ // Dividing by a commoditized amount is only valid if the sole
+ // commodity in the balance is of the same kind as the amount's
+ // commodity.
+ if (*amounts.begin()->first == amt.commodity())
+ amounts.begin()->second /= amt;
+ else
+ throw_(balance_error,
+ "Cannot divide a balance with annotated commodities by a commoditized amount");
}
else {
- amounts_map::iterator i = amounts.find(&amt.commodity());
- if (i != amounts.end()) {
- (*i).second /= amt;
- } else {
- // Try stripping annotations before giving an error.
- balance_t temp(strip_annotations());
- if (temp.amounts.size() == 1 &&
- (*temp.amounts.begin()).first == &amt.commodity())
- return *this = temp / amt;
-
- std::ostringstream errmsg;
- errmsg << "Attempt to divide balance by a commodity"
- << " not found in that balance: "
- << temp << " * " << amt;
- throw new amount_error(errmsg.str());
- }
+ assert(amounts.size() > 1);
+ throw_(balance_error,
+ "Cannot divide a multi-commodity balance by a commoditized amount");
}
return *this;
}
-balance_t::operator amount_t() const
+optional<balance_t>
+balance_t::value(const optional<moment_t>& moment) const
{
- if (amounts.size() == 1) {
- return (*amounts.begin()).second;
- }
- else if (amounts.size() == 0) {
- return amount_t();
- }
- else {
- // Try stripping annotations before giving an error.
- balance_t temp(strip_annotations());
- if (temp.amounts.size() == 1)
- return (*temp.amounts.begin()).second;
-
- std::ostringstream errmsg;
- errmsg << "Cannot convert a balance with "
- << "multiple commodities to an amount: " << temp;
- throw new amount_error(errmsg.str());
- }
-}
+ optional<balance_t> temp;
-} // namespace ledger
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ if (optional<amount_t> val = i->second.value(moment)) {
+ if (! temp)
+ temp = balance_t();
+ *temp += *val;
+ }
-#ifdef USE_BOOST_PYTHON
+ return temp;
+}
-#include <boost/python.hpp>
+optional<amount_t>
+balance_t::commodity_amount(const optional<const commodity_t&>& commodity) const
+{
+ // jww (2007-05-20): Needs work
+ if (! commodity) {
+ if (amounts.size() == 1) {
+ amounts_map::const_iterator i = amounts.begin();
+ return i->second;
+ }
+ else if (amounts.size() > 1) {
+ // Try stripping annotations before giving an error.
+ balance_t temp(strip_annotations());
+ if (temp.amounts.size() == 1)
+ return temp.commodity_amount(commodity);
-using namespace boost::python;
-using namespace ledger;
+ throw_(amount_error,
+ "Requested amount of a balance with multiple commodities: " << temp);
+ }
+ }
+ else if (amounts.size() > 0) {
+ amounts_map::const_iterator i = amounts.find(&*commodity);
+ if (i != amounts.end())
+ return i->second;
+ }
+ return none;
+}
-unsigned int balance_len(balance_t& bal)
+balance_t balance_t::strip_annotations(const bool keep_price,
+ const bool keep_date,
+ const bool keep_tag) const
{
- return bal.amounts.size();
+ balance_t temp;
+
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ temp += i->second.strip_annotations(keep_price, keep_date, keep_tag);
+
+ return temp;
}
-amount_t balance_getitem(balance_t& bal, int i)
+void balance_t::print(std::ostream& out,
+ const int first_width,
+ const int latter_width) const
{
- std::size_t len = bal.amounts.size();
+ bool first = true;
+ int lwidth = latter_width;
- if (abs(i) >= len) {
- PyErr_SetString(PyExc_IndexError, "Index out of range");
- throw_error_already_set();
- }
+ if (lwidth == -1)
+ lwidth = first_width;
- int x = i < 0 ? len + i : i;
- amounts_map::iterator elem = bal.amounts.begin();
- while (--x >= 0)
- elem++;
+ typedef std::vector<const amount_t *> amounts_array;
+ amounts_array sorted;
- return (*elem).second;
-}
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ if (i->second)
+ sorted.push_back(&i->second);
-unsigned int balance_pair_len(balance_pair_t& bal_pair)
-{
- return balance_len(bal_pair.quantity);
-}
+ std::stable_sort(sorted.begin(), sorted.end(),
+ compare_amount_commodities());
-amount_t balance_pair_getitem(balance_pair_t& bal_pair, int i)
-{
- return balance_getitem(bal_pair.quantity, i);
-}
+ for (amounts_array::const_iterator i = sorted.begin();
+ i != sorted.end();
+ i++) {
+ int width;
+ if (! first) {
+ out << std::endl;
+ width = lwidth;
+ } else {
+ first = false;
+ width = first_width;
+ }
-void export_balance()
-{
- class_< balance_t > ("Balance")
- .def(init<balance_t>())
- .def(init<amount_t>())
- .def(init<long>())
- .def(init<unsigned long>())
- .def(init<double>())
-
- .def(self += self)
- .def(self += other<amount_t>())
- .def(self += long())
- .def(self + self)
- .def(self + other<amount_t>())
- .def(self + long())
- .def(self -= self)
- .def(self -= other<amount_t>())
- .def(self -= long())
- .def(self - self)
- .def(self - other<amount_t>())
- .def(self - long())
- .def(self *= self)
- .def(self *= other<amount_t>())
- .def(self *= long())
- .def(self * self)
- .def(self * other<amount_t>())
- .def(self * long())
- .def(self /= self)
- .def(self /= other<amount_t>())
- .def(self /= long())
- .def(self / self)
- .def(self / other<amount_t>())
- .def(self / long())
- .def(- self)
-
- .def(self < self)
- .def(self < other<amount_t>())
- .def(self < long())
- .def(self <= self)
- .def(self <= other<amount_t>())
- .def(self <= long())
- .def(self > self)
- .def(self > other<amount_t>())
- .def(self > long())
- .def(self >= self)
- .def(self >= other<amount_t>())
- .def(self >= long())
- .def(self == self)
- .def(self == other<amount_t>())
- .def(self == long())
- .def(self != self)
- .def(self != other<amount_t>())
- .def(self != long())
- .def(! self)
-
- .def(abs(self))
- .def(self_ns::str(self))
-
- .def("__len__", balance_len)
- .def("__getitem__", balance_getitem)
-
- .def("valid", &balance_t::valid)
-
- .def("realzero", &balance_t::realzero)
- .def("amount", &balance_t::amount)
- .def("value", &balance_t::value)
- .def("price", &balance_t::price)
- .def("date", &balance_t::date)
- .def("strip_annotations", &balance_t::strip_annotations)
- .def("write", &balance_t::write)
- .def("round", &balance_t::round)
- .def("negate", &balance_t::negate)
- .def("negated", &balance_t::negated)
- ;
+ out.width(width);
+ out.fill(' ');
+ out << std::right << **i;
+ }
- class_< balance_pair_t > ("BalancePair")
- .def(init<balance_pair_t>())
- .def(init<balance_t>())
- .def(init<amount_t>())
- .def(init<long>())
- .def(init<unsigned long>())
- .def(init<double>())
-
- .def(self += self)
- .def(self += other<balance_t>())
- .def(self += other<amount_t>())
- .def(self += long())
- .def(self + self)
- .def(self + other<balance_t>())
- .def(self + other<amount_t>())
- .def(self + long())
- .def(self -= self)
- .def(self -= other<balance_t>())
- .def(self -= other<amount_t>())
- .def(self -= long())
- .def(self - self)
- .def(self - other<balance_t>())
- .def(self - other<amount_t>())
- .def(self - long())
- .def(self *= self)
- .def(self *= other<balance_t>())
- .def(self *= other<amount_t>())
- .def(self *= long())
- .def(self * self)
- .def(self * other<balance_t>())
- .def(self * other<amount_t>())
- .def(self * long())
- .def(self /= self)
- .def(self /= other<balance_t>())
- .def(self /= other<amount_t>())
- .def(self /= long())
- .def(self / self)
- .def(self / other<balance_t>())
- .def(self / other<amount_t>())
- .def(self / long())
- .def(- self)
-
- .def(self < self)
- .def(self < other<balance_t>())
- .def(self < other<amount_t>())
- .def(self < long())
- .def(self <= self)
- .def(self <= other<balance_t>())
- .def(self <= other<amount_t>())
- .def(self <= long())
- .def(self > self)
- .def(self > other<balance_t>())
- .def(self > other<amount_t>())
- .def(self > long())
- .def(self >= self)
- .def(self >= other<balance_t>())
- .def(self >= other<amount_t>())
- .def(self >= long())
- .def(self == self)
- .def(self == other<balance_t>())
- .def(self == other<amount_t>())
- .def(self == long())
- .def(self != self)
- .def(self != other<balance_t>())
- .def(self != other<amount_t>())
- .def(self != long())
- .def(! self)
-
- .def(abs(self))
- .def(self_ns::str(self))
-
- .def("__len__", balance_pair_len)
- .def("__getitem__", balance_pair_getitem)
-
- .def("valid", &balance_pair_t::valid)
-
- .def("realzero", &balance_pair_t::realzero)
- .def("amount", &balance_pair_t::amount)
- .def("value", &balance_pair_t::value)
- .def("price", &balance_pair_t::price)
- .def("date", &balance_pair_t::date)
- .def("strip_annotations", &balance_pair_t::strip_annotations)
- .def("write", &balance_pair_t::write)
- .def("round", &balance_pair_t::round)
- .def("negate", &balance_pair_t::negate)
- .def("negated", &balance_pair_t::negated)
-
- .add_property("cost",
- make_getter(&balance_pair_t::cost,
- return_value_policy<reference_existing_object>()))
- ;
+ if (first) {
+ out.width(first_width);
+ out.fill(' ');
+ out << std::right << "0";
+ }
}
-#endif // USE_BOOST_PYTHON
+} // namespace ledger
diff --git a/balance.h b/balance.h
index ba76d459..aeee506f 100644
--- a/balance.h
+++ b/balance.h
@@ -1,942 +1,516 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file balance.h
+ * @author John Wiegley
+ * @date Sun May 20 15:28:44 2007
+ *
+ * @brief Basic type for adding multiple commodities together.
+ *
+ * Unlike the amount_t class, which throws an exception if amounts of
+ * differing commodities are added or subtracted, the balance_t class
+ * is designed to allow this, tracking the amounts of each component
+ * commodity separately.
+ */
#ifndef _BALANCE_H
#define _BALANCE_H
#include "amount.h"
-#include <map>
-#include <iostream>
-
namespace ledger {
-typedef std::map<const commodity_t *, amount_t> amounts_map;
-typedef std::pair<const commodity_t *, amount_t> amounts_pair;
+DECLARE_EXCEPTION(error, balance_error);
+/**
+ * @class balance_t
+ *
+ * @brief A wrapper around amount_t allowing addition of multiple commodities.
+ *
+ * The balance_t class is appopriate for keeping a running balance
+ * where amounts of multiple commodities may be involved.
+ */
class balance_t
+ : public equality_comparable<balance_t,
+ equality_comparable<balance_t, amount_t,
+ equality_comparable<balance_t, double,
+ equality_comparable<balance_t, unsigned long,
+ equality_comparable<balance_t, long,
+ additive<balance_t,
+ additive<balance_t, amount_t,
+ additive<balance_t, double,
+ additive<balance_t, unsigned long,
+ additive<balance_t, long,
+ multiplicative<balance_t, amount_t,
+ multiplicative<balance_t, double,
+ multiplicative<balance_t, unsigned long,
+ multiplicative<balance_t, long> > > > > > > > > > > > > >
{
- public:
- amounts_map amounts;
+public:
+ typedef std::map<const commodity_t *, amount_t> amounts_map;
- bool valid() const {
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if (! (*i).second.valid())
- return false;
- return true;
- }
+ amounts_map amounts;
- // constructors
- balance_t() {}
- balance_t(const balance_t& bal) {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- *this += (*i).second;
+ // jww (2007-05-20): Remove these two by adding access methods
+ friend class value_t;
+ friend class entry_base_t;
+
+ /**
+ * Constructors. balance_t supports similar forms of construction
+ * to amount_t.
+ *
+ * balance_t() creates an empty balance to which amounts or other
+ * balances may be added or subtracted.
+ *
+ * balance_t(amount_t) constructs a balance whose starting value is
+ * equal to the given amount.
+ *
+ * balance_t(double), balance_t(unsigned long) and balance_t(long)
+ * will construct an amount from their arguments and then construct
+ * a balance whose starting value is equal to that amount. This
+ * initial balance will have no commodity.
+ *
+ * balance_t(string) and balance_t(const char *) both convert from a
+ * string representation of an amount to a balance whose initial
+ * value is that amount. This is the proper way to initialize a
+ * balance like '$100.00'.
+ */
+ balance_t() {
+ TRACE_CTOR(balance_t, "");
}
balance_t(const amount_t& amt) {
- if (! amt.realzero())
- amounts.insert(amounts_pair(&amt.commodity(), amt));
+ TRACE_CTOR(balance_t, "const amount_t&");
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot initialize a balance from an uninitialized amount");
+ if (! amt.is_realzero())
+ amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
}
- template <typename T>
- balance_t(T value) {
- amount_t amt(value);
- if (! amt.realzero())
- amounts.insert(amounts_pair(&amt.commodity(), amt));
- }
-
- // assignment operator
- balance_t& operator=(const balance_t& bal) {
- if (this != &bal) {
- amounts.clear();
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- *this += (*i).second;
- }
- return *this;
+ balance_t(const double val) {
+ TRACE_CTOR(balance_t, "const double");
+ amounts.insert
+ (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
}
- balance_t& operator=(const amount_t& amt) {
- amounts.clear();
- *this += amt;
- return *this;
+ balance_t(const unsigned long val) {
+ TRACE_CTOR(balance_t, "const unsigned long");
+ amounts.insert
+ (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
}
- template <typename T>
- balance_t& operator=(T value) {
- amounts.clear();
- *this += value;
- return *this;
+ balance_t(const long val) {
+ TRACE_CTOR(balance_t, "const long");
+ amounts.insert
+ (amounts_map::value_type(amount_t::current_pool->null_commodity, val));
}
- // in-place arithmetic
- balance_t& operator+=(const balance_t& bal) {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- *this += (*i).second;
- return *this;
+ explicit balance_t(const string& val) {
+ TRACE_CTOR(balance_t, "const string&");
+ amount_t temp(val);
+ amounts.insert(amounts_map::value_type(&temp.commodity(), temp));
}
- balance_t& operator+=(const amount_t& amt) {
- amounts_map::iterator i = amounts.find(&amt.commodity());
- if (i != amounts.end())
- (*i).second += amt;
- else if (! amt.realzero())
- amounts.insert(amounts_pair(&amt.commodity(), amt));
- return *this;
+ explicit balance_t(const char * val) {
+ TRACE_CTOR(balance_t, "const char *");
+ amount_t temp(val);
+ amounts.insert(amounts_map::value_type(&temp.commodity(), temp));
}
- template <typename T>
- balance_t& operator+=(T val) {
- return *this += amount_t(val);
+
+ /**
+ * Destructor. Destroys all of the accumulated amounts in the
+ * balance.
+ */
+ virtual ~balance_t() {
+ TRACE_DTOR(balance_t);
}
- balance_t& operator-=(const balance_t& bal) {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- *this -= (*i).second;
- return *this;
+
+ /**
+ * Assignment and copy operators. An balance may be assigned or copied.
+ */
+ balance_t(const balance_t& bal) : amounts(bal.amounts) {
+ TRACE_CTOR(balance_t, "copy");
}
- balance_t& operator-=(const amount_t& amt) {
- amounts_map::iterator i = amounts.find(&amt.commodity());
- if (i != amounts.end()) {
- (*i).second -= amt;
- if ((*i).second.realzero())
- amounts.erase(i);
- }
- else if (! amt.realzero()) {
- amounts.insert(amounts_pair(&amt.commodity(), - amt));
- }
+
+ balance_t& operator=(const balance_t& bal) {
+ if (this != &bal)
+ amounts = bal.amounts;
return *this;
}
- template <typename T>
- balance_t& operator-=(T val) {
- return *this -= amount_t(val);
- }
+ balance_t& operator=(const amount_t& amt) {
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot assign an uninitialized amount to a balance");
- // simple arithmetic
- balance_t operator+(const balance_t& bal) const {
- balance_t temp = *this;
- temp += bal;
- return temp;
- }
- balance_t operator+(const amount_t& amt) const {
- balance_t temp = *this;
- temp += amt;
- return temp;
- }
- template <typename T>
- balance_t operator+(T val) const {
- balance_t temp = *this;
- temp += val;
- return temp;
- }
- balance_t operator-(const balance_t& bal) const {
- balance_t temp = *this;
- temp -= bal;
- return temp;
- }
- balance_t operator-(const amount_t& amt) const {
- balance_t temp = *this;
- temp -= amt;
- return temp;
- }
- template <typename T>
- balance_t operator-(T val) const {
- balance_t temp = *this;
- temp -= val;
- return temp;
- }
+ amounts.clear();
+ if (! amt.is_realzero())
+ amounts.insert(amounts_map::value_type(&amt.commodity(), amt));
- // multiplication and divide
- balance_t& operator*=(const balance_t& bal);
- balance_t& operator*=(const amount_t& amt);
- template <typename T>
- balance_t& operator*=(T val) {
- return *this *= amount_t(val);
+ return *this;
}
- balance_t& operator/=(const balance_t& bal);
- balance_t& operator/=(const amount_t& amt);
- template <typename T>
- balance_t& operator/=(T val) {
- return *this /= amount_t(val);
+ balance_t& operator=(const string& str) {
+ return *this = balance_t(str);
+ }
+ balance_t& operator=(const char * str) {
+ return *this = balance_t(str);
+ }
+
+ /**
+ * Comparison operators. Balances are fairly restrictive in terms
+ * of how they may be compared. They may be compared for equality
+ * or inequality, but this is all, since the concept of "less than"
+ * or "greater than" makes no sense when amounts of multiple
+ * commodities are involved.
+ *
+ * Balances may also be compared to amounts, in which case the sum
+ * of the balance must equal the amount exactly.
+ *
+ * If a comparison between balances is desired, the balances must
+ * first be rendered to value equivalent amounts using the `value'
+ * method, to determine a market valuation at some specific moment
+ * in time.
+ */
+ bool operator==(const balance_t& bal) const {
+ amounts_map::const_iterator i, j;
+ for (i = amounts.begin(), j = bal.amounts.begin();
+ i != amounts.end() && j != bal.amounts.end();
+ i++, j++) {
+ if (! (i->first == j->first && i->second == j->second))
+ return false;
+ }
+ return i == amounts.end() && j == bal.amounts.end();
}
+ bool operator==(const amount_t& amt) const {
+ if (amt.is_null())
+ throw_(balance_error,
+ "Cannot compare a balance to an uninitialized amount");
- // multiplication and divide
- balance_t operator*(const balance_t& bal) const {
- balance_t temp = *this;
- temp *= bal;
- return temp;
- }
- balance_t operator*(const amount_t& amt) const {
- balance_t temp = *this;
- temp *= amt;
- return temp;
- }
- template <typename T>
- balance_t operator*(T val) const {
- balance_t temp = *this;
- temp *= val;
- return temp;
- }
- balance_t operator/(const balance_t& bal) const {
- balance_t temp = *this;
- temp /= bal;
- return temp;
- }
- balance_t operator/(const amount_t& amt) const {
- balance_t temp = *this;
- temp /= amt;
- return temp;
+ if (amt.is_realzero())
+ return amounts.empty();
+ else
+ return amounts.size() == 1 && amounts.begin()->second == amt;
}
+
template <typename T>
- balance_t operator/(T val) const {
- balance_t temp = *this;
- temp /= val;
- return temp;
+ bool operator==(const T& val) const {
+ return *this == balance_t(val);
}
- // comparison
- bool operator<(const balance_t& bal) const {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- if (! (amount(*(*i).first) < (*i).second))
- return false;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if (! ((*i).second < bal.amount(*(*i).first)))
- return false;
+ /**
+ * Binary arithmetic operators. Balances support addition and
+ * subtraction of other balances or amounts, but multiplication and
+ * division are restricted to uncommoditized amounts only.
+ */
+ balance_t& operator+=(const balance_t& bal);
+ balance_t& operator+=(const amount_t& amt);
+ balance_t& operator-=(const balance_t& bal);
+ balance_t& operator-=(const amount_t& amt);
- if (bal.amounts.size() == 0 && amounts.size() == 0)
- return false;
+ virtual balance_t& operator*=(const amount_t& amt);
- return true;
+ balance_t& operator*=(const double val) {
+ return *this *= amount_t(val);
}
- bool operator<(const amount_t& amt) const {
- if (amt.commodity())
- return amount(amt.commodity()) < amt;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second < amt)
- return true;
- return false;
+ balance_t& operator*=(const unsigned long val) {
+ return *this *= amount_t(val);
}
- template <typename T>
- bool operator<(T val) const {
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second < val)
- return true;
- return false;
+ balance_t& operator*=(const long val) {
+ return *this *= amount_t(val);
}
- bool operator<=(const balance_t& bal) const {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- if (! (amount(*(*i).first) <= (*i).second))
- return false;
+ virtual balance_t& operator/=(const amount_t& amt);
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if (! ((*i).second <= bal.amount(*(*i).first)))
- return false;
-
- return true;
+ balance_t& operator/=(const double val) {
+ return *this /= amount_t(val);
}
- bool operator<=(const amount_t& amt) const {
- if (amt.commodity())
- return amount(amt.commodity()) <= amt;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second <= amt)
- return true;
- return false;
+ balance_t& operator/=(const unsigned long val) {
+ return *this /= amount_t(val);
}
- template <typename T>
- bool operator<=(T val) const {
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second <= val)
- return true;
- return false;
+ balance_t& operator/=(const long val) {
+ return *this /= amount_t(val);
}
- bool operator>(const balance_t& bal) const {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- if (! (amount(*(*i).first) > (*i).second))
- return false;
-
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if (! ((*i).second > bal.amount(*(*i).first)))
- return false;
-
- if (bal.amounts.size() == 0 && amounts.size() == 0)
- return false;
-
- return true;
+ /**
+ * Unary arithmetic operators. There are only a few unary methods
+ * support on balance:
+ *
+ * negate(), also unary minus (- x), returns a balance all of whose
+ * component amounts have been negated. In order words, it inverts
+ * the sign of all member amounts.
+ *
+ * abs() returns a balance where no component amount is negative.
+ *
+ * reduce() reduces the values in a balance to their most basic
+ * commodity forms, for amounts that utilize "scaling commodities".
+ * For example, a balance of 1h and 1m after reduction will be
+ * 3660s.
+ *
+ * unreduce(), if used with amounts that use "scaling commodities",
+ * yields the most compact form greater than 1.0 for each component
+ * amount. That is, a balance of 10m and 1799s will unreduce to
+ * 39.98m.
+ *
+ * value(optional<moment_t>) returns the total historical value for
+ * a balance -- the default moment returns a value based on the most
+ * recently known price -- based on the price history of its
+ * component commodities. See amount_t::value for an example.
+ *
+ * Further, for the sake of efficiency and avoiding temporary
+ * objects, the following methods support "in-place" variants act on
+ * the balance itself and return a reference to the result
+ * (`*this'):
+ *
+ * in_place_negate()
+ * in_place_reduce()
+ * in_place_unreduce()
+ */
+ balance_t negate() const {
+ balance_t temp(*this);
+ temp.in_place_negate();
+ return temp;
}
- bool operator>(const amount_t& amt) const {
- if (amt.commodity())
- return amount(amt.commodity()) > amt;
-
- for (amounts_map::const_iterator i = amounts.begin();
+ virtual balance_t& in_place_negate() {
+ for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
- if ((*i).second > amt)
- return true;
- return false;
+ i->second.in_place_negate();
+ return *this;
}
- template <typename T>
- bool operator>(T val) const {
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second > val)
- return true;
- return false;
+ balance_t operator-() const {
+ return negate();
}
- bool operator>=(const balance_t& bal) const {
- for (amounts_map::const_iterator i = bal.amounts.begin();
- i != bal.amounts.end();
- i++)
- if (! (amount(*(*i).first) >= (*i).second))
- return false;
-
+ balance_t abs() const {
+ balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if (! ((*i).second >= bal.amount(*(*i).first)))
- return false;
-
- return true;
+ temp += i->second.abs();
+ return temp;
}
- bool operator>=(const amount_t& amt) const {
- if (amt.commodity())
- return amount(amt.commodity()) >= amt;
- for (amounts_map::const_iterator i = amounts.begin();
- i != amounts.end();
- i++)
- if ((*i).second >= amt)
- return true;
- return false;
+ balance_t reduce() const {
+ balance_t temp(*this);
+ temp.in_place_reduce();
+ return temp;
}
- template <typename T>
- bool operator>=(T val) const {
+ virtual balance_t& in_place_reduce() {
+ // A temporary must be used here because reduction may cause
+ // multiple component amounts to collapse to the same commodity.
+ balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if ((*i).second >= val)
- return true;
- return false;
+ temp += i->second.reduce();
+ return *this = temp;
}
- bool operator==(const balance_t& bal) const {
- amounts_map::const_iterator i, j;
- for (i = amounts.begin(), j = bal.amounts.begin();
- i != amounts.end() && j != bal.amounts.end();
- i++, j++) {
- if (! ((*i).first == (*j).first &&
- (*i).second == (*j).second))
- return false;
- }
- return i == amounts.end() && j == bal.amounts.end();
+ balance_t unreduce() const {
+ balance_t temp(*this);
+ temp.in_place_unreduce();
+ return temp;
}
- bool operator==(const amount_t& amt) const {
- if (amt.commodity())
- return amounts.size() == 1 && (*amounts.begin()).second == amt;
-
+ virtual balance_t& in_place_unreduce() {
+ // A temporary must be used here because unreduction may cause
+ // multiple component amounts to collapse to the same commodity.
+ balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if ((*i).second == amt)
- return true;
- return false;
- }
- template <typename T>
- bool operator==(T val) const {
+ temp += i->second.unreduce();
+ return *this = temp;
+ }
+
+ optional<balance_t> value(const optional<moment_t>& moment = none) const;
+
+ /**
+ * Truth tests. An balance may be truth test in two ways:
+ *
+ * is_nonzero(), or operator bool, returns true if a balance's
+ * display value is not zero.
+ *
+ * is_zero() returns true if an balance's display value is zero.
+ * Thus, a balance containing $0.0001 is considered zero if the
+ * current display precision for dollars is two decimal places.
+ *
+ * is_realzero() returns true if an balance's actual value is zero.
+ * Thus, a balance containing $0.0001 is never considered realzero.
+ *
+ * is_empty() returns true if a balance has no amounts within it.
+ * This can occur after a balance has been default initialized, or
+ * if the exact amount it contains is subsequently subtracted from
+ * it.
+ */
+ operator bool() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if ((*i).second == val)
+ if (i->second.is_nonzero())
return true;
return false;
}
- bool operator!=(const balance_t& bal) const {
- return ! (*this == bal);
- }
- bool operator!=(const amount_t& amt) const {
- return ! (*this == amt);
- }
- template <typename T>
- bool operator!=(T val) const {
- return ! (*this == val);
- }
-
- // unary negation
- void negate() {
- for (amounts_map::iterator i = amounts.begin();
- i != amounts.end();
- i++)
- (*i).second.negate();
- }
- balance_t negated() const {
- balance_t temp = *this;
- temp.negate();
- return temp;
- }
- balance_t operator-() const {
- return negated();
- }
+ bool is_zero() const {
+ if (is_empty())
+ return true;
- // conversion operators
- operator amount_t() const;
- operator bool() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if ((*i).second)
- return true;
- return false;
+ if (! i->second.is_zero())
+ return false;
+ return true;
}
- bool realzero() const {
- if (amounts.size() == 0)
+ bool is_realzero() const {
+ if (is_empty())
return true;
+
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if (! (*i).second.realzero())
+ if (! i->second.is_realzero())
return false;
return true;
}
- amount_t amount(const commodity_t& commodity =
- *commodity_t::null_commodity) const;
- balance_t value(const datetime_t& moment = datetime_t::now) const;
- balance_t price() const;
- datetime_t date() const;
-
+ bool is_empty() const {
+ return amounts.size() == 0;
+ }
+
+ /**
+ * Conversion methods. A balance can be converted to an amount, but
+ * only if contains a single component amount.
+ */
+ amount_t to_amount() const {
+ if (is_empty())
+ throw_(balance_error, "Cannot convert an empty balance to an amount");
+ else if (amounts.size() == 1)
+ return amounts.begin()->second;
+ else
+ throw_(balance_error,
+ "Cannot convert a balance with multiple commodities to an amount");
+ }
+
+ /**
+ * Commodity-related methods. Balances support two
+ * commodity-related methods:
+ *
+ * commodity_count() returns the number of different commodities
+ * stored in the balance.
+ *
+ * commodity_amount(optional<commodity_t>) returns an (optional)
+ * amount for the given commodity within the balance; if no
+ * commodity is specified, it returns the (optional) uncommoditized
+ * component of the balance. If no matching element can be found,
+ * boost::none is returned.
+ */
+ std::size_t commodity_count() const {
+ return amounts.size();
+ }
+
+ optional<amount_t>
+ commodity_amount(const optional<const commodity_t&>& commodity = none) const;
+
+ /**
+ * Annotated commodity methods. The amounts contained by a balance
+ * may use annotated commodities. The `strip_annotations' method
+ * will return a balance all of whose component amount have had
+ * their commodity annotations likewise stripped. See
+ * amount_t::strip_annotations for more details.
+ */
balance_t
strip_annotations(const bool keep_price = amount_t::keep_price,
const bool keep_date = amount_t::keep_date,
const bool keep_tag = amount_t::keep_tag) const;
- void write(std::ostream& out, const int first_width,
+ /**
+ * Printing methods. A balance may be output to a stream using the
+ * `print' method. There is also a global operator<< defined which
+ * simply calls print for a balance on the given stream. There is
+ * one form of the print method, which takes two required arguments
+ * and one arguments with a default value:
+ *
+ * print(ostream, int first_width, int latter_width) prints a
+ * balance to the given output stream, using each commodity's
+ * default display characteristics. The first_width parameter
+ * specifies the width that should be used for printing amounts
+ * (since they are likely to vary in width). The latter_width, if
+ * specified, gives the width to be used for each line after the
+ * first. This is useful when printing in a column which falls at
+ * the right-hand side of the screen.
+ *
+ * In addition to the width constraints, balances will also print
+ * with commodities in alphabetized order, regardless of the
+ * relative amounts of those commodities. There is no option to
+ * change this behavior.
+ */
+ void print(std::ostream& out, const int first_width,
const int latter_width = -1) const;
- void abs() {
- for (amounts_map::iterator i = amounts.begin();
- i != amounts.end();
- i++)
- (*i).second.abs();
- }
-
- void reduce() {
- for (amounts_map::iterator i = amounts.begin();
- i != amounts.end();
- i++)
- (*i).second.reduce();
- }
-
- balance_t reduced() const {
- balance_t temp(*this);
- temp.reduce();
- return temp;
- }
-
- void round() {
- for (amounts_map::iterator i = amounts.begin();
+ /**
+ * Debugging methods. There are two methods defined to help with
+ * debugging:
+ *
+ * dump(ostream) dumps a balance to an output stream. There is
+ * little different from print(), it simply surrounds the display
+ * value with a marker, for example "BALANCE($1.00, DM 12.00)".
+ * This code is used by other dumping code elsewhere in Ledger.
+ *
+ * valid() returns true if the amounts within the balance are valid.
+ */
+ void dump(std::ostream& out) const {
+ out << "BALANCE(";
+ bool first = true;
+ for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
- i++)
- if ((*i).second.commodity())
- (*i).second = (*i).second.round();
+ i++) {
+ if (first)
+ first = false;
+ else
+ out << ", ";
+ i->second.print(out);
+ }
+ out << ")";
}
- balance_t unround() const {
- balance_t temp;
+ virtual bool valid() const {
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++)
- if ((*i).second.commodity())
- temp += (*i).second.unround();
- return temp;
+ if (! i->second.valid())
+ return false;
+ return true;
}
};
-inline balance_t abs(const balance_t& bal) {
- balance_t temp = bal;
- temp.abs();
- return temp;
-}
-
inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) {
- bal.write(out, 12);
- return out;
-}
-
-class balance_pair_t
-{
- public:
- balance_t quantity;
- balance_t * cost;
-
- // constructors
- balance_pair_t() : cost(NULL) {}
- balance_pair_t(const balance_pair_t& bal_pair)
- : quantity(bal_pair.quantity), cost(NULL) {
- if (bal_pair.cost)
- cost = new balance_t(*bal_pair.cost);
- }
- balance_pair_t(const balance_t& _quantity)
- : quantity(_quantity), cost(NULL) {}
- balance_pair_t(const amount_t& _quantity)
- : quantity(_quantity), cost(NULL) {}
- template <typename T>
- balance_pair_t(T value) : quantity(value), cost(NULL) {}
-
- // destructor
- ~balance_pair_t() {
- if (cost) delete cost;
- }
-
- // assignment operator
- balance_pair_t& operator=(const balance_pair_t& bal_pair) {
- if (this != &bal_pair) {
- if (cost) {
- delete cost;
- cost = NULL;
- }
- quantity = bal_pair.quantity;
- if (bal_pair.cost)
- cost = new balance_t(*bal_pair.cost);
- }
- return *this;
- }
- balance_pair_t& operator=(const balance_t& bal) {
- if (cost) {
- delete cost;
- cost = NULL;
- }
- quantity = bal;
- return *this;
- }
- balance_pair_t& operator=(const amount_t& amt) {
- if (cost) {
- delete cost;
- cost = NULL;
- }
- quantity = amt;
- return *this;
- }
- template <typename T>
- balance_pair_t& operator=(T value) {
- if (cost) {
- delete cost;
- cost = NULL;
- }
- quantity = value;
- return *this;
- }
-
- // in-place arithmetic
- balance_pair_t& operator+=(const balance_pair_t& bal_pair) {
- if (bal_pair.cost && ! cost)
- cost = new balance_t(quantity);
- quantity += bal_pair.quantity;
- if (cost)
- *cost += bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;
- return *this;
- }
- balance_pair_t& operator+=(const balance_t& bal) {
- quantity += bal;
- if (cost)
- *cost += bal;
- return *this;
- }
- balance_pair_t& operator+=(const amount_t& amt) {
- quantity += amt;
- if (cost)
- *cost += amt;
- return *this;
- }
- template <typename T>
- balance_pair_t& operator+=(T val) {
- return *this += amount_t(val);
- }
-
- balance_pair_t& operator-=(const balance_pair_t& bal_pair) {
- if (bal_pair.cost && ! cost)
- cost = new balance_t(quantity);
- quantity -= bal_pair.quantity;
- if (cost)
- *cost -= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;
- return *this;
- }
- balance_pair_t& operator-=(const balance_t& bal) {
- quantity -= bal;
- if (cost)
- *cost -= bal;
- return *this;
- }
- balance_pair_t& operator-=(const amount_t& amt) {
- quantity -= amt;
- if (cost)
- *cost -= amt;
- return *this;
- }
- template <typename T>
- balance_pair_t& operator-=(T val) {
- return *this -= amount_t(val);
- }
-
- // simple arithmetic
- balance_pair_t operator+(const balance_pair_t& bal_pair) const {
- balance_pair_t temp = *this;
- temp += bal_pair;
- return temp;
- }
- balance_pair_t operator+(const balance_t& bal) const {
- balance_pair_t temp = *this;
- temp += bal;
- return temp;
- }
- balance_pair_t operator+(const amount_t& amt) const {
- balance_pair_t temp = *this;
- temp += amt;
- return temp;
- }
- template <typename T>
- balance_pair_t operator+(T val) const {
- balance_pair_t temp = *this;
- temp += val;
- return temp;
- }
-
- balance_pair_t operator-(const balance_pair_t& bal_pair) const {
- balance_pair_t temp = *this;
- temp -= bal_pair;
- return temp;
- }
- balance_pair_t operator-(const balance_t& bal) const {
- balance_pair_t temp = *this;
- temp -= bal;
- return temp;
- }
- balance_pair_t operator-(const amount_t& amt) const {
- balance_pair_t temp = *this;
- temp -= amt;
- return temp;
- }
- template <typename T>
- balance_pair_t operator-(T val) const {
- balance_pair_t temp = *this;
- temp -= val;
- return temp;
- }
-
- // multiplication and division
- balance_pair_t& operator*=(const balance_pair_t& bal_pair) {
- if (bal_pair.cost && ! cost)
- cost = new balance_t(quantity);
- quantity *= bal_pair.quantity;
- if (cost)
- *cost *= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;
- return *this;
- }
- balance_pair_t& operator*=(const balance_t& bal) {
- quantity *= bal;
- if (cost)
- *cost *= bal;
- return *this;
- }
- balance_pair_t& operator*=(const amount_t& amt) {
- quantity *= amt;
- if (cost)
- *cost *= amt;
- return *this;
- }
- template <typename T>
- balance_pair_t& operator*=(T val) {
- return *this *= amount_t(val);
- }
-
- balance_pair_t& operator/=(const balance_pair_t& bal_pair) {
- if (bal_pair.cost && ! cost)
- cost = new balance_t(quantity);
- quantity /= bal_pair.quantity;
- if (cost)
- *cost /= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;
- return *this;
- }
- balance_pair_t& operator/=(const balance_t& bal) {
- quantity /= bal;
- if (cost)
- *cost /= bal;
- return *this;
- }
- balance_pair_t& operator/=(const amount_t& amt) {
- quantity /= amt;
- if (cost)
- *cost /= amt;
- return *this;
- }
- template <typename T>
- balance_pair_t& operator/=(T val) {
- return *this /= amount_t(val);
- }
-
- balance_pair_t operator*(const balance_pair_t& bal_pair) const {
- balance_pair_t temp = *this;
- temp *= bal_pair;
- return temp;
- }
- balance_pair_t operator*(const balance_t& bal) const {
- balance_pair_t temp = *this;
- temp *= bal;
- return temp;
- }
- balance_pair_t operator*(const amount_t& amt) const {
- balance_pair_t temp = *this;
- temp *= amt;
- return temp;
- }
- template <typename T>
- balance_pair_t operator*(T val) const {
- balance_pair_t temp = *this;
- temp *= val;
- return temp;
- }
-
- balance_pair_t operator/(const balance_pair_t& bal_pair) const {
- balance_pair_t temp = *this;
- temp /= bal_pair;
- return temp;
- }
- balance_pair_t operator/(const balance_t& bal) const {
- balance_pair_t temp = *this;
- temp /= bal;
- return temp;
- }
- balance_pair_t operator/(const amount_t& amt) const {
- balance_pair_t temp = *this;
- temp /= amt;
- return temp;
- }
- template <typename T>
- balance_pair_t operator/(T val) const {
- balance_pair_t temp = *this;
- temp /= val;
- return temp;
- }
-
- // comparison
- bool operator<(const balance_pair_t& bal_pair) const {
- return quantity < bal_pair.quantity;
- }
- bool operator<(const balance_t& bal) const {
- return quantity < bal;
- }
- bool operator<(const amount_t& amt) const {
- return quantity < amt;
- }
- template <typename T>
- bool operator<(T val) const {
- return quantity < val;
- }
-
- bool operator<=(const balance_pair_t& bal_pair) const {
- return quantity <= bal_pair.quantity;
- }
- bool operator<=(const balance_t& bal) const {
- return quantity <= bal;
- }
- bool operator<=(const amount_t& amt) const {
- return quantity <= amt;
- }
- template <typename T>
- bool operator<=(T val) const {
- return quantity <= val;
- }
-
- bool operator>(const balance_pair_t& bal_pair) const {
- return quantity > bal_pair.quantity;
- }
- bool operator>(const balance_t& bal) const {
- return quantity > bal;
- }
- bool operator>(const amount_t& amt) const {
- return quantity > amt;
- }
- template <typename T>
- bool operator>(T val) const {
- return quantity > val;
- }
-
- bool operator>=(const balance_pair_t& bal_pair) const {
- return quantity >= bal_pair.quantity;
- }
- bool operator>=(const balance_t& bal) const {
- return quantity >= bal;
- }
- bool operator>=(const amount_t& amt) const {
- return quantity >= amt;
- }
- template <typename T>
- bool operator>=(T val) const {
- return quantity >= val;
- }
-
- bool operator==(const balance_pair_t& bal_pair) const {
- return quantity == bal_pair.quantity;
- }
- bool operator==(const balance_t& bal) const {
- return quantity == bal;
- }
- bool operator==(const amount_t& amt) const {
- return quantity == amt;
- }
- template <typename T>
- bool operator==(T val) const {
- return quantity == val;
- }
-
- bool operator!=(const balance_pair_t& bal_pair) const {
- return ! (*this == bal_pair);
- }
- bool operator!=(const balance_t& bal) const {
- return ! (*this == bal);
- }
- bool operator!=(const amount_t& amt) const {
- return ! (*this == amt);
- }
- template <typename T>
- bool operator!=(T val) const {
- return ! (*this == val);
- }
-
- // unary negation
- void negate() {
- quantity.negate();
- if (cost) cost->negate();
- }
- balance_pair_t negated() const {
- balance_pair_t temp = *this;
- temp.negate();
- return temp;
- }
- balance_pair_t operator-() const {
- return negated();
- }
-
- // test for non-zero (use ! for zero)
- operator balance_t() const {
- return quantity;
- }
- operator amount_t() const {
- return quantity;
- }
- operator bool() const {
- return quantity;
- }
-
- bool realzero() const {
- return ((! cost || cost->realzero()) && quantity.realzero());
- }
-
- void abs() {
- quantity.abs();
- if (cost) cost->abs();
- }
-
- amount_t amount(const commodity_t& commodity =
- *commodity_t::null_commodity) const {
- return quantity.amount(commodity);
- }
- balance_t value(const datetime_t& moment = datetime_t::now) const {
- return quantity.value(moment);
- }
- balance_t price() const {
- return quantity.price();
- }
- datetime_t date() const {
- return quantity.date();
- }
-
- balance_t
- strip_annotations(const bool keep_price = amount_t::keep_price,
- const bool keep_date = amount_t::keep_date,
- const bool keep_tag = amount_t::keep_tag) const {
- return quantity.strip_annotations(keep_price, keep_date, keep_tag);
- }
-
- void write(std::ostream& out, const int first_width,
- const int latter_width = -1) const {
- quantity.write(out, first_width, latter_width);
- }
-
- balance_pair_t& add(const amount_t& amount,
- const amount_t * a_cost = NULL) {
- if (a_cost && ! cost)
- cost = new balance_t(quantity);
- quantity += amount;
- if (cost)
- *cost += a_cost ? *a_cost : amount;
- return *this;
- }
-
- bool valid() {
- return quantity.valid() && (! cost || cost->valid());
- }
-
- void reduce() {
- quantity.reduce();
- if (cost) cost->reduce();
- }
-
- balance_pair_t reduced() const {
- balance_pair_t temp(*this);
- temp.reduce();
- return temp;
- }
-
- void round() {
- quantity.round();
- if (cost) cost->round();
- }
-
- balance_pair_t unround() {
- balance_pair_t temp(quantity.unround());
- if (cost)
- temp.cost = new balance_t(cost->unround());
- return temp;
- }
-};
-
-inline balance_pair_t abs(const balance_pair_t& bal_pair) {
- balance_pair_t temp;
- temp.abs();
- return temp;
-}
-
-inline std::ostream& operator<<(std::ostream& out,
- const balance_pair_t& bal_pair) {
- bal_pair.quantity.write(out, 12);
+ bal.print(out, 12);
return out;
}
diff --git a/balpair.h b/balpair.h
new file mode 100644
index 00000000..96ccf42a
--- /dev/null
+++ b/balpair.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file balpair.h
+ * @author John Wiegley
+ * @date Sun May 20 19:11:58 2007
+ *
+ * @brief Provides an abstraction around balance_t for tracking costs.
+ *
+ * When a transaction's amount is added to a balance, only the "value"
+ * of the amount is added -- not the associated cost of the
+ * transaction. To provide for this, the balance_pair_t type allows
+ * for adding amounts and costs simultaneously to a single balance.
+ * Both are tracked, and any time either the total amount balance or
+ * the total cost balance may be extracted.
+ *
+ * Note: By default, all balance-like operations operate on the amount
+ * balance, and not the cost. Also, the cost is entirely optional, in
+ * which case a balance_pair_t may be used as if it were a balance_t,
+ * from which is it derived.
+ */
+#ifndef _BALPAIR_H
+#define _BARPAIR_H
+
+#include "balance.h"
+
+namespace ledger {
+
+class balance_pair_t
+ : public balance_t,
+ public equality_comparable<balance_pair_t,
+ equality_comparable<balance_pair_t, balance_t,
+ equality_comparable<balance_pair_t, amount_t,
+ equality_comparable<balance_pair_t, double,
+ equality_comparable<balance_pair_t, unsigned long,
+ equality_comparable<balance_pair_t, long,
+ additive<balance_pair_t,
+ additive<balance_pair_t, balance_t,
+ additive<balance_pair_t, amount_t,
+ additive<balance_pair_t, double,
+ additive<balance_pair_t, unsigned long,
+ additive<balance_pair_t, long,
+ multiplicative<balance_pair_t, amount_t,
+ multiplicative<balance_pair_t, balance_t,
+ multiplicative<balance_pair_t, double,
+ multiplicative<balance_pair_t, unsigned long,
+ multiplicative<balance_pair_t, long> > > > > > > > > > > > > > > > >
+{
+ /**
+ * The `cost' member of a balance pair tracks the cost associated
+ * with each transaction amount that is added. This member is
+ * optional, and if not cost-bearing transactions are added, it will
+ * remain uninitialized.
+ */
+ optional<balance_t> cost;
+
+ friend class value_t;
+ friend class entry_base_t;
+
+public:
+ /**
+ * Constructors. balance_pair_t supports identical forms of construction
+ * to balance_t. See balance_t for more information.
+ */
+ balance_pair_t() {
+ TRACE_CTOR(balance_pair_t, "");
+ }
+ balance_pair_t(const balance_t& bal) : balance_t(bal) {
+ TRACE_CTOR(balance_pair_t, "const balance_t&");
+ }
+ balance_pair_t(const balance_t& bal,
+ const balance_t& cost_bal)
+ : balance_t(bal), cost(cost_bal) {
+ TRACE_CTOR(balance_pair_t, "const balance_t&, const balance_t&");
+ }
+ balance_pair_t(const amount_t& amt) : balance_t(amt) {
+ TRACE_CTOR(balance_pair_t, "const amount_t&");
+ }
+ balance_pair_t(const amount_t& amt, const amount_t& cost_amt)
+ : balance_t(amt), cost(cost_amt) {
+ TRACE_CTOR(balance_pair_t, "const amount_t&, const amount_t&");
+ }
+ balance_pair_t(const double val) : balance_t(val) {
+ TRACE_CTOR(balance_pair_t, "const double");
+ }
+ balance_pair_t(const unsigned long val) : balance_t(val) {
+ TRACE_CTOR(balance_pair_t, "const unsigned long");
+ }
+ balance_pair_t(const long val) : balance_t(val) {
+ TRACE_CTOR(balance_pair_t, "const long");
+ }
+
+ explicit balance_pair_t(const string& val) : balance_t(val) {
+ TRACE_CTOR(balance_pair_t, "const string&");
+ }
+ explicit balance_pair_t(const char * val) : balance_t(val) {
+ TRACE_CTOR(balance_pair_t, "const char *");
+ }
+
+ /**
+ * Destructor.
+ */
+ virtual ~balance_pair_t() {
+ TRACE_DTOR(balance_pair_t);
+ }
+
+ /**
+ * Assignment and copy operators. A balance pair may be assigned or
+ * copied, and assigned or copied from a balance.
+ */
+ balance_pair_t(const balance_pair_t& bal_pair)
+ : balance_t(bal_pair), cost(bal_pair.cost) {
+ TRACE_CTOR(balance_pair_t, "copy");
+ }
+
+ balance_pair_t& operator=(const balance_pair_t& bal_pair) {
+ if (this != &bal_pair) {
+ balance_t::operator=(bal_pair.quantity());
+ cost = bal_pair.cost;
+ }
+ return *this;
+ }
+ balance_pair_t& operator=(const balance_t& bal) {
+ balance_t::operator=(bal);
+ return *this;
+ }
+ balance_pair_t& operator=(const amount_t& amt) {
+ balance_t::operator=(amt);
+ return *this;
+ }
+
+ balance_t& operator=(const string& str) {
+ return *this = balance_t(str);
+ }
+ balance_t& operator=(const char * str) {
+ return *this = balance_t(str);
+ }
+
+ /**
+ * Binary arithmetic operators. Balances support addition and
+ * subtraction of other balance pairs, balances or amounts, but
+ * multiplication and division are restricted to uncommoditized
+ * amounts only.
+ *
+ * There is also an additional additive method called `add' which
+ * allows for adding an amount and an associated cost
+ * simultaneously. The signature is:
+ * add(amount_t amount, optional<amount_t> cost)
+ */
+ balance_pair_t& operator+=(const balance_pair_t& bal_pair) {
+ balance_t::operator+=(bal_pair);
+ if (bal_pair.cost) {
+ if (! cost)
+ cost = quantity();
+ *cost += *bal_pair.cost;
+ }
+ return *this;
+ }
+ balance_pair_t& operator-=(const balance_pair_t& bal_pair) {
+ balance_t::operator+=(bal_pair);
+ if (bal_pair.cost) {
+ if (! cost)
+ cost = quantity();
+ *cost += *bal_pair.cost;
+ }
+ return *this;
+ }
+
+ virtual balance_pair_t& operator*=(const amount_t& amt) {
+ balance_t::operator*=(amt);
+ if (cost)
+ *cost *= amt;
+ return *this;
+ }
+
+ virtual balance_pair_t& operator/=(const amount_t& amt) {
+ balance_t::operator/=(amt);
+ if (cost)
+ *cost /= amt;
+ return *this;
+ }
+
+ balance_pair_t& add(const amount_t& amt,
+ const optional<amount_t>& a_cost = none) {
+ if (a_cost && ! cost)
+ cost = quantity();
+
+ *this += amt;
+
+ if (cost)
+ *cost += a_cost ? *a_cost : amt;
+
+ return *this;
+ }
+
+ /**
+ * Unary arithmetic operators. There are only a few unary methods
+ * supported for balance pairs (otherwise, the operators inherited
+ * from balance_t are used):
+ *
+ * abs() returns the absolute value of both the quantity and the
+ * cost of a balance pair.
+ *
+ * in_place_negate() negates all the amounts in both the quantity
+ * and the cost.
+ *
+ * in_place_reduce() reduces all the amounts in both the quantity
+ * and the cost.
+ *
+ * in_place_unreduce() unreduces all the amounts in both the
+ * quantity and the cost.
+ *
+ * quantity() returns the balance part of a balance. It is the same
+ * as doing a downcast<balance_t>(balance_pair).
+ */
+ balance_pair_t abs() const {
+ balance_t temp;
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ temp += i->second.abs();
+
+ if (cost) {
+ balance_t cost_temp;
+ for (amounts_map::const_iterator i = cost->amounts.begin();
+ i != cost->amounts.end();
+ i++)
+ cost_temp += i->second.abs();
+ return balance_pair_t(temp, cost_temp);
+ }
+ return temp;
+ }
+
+ virtual balance_t& in_place_negate() {
+ balance_t::in_place_negate();
+ if (cost)
+ cost->in_place_negate();
+ return *this;
+ }
+
+ virtual balance_t& in_place_reduce() {
+ // A temporary must be used here because reduction may cause
+ // multiple component amounts to collapse to the same commodity.
+ balance_t temp;
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ temp += i->second.reduce();
+
+ if (cost) {
+ balance_t cost_temp;
+ for (amounts_map::const_iterator i = cost->amounts.begin();
+ i != cost->amounts.end();
+ i++)
+ cost_temp += i->second.reduce();
+ return *this = balance_pair_t(temp, cost_temp);
+ }
+ return *this = temp;
+ }
+
+ virtual balance_t& in_place_unreduce() {
+ // A temporary must be used here because unreduction may cause
+ // multiple component amounts to collapse to the same commodity.
+ balance_t temp;
+ for (amounts_map::const_iterator i = amounts.begin();
+ i != amounts.end();
+ i++)
+ temp += i->second.unreduce();
+
+ if (cost) {
+ balance_t cost_temp;
+ for (amounts_map::const_iterator i = cost->amounts.begin();
+ i != cost->amounts.end();
+ i++)
+ cost_temp += i->second.unreduce();
+ return *this = balance_pair_t(temp, cost_temp);
+ }
+ return *this = temp;
+ }
+
+ balance_t& quantity() {
+ return *this;
+ }
+ const balance_t& quantity() const {
+ return *this;
+ }
+
+ /**
+ * Truth tests. An balance pair may be truth tested by comparison
+ * to another balance pair, or by using one of the inherited
+ * operators from balance_t.
+ */
+ bool operator==(const balance_pair_t& bal_pair) const {
+ if (quantity() != bal_pair.quantity())
+ return false;
+
+ if ((cost && ! bal_pair.cost) ||
+ (! cost && bal_pair.cost))
+ return false;
+
+ if (*cost != *bal_pair.cost)
+ return false;
+
+ return true;
+ }
+
+ bool operator==(const balance_t& bal) const {
+ return balance_t::operator==(bal);
+ }
+ bool operator==(const amount_t& amt) const {
+ return balance_t::operator==(amt);
+ }
+ template <typename T>
+ bool operator==(const T& val) const {
+ return balance_t::operator==(val);
+ }
+
+ /**
+ * Debugging methods. There is only one method specifically for
+ * balance pairs to help with debugging:
+ *
+ * valid() returns true if the balances within the balance pair are
+ * valid.
+ */
+ virtual bool valid() {
+ if (! balance_t::valid())
+ return false;
+
+ if (cost && ! cost->valid())
+ return false;
+
+ return true;
+ }
+};
+
+} // namespace ledger
+
+#endif // _BALPAIR_H
diff --git a/binary.cc b/binary.cc
index 9185f766..52d5f196 100644
--- a/binary.cc
+++ b/binary.cc
@@ -1,151 +1,71 @@
-#include "journal.h"
-#include "valexpr.h"
-#include "binary.h"
-
-#include <fstream>
-#include <sys/stat.h>
-
-#define TIMELOG_SUPPORT 1
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "utils.h"
namespace ledger {
+namespace binary {
-static unsigned long binary_magic_number = 0xFFEED765;
-#ifdef DEBUG_ENABLED
-static unsigned long format_version = 0x0002060b;
-#else
-static unsigned long format_version = 0x0002060a;
-#endif
-
-static account_t ** accounts;
-static account_t ** accounts_next;
-static unsigned int account_index;
-
-static commodity_base_t ** base_commodities;
-static commodity_base_t ** base_commodities_next;
-static unsigned int base_commodity_index;
-
-static commodity_t ** commodities;
-static commodity_t ** commodities_next;
-static unsigned int commodity_index;
-
-extern char * bigints;
-extern char * bigints_next;
-extern unsigned int bigints_index;
-extern unsigned int bigints_count;
-
-template <typename T>
-inline void read_binary_number_nocheck(std::istream& in, T& num) {
- in.read((char *)&num, sizeof(num));
-}
-
-template <typename T>
-inline T read_binary_number_nocheck(std::istream& in) {
- T num;
- read_binary_number_nocheck(in, num);
- return num;
-}
-
-template <typename T>
-inline void read_binary_number_nocheck(char *& data, T& num) {
- num = *((T *) data);
- data += sizeof(T);
-}
-
-template <typename T>
-inline T read_binary_number_nocheck(char *& data) {
- T num;
- read_binary_number_nocheck(data, num);
- return num;
-}
-
-#if DEBUG_LEVEL >= ALPHA
-static void assert_failed() {
- assert(0);
-}
-#define read_binary_guard(in, id) \
- if (read_binary_number_nocheck<unsigned short>(in) != id) \
- assert_failed();
-#else
-#define read_binary_guard(in, id)
-#endif
-
-template <typename T>
-inline void read_binary_number(std::istream& in, T& num) {
- read_binary_guard(in, 0x2003);
- in.read((char *)&num, sizeof(num));
- read_binary_guard(in, 0x2004);
-}
-
-inline void read_binary_bool(std::istream& in, bool& num) {
- read_binary_guard(in, 0x2005);
+void read_bool(std::istream& in, bool& num)
+{
+ read_guard(in, 0x2005);
unsigned char val;
- in.read((char *)&val, sizeof(val));
+ in.read(reinterpret_cast<char *>(&val), sizeof(val));
num = val == 1;
- read_binary_guard(in, 0x2006);
-}
-
-template <typename T>
-inline void read_binary_long(std::istream& in, T& num) {
- read_binary_guard(in, 0x2001);
-
- unsigned char len;
- read_binary_number_nocheck(in, len);
-
- num = 0;
- unsigned char temp;
- if (len > 3) {
- read_binary_number_nocheck(in, temp);
- num |= ((unsigned long)temp) << 24;
- }
- if (len > 2) {
- read_binary_number_nocheck(in, temp);
- num |= ((unsigned long)temp) << 16;
- }
- if (len > 1) {
- read_binary_number_nocheck(in, temp);
- num |= ((unsigned long)temp) << 8;
- }
-
- read_binary_number_nocheck(in, temp);
- num |= ((unsigned long)temp);
-
- read_binary_guard(in, 0x2002);
-}
-
-template <typename T>
-inline T read_binary_number(std::istream& in) {
- T num;
- read_binary_number(in, num);
- return num;
+ read_guard(in, 0x2006);
}
-inline bool read_binary_bool(std::istream& in) {
- bool num;
- read_binary_bool(in, num);
- return num;
-}
-
-template <typename T>
-inline T read_binary_long(std::istream& in) {
- T num;
- read_binary_long(in, num);
- return num;
+void read_bool(const char *& data, bool& num)
+{
+ read_guard(data, 0x2005);
+ const unsigned char val = *reinterpret_cast<const unsigned char *>(data);
+ data += sizeof(unsigned char);
+ num = val == 1;
+ read_guard(data, 0x2006);
}
-inline void read_binary_string(std::istream& in, std::string& str)
+void read_string(std::istream& in, string& str)
{
- read_binary_guard(in, 0x3001);
+ read_guard(in, 0x3001);
unsigned char len;
- read_binary_number_nocheck(in, len);
+ read_number_nocheck(in, len);
if (len == 0xff) {
unsigned short slen;
- read_binary_number_nocheck(in, slen);
+ read_number_nocheck(in, slen);
char * buf = new char[slen + 1];
in.read(buf, slen);
buf[slen] = '\0';
str = buf;
- delete[] buf;
+ checked_array_delete(buf);
}
else if (len) {
char buf[256];
@@ -156,1183 +76,82 @@ inline void read_binary_string(std::istream& in, std::string& str)
str = "";
}
- read_binary_guard(in, 0x3002);
-}
-
-inline std::string read_binary_string(std::istream& in) {
- std::string temp;
- read_binary_string(in, temp);
- return temp;
-}
-
-template <typename T>
-inline void read_binary_number(char *& data, T& num) {
- read_binary_guard(data, 0x2003);
- num = *((T *) data);
- data += sizeof(T);
- read_binary_guard(data, 0x2004);
+ read_guard(in, 0x3002);
}
-inline void read_binary_bool(char *& data, bool& num) {
- read_binary_guard(data, 0x2005);
- unsigned char val = *((unsigned char *) data);
- data += sizeof(unsigned char);
- num = val == 1;
- read_binary_guard(data, 0x2006);
-}
-
-template <typename T>
-inline void read_binary_long(char *& data, T& num) {
- read_binary_guard(data, 0x2001);
-
- unsigned char len;
- read_binary_number_nocheck(data, len);
-
- num = 0;
- unsigned char temp;
- if (len > 3) {
- read_binary_number_nocheck(data, temp);
- num |= ((unsigned long)temp) << 24;
- }
- if (len > 2) {
- read_binary_number_nocheck(data, temp);
- num |= ((unsigned long)temp) << 16;
- }
- if (len > 1) {
- read_binary_number_nocheck(data, temp);
- num |= ((unsigned long)temp) << 8;
- }
-
- read_binary_number_nocheck(data, temp);
- num |= ((unsigned long)temp);
-
- read_binary_guard(data, 0x2002);
-}
-
-template <typename T>
-inline T read_binary_number(char *& data) {
- T num;
- read_binary_number(data, num);
- return num;
-}
-
-inline bool read_binary_bool(char *& data) {
- bool num;
- read_binary_bool(data, num);
- return num;
-}
-
-template <typename T>
-inline T read_binary_long(char *& data) {
- T num;
- read_binary_long(data, num);
- return num;
-}
-
-inline void read_binary_string(char *& data, std::string& str)
+void read_string(const char *& data, string& str)
{
- read_binary_guard(data, 0x3001);
+ read_guard(data, 0x3001);
unsigned char len;
- read_binary_number_nocheck(data, len);
+ read_number_nocheck(data, len);
if (len == 0xff) {
unsigned short slen;
- read_binary_number_nocheck(data, slen);
- str = std::string(data, slen);
+ read_number_nocheck(data, slen);
+ str = string(data, slen);
data += slen;
}
else if (len) {
- str = std::string(data, len);
+ str = string(data, len);
data += len;
}
else {
str = "";
}
- read_binary_guard(data, 0x3002);
-}
-
-inline std::string read_binary_string(char *& data)
-{
- std::string temp;
- read_binary_string(data, temp);
- return temp;
+ read_guard(data, 0x3002);
}
-inline void read_binary_string(char *& data, std::string * str)
+void read_string(const char *& data, string * str)
{
- read_binary_guard(data, 0x3001);
+ read_guard(data, 0x3001);
unsigned char len;
- read_binary_number_nocheck(data, len);
+ read_number_nocheck(data, len);
if (len == 0xff) {
unsigned short slen;
- read_binary_number_nocheck(data, slen);
- new(str) std::string(data, slen);
+ read_number_nocheck(data, slen);
+ new(str) string(data, slen);
data += slen;
}
else if (len) {
- new(str) std::string(data, len);
+ new(str) string(data, len);
data += len;
}
else {
- new(str) std::string("");
+ new(str) string("");
}
- read_binary_guard(data, 0x3002);
+ read_guard(data, 0x3002);
}
-inline void read_binary_amount(char *& data, amount_t& amt)
-{
- commodity_t::ident_t ident;
- read_binary_long(data, ident);
- if (ident == 0xffffffff)
- amt.commodity_ = NULL;
- else if (ident == 0)
- amt.commodity_ = commodity_t::null_commodity;
- else
- amt.commodity_ = commodities[ident - 1];
-
- amt.read_quantity(data);
-}
-inline void read_binary_value(char *& data, value_t& val)
+void write_bool(std::ostream& out, bool num)
{
- val.type = static_cast<value_t::type_t>(read_binary_long<int>(data));
-
- switch (val.type) {
- case value_t::BOOLEAN:
- read_binary_bool(data, *((bool *) val.data));
- break;
- case value_t::INTEGER:
- read_binary_long(data, *((long *) val.data));
- break;
- case value_t::DATETIME:
- read_binary_number(data, *((datetime_t *) val.data));
- break;
- case value_t::AMOUNT:
- read_binary_amount(data, *((amount_t *) val.data));
- break;
-
- case value_t::BALANCE:
- case value_t::BALANCE_PAIR:
- assert(0);
- break;
- }
-}
-
-inline void read_binary_mask(char *& data, mask_t *& mask)
-{
- bool exclude;
- read_binary_number(data, exclude);
- std::string pattern;
- read_binary_string(data, pattern);
-
- mask = new mask_t(pattern);
- mask->exclude = exclude;
-}
-
-inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
-{
- if (! read_binary_bool(data)) {
- expr = NULL;
- return;
- }
-
- value_expr_t::kind_t kind;
- read_binary_number(data, kind);
-
- expr = new value_expr_t(kind);
-
- if (kind > value_expr_t::TERMINALS) {
- read_binary_value_expr(data, expr->left);
- if (expr->left) expr->left->acquire();
- }
-
- switch (expr->kind) {
- case value_expr_t::O_ARG:
- case value_expr_t::INDEX:
- read_binary_long(data, expr->arg_index);
- break;
- case value_expr_t::CONSTANT:
- expr->value = new value_t;
- read_binary_value(data, *expr->value);
- break;
-
- case value_expr_t::F_CODE_MASK:
- case value_expr_t::F_PAYEE_MASK:
- case value_expr_t::F_NOTE_MASK:
- case value_expr_t::F_ACCOUNT_MASK:
- case value_expr_t::F_SHORT_ACCOUNT_MASK:
- case value_expr_t::F_COMMODITY_MASK:
- if (read_binary_bool(data))
- read_binary_mask(data, expr->mask);
- break;
-
- default:
- if (kind > value_expr_t::TERMINALS) {
- read_binary_value_expr(data, expr->right);
- if (expr->right) expr->right->acquire();
- }
- break;
- }
-}
-
-
-inline void read_binary_transaction(char *& data, transaction_t * xact)
-{
- read_binary_number(data, xact->_date);
- read_binary_number(data, xact->_date_eff);
- xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1];
-
- unsigned char flag = read_binary_number<unsigned char>(data);
- if (flag == 0) {
- read_binary_amount(data, xact->amount);
- }
- else if (flag == 1) {
- read_binary_amount(data, xact->amount);
- read_binary_string(data, xact->amount_expr.expr);
- }
- else {
- value_expr_t * ptr = NULL;
- read_binary_value_expr(data, ptr);
- assert(ptr);
- xact->amount_expr.reset(ptr);
- read_binary_string(data, xact->amount_expr.expr);
- }
-
- if (read_binary_bool(data)) {
- xact->cost = new amount_t;
- read_binary_amount(data, *xact->cost);
- read_binary_string(data, xact->cost_expr);
- } else {
- xact->cost = NULL;
- }
-
- read_binary_number(data, xact->state);
- read_binary_number(data, xact->flags);
- xact->flags |= TRANSACTION_BULK_ALLOC;
- read_binary_string(data, &xact->note);
-
- xact->beg_pos = read_binary_long<unsigned long>(data);
- read_binary_long(data, xact->beg_line);
- xact->end_pos = read_binary_long<unsigned long>(data);
- read_binary_long(data, xact->end_line);
-
- xact->data = NULL;
-
- if (xact->amount_expr)
- compute_amount(xact->amount_expr, xact->amount, xact);
-}
-
-inline void read_binary_entry_base(char *& data, entry_base_t * entry,
- transaction_t *& xact_pool, bool& finalize)
-{
- read_binary_long(data, entry->src_idx);
- entry->beg_pos = read_binary_long<unsigned long>(data);
- read_binary_long(data, entry->beg_line);
- entry->end_pos = read_binary_long<unsigned long>(data);
- read_binary_long(data, entry->end_line);
-
- bool ignore_calculated = read_binary_bool(data);
-
- for (unsigned long i = 0, count = read_binary_long<unsigned long>(data);
- i < count;
- i++) {
- new(xact_pool) transaction_t;
- read_binary_transaction(data, xact_pool);
- if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED)
- finalize = true;
- entry->add_transaction(xact_pool++);
- }
-}
-
-inline void read_binary_entry(char *& data, entry_t * entry,
- transaction_t *& xact_pool, bool& finalize)
-{
- read_binary_entry_base(data, entry, xact_pool, finalize);
- read_binary_number(data, entry->_date);
- read_binary_number(data, entry->_date_eff);
- read_binary_string(data, &entry->code);
- read_binary_string(data, &entry->payee);
-}
-
-inline void read_binary_auto_entry(char *& data, auto_entry_t * entry,
- transaction_t *& xact_pool)
-{
- bool ignore;
- read_binary_entry_base(data, entry, xact_pool, ignore);
- value_expr_t * expr;
- read_binary_value_expr(data, expr);
- // the item_predicate constructor will acquire the reference
- entry->predicate = new item_predicate<transaction_t>(expr);
-}
-
-inline void read_binary_period_entry(char *& data, period_entry_t * entry,
- transaction_t *& xact_pool, bool& finalize)
-{
- read_binary_entry_base(data, entry, xact_pool, finalize);
- read_binary_string(data, &entry->period_string);
- std::istringstream stream(entry->period_string);
- entry->period.parse(stream);
-}
-
-inline commodity_base_t * read_binary_commodity_base(char *& data)
-{
- commodity_base_t * commodity = new commodity_base_t;
- *base_commodities_next++ = commodity;
-
- read_binary_string(data, commodity->symbol);
- read_binary_string(data, commodity->name);
- read_binary_string(data, commodity->note);
- read_binary_number(data, commodity->precision);
- read_binary_number(data, commodity->flags);
-
- return commodity;
-}
-
-inline void read_binary_commodity_base_extra(char *& data,
- commodity_t::ident_t ident)
-{
- commodity_base_t * commodity = base_commodities[ident];
-
- bool read_history = false;
- for (unsigned long i = 0, count = read_binary_long<unsigned long>(data);
- i < count;
- i++) {
- datetime_t when;
- read_binary_number(data, when);
- amount_t amt;
- read_binary_amount(data, amt);
-
- // Upon insertion, amt will be copied, which will cause the amount
- // to be duplicated (and thus not lost when the journal's
- // item_pool is deleted).
- if (! commodity->history)
- commodity->history = new commodity_base_t::history_t;
- commodity->history->prices.insert(history_pair(when, amt));
-
- read_history = true;
- }
- if (read_history)
- read_binary_number(data, commodity->history->last_lookup);
-
- if (read_binary_bool(data)) {
- amount_t amt;
- read_binary_amount(data, amt);
- commodity->smaller = new amount_t(amt);
- }
-
- if (read_binary_bool(data)) {
- amount_t amt;
- read_binary_amount(data, amt);
- commodity->larger = new amount_t(amt);
- }
-}
-
-inline commodity_t * read_binary_commodity(char *& data)
-{
- commodity_t * commodity = new commodity_t;
- *commodities_next++ = commodity;
-
- commodity->base =
- base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
-
- read_binary_string(data, commodity->qualified_symbol);
- commodity->annotated = false;
-
- return commodity;
-}
-
-inline commodity_t * read_binary_commodity_annotated(char *& data)
-{
- annotated_commodity_t * commodity = new annotated_commodity_t;
- *commodities_next++ = commodity;
-
- commodity->base =
- base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
-
- read_binary_string(data, commodity->qualified_symbol);
- commodity->annotated = true;
-
- commodity->ptr =
- commodities[read_binary_long<commodity_t::ident_t>(data) - 1];
-
- // This read-and-then-assign causes a new amount to be allocated
- // which does not live within the bulk allocation pool, since that
- // pool will be deleted *before* the commodities are destroyed.
- amount_t amt;
- read_binary_amount(data, amt);
- commodity->price = amt;
-
- read_binary_number(data, commodity->date);
- read_binary_string(data, commodity->tag);
-
- return commodity;
-}
-
-inline
-account_t * read_binary_account(char *& data, journal_t * journal,
- account_t * master = NULL)
-{
- account_t * acct = new account_t(NULL);
- *accounts_next++ = acct;
-
- acct->journal = journal;
-
- account_t::ident_t id;
- read_binary_long(data, id); // parent id
- if (id == 0xffffffff)
- acct->parent = NULL;
- else
- acct->parent = accounts[id - 1];
-
- read_binary_string(data, acct->name);
- read_binary_string(data, acct->note);
- read_binary_number(data, acct->depth);
-
- // If all of the subaccounts will be added to a different master
- // account, throw away what we've learned about the recorded
- // journal's own master account.
-
- if (master && acct != master) {
- delete acct;
- acct = master;
- }
-
- for (account_t::ident_t i = 0,
- count = read_binary_long<account_t::ident_t>(data);
- i < count;
- i++) {
- account_t * child = read_binary_account(data, journal);
- child->parent = acct;
- assert(acct != child);
- acct->add_account(child);
- }
-
- return acct;
-}
-
-unsigned int read_binary_journal(std::istream& in,
- const std::string& file,
- journal_t * journal,
- account_t * master)
-{
- account_index =
- base_commodity_index =
- commodity_index = 0;
-
- // Read in the files that participated in this journal, so that they
- // can be checked for changes on reading.
-
- if (! file.empty()) {
- for (unsigned short i = 0,
- count = read_binary_number<unsigned short>(in);
- i < count;
- i++) {
- std::string path = read_binary_string(in);
- std::time_t old_mtime;
- read_binary_number(in, old_mtime);
- struct stat info;
- stat(path.c_str(), &info);
- if (std::difftime(info.st_mtime, old_mtime) > 0)
- return 0;
-
- journal->sources.push_back(path);
- }
-
- // Make sure that the cache uses the same price database,
- // otherwise it means that LEDGER_PRICE_DB has been changed, and
- // we should ignore this cache file.
- if (read_binary_string(in) != journal->price_db)
- return 0;
- }
-
- // Read all of the data in at once, so that we're just dealing with
- // a big data buffer.
-
- unsigned long data_size = read_binary_number<unsigned long>(in);
-
- char * data_pool = new char[data_size];
- char * data = data_pool;
- in.read(data, data_size);
-
- // Read in the accounts
-
- account_t::ident_t a_count = read_binary_long<account_t::ident_t>(data);
- accounts = accounts_next = new account_t *[a_count];
-
- assert(journal->master);
- delete journal->master;
- journal->master = read_binary_account(data, journal, master);
-
- if (read_binary_bool(data))
- journal->basket = accounts[read_binary_long<account_t::ident_t>(data) - 1];
-
- // Allocate the memory needed for the entries and transactions in
- // one large block, which is then chopped up and custom constructed
- // as necessary.
-
- unsigned long count = read_binary_long<unsigned long>(data);
- unsigned long auto_count = read_binary_long<unsigned long>(data);
- unsigned long period_count = read_binary_long<unsigned long>(data);
- unsigned long xact_count = read_binary_number<unsigned long>(data);
- unsigned long bigint_count = read_binary_number<unsigned long>(data);
-
- std::size_t pool_size = (sizeof(entry_t) * count +
- sizeof(transaction_t) * xact_count +
- sizeof_bigint_t() * bigint_count);
-
- char * item_pool = new char[pool_size];
-
- journal->item_pool = item_pool;
- journal->item_pool_end = item_pool + pool_size;
-
- entry_t * entry_pool = (entry_t *) item_pool;
- transaction_t * xact_pool = (transaction_t *) (item_pool +
- sizeof(entry_t) * count);
- bigints_index = 0;
- bigints = bigints_next = (item_pool + sizeof(entry_t) * count +
- sizeof(transaction_t) * xact_count);
-
- // Read in the base commodities and then derived commodities
-
- commodity_base_t::ident_t bc_count =
- read_binary_long<commodity_base_t::ident_t>(data);
- base_commodities = base_commodities_next = new commodity_base_t *[bc_count];
-
- for (commodity_base_t::ident_t i = 0; i < bc_count; i++) {
- commodity_base_t * commodity = read_binary_commodity_base(data);
-
- std::pair<base_commodities_map::iterator, bool> result =
- commodity_base_t::commodities.insert
- (base_commodities_pair(commodity->symbol, commodity));
- if (! result.second) {
- base_commodities_map::iterator c =
- commodity_base_t::commodities.find(commodity->symbol);
-
- // It's possible the user might have used a commodity in a value
- // expression passed to an option, we'll just override the
- // flags, but keep the commodity pointer intact.
- if (c == commodity_base_t::commodities.end())
- throw new error(std::string("Failed to read base commodity from cache: ") +
- commodity->symbol);
-
- (*c).second->name = commodity->name;
- (*c).second->note = commodity->note;
- (*c).second->precision = commodity->precision;
- (*c).second->flags = commodity->flags;
- if ((*c).second->smaller)
- delete (*c).second->smaller;
- (*c).second->smaller = commodity->smaller;
- if ((*c).second->larger)
- delete (*c).second->larger;
- (*c).second->larger = commodity->larger;
-
- *(base_commodities_next - 1) = (*c).second;
- delete commodity;
- }
- }
-
- commodity_t::ident_t c_count = read_binary_long<commodity_t::ident_t>(data);
- commodities = commodities_next = new commodity_t *[c_count];
-
- for (commodity_t::ident_t i = 0; i < c_count; i++) {
- commodity_t * commodity;
- std::string mapping_key;
-
- if (! read_binary_bool(data)) {
- commodity = read_binary_commodity(data);
- mapping_key = commodity->base->symbol;
- } else {
- read_binary_string(data, mapping_key);
- commodity = read_binary_commodity_annotated(data);
- }
-
- std::pair<commodities_map::iterator, bool> result =
- commodity_t::commodities.insert(commodities_pair
- (mapping_key, commodity));
- if (! result.second) {
- commodities_map::iterator c =
- commodity_t::commodities.find(mapping_key);
- if (c == commodity_t::commodities.end())
- throw new error(std::string("Failed to read commodity from cache: ") +
- commodity->symbol());
-
- *(commodities_next - 1) = (*c).second;
- delete commodity;
- }
- }
-
- for (commodity_base_t::ident_t i = 0; i < bc_count; i++)
- read_binary_commodity_base_extra(data, i);
-
- commodity_t::ident_t ident;
- read_binary_long(data, ident);
- if (ident == 0xffffffff || ident == 0)
- commodity_t::default_commodity = NULL;
- else
- commodity_t::default_commodity = commodities[ident - 1];
-
- // Read in the entries and transactions
-
- for (unsigned long i = 0; i < count; i++) {
- new(entry_pool) entry_t;
- bool finalize = false;
- read_binary_entry(data, entry_pool, xact_pool, finalize);
- entry_pool->journal = journal;
- if (finalize && ! entry_pool->finalize())
- continue;
- journal->entries.push_back(entry_pool++);
- }
-
- for (unsigned long i = 0; i < auto_count; i++) {
- auto_entry_t * auto_entry = new auto_entry_t;
- read_binary_auto_entry(data, auto_entry, xact_pool);
- auto_entry->journal = journal;
- journal->auto_entries.push_back(auto_entry);
- }
-
- for (unsigned long i = 0; i < period_count; i++) {
- period_entry_t * period_entry = new period_entry_t;
- bool finalize = false;
- read_binary_period_entry(data, period_entry, xact_pool, finalize);
- period_entry->journal = journal;
- if (finalize && ! period_entry->finalize())
- continue;
- journal->period_entries.push_back(period_entry);
- }
-
- // Clean up and return the number of entries read
-
- delete[] accounts;
- delete[] commodities;
- delete[] data_pool;
-
- VALIDATE(journal->valid());
-
- return count;
-}
-
-bool binary_parser_t::test(std::istream& in) const
-{
- if (read_binary_number_nocheck<unsigned long>(in) == binary_magic_number &&
- read_binary_number_nocheck<unsigned long>(in) == format_version)
- return true;
-
- in.clear();
- in.seekg(0, std::ios::beg);
- return false;
-}
-
-unsigned int binary_parser_t::parse(std::istream& in,
- config_t& config,
- journal_t * journal,
- account_t * master,
- const std::string * original_file)
-{
- return read_binary_journal(in, original_file ? *original_file : "",
- journal, master);
-}
-
-template <typename T>
-inline void write_binary_number_nocheck(std::ostream& out, T num) {
- out.write((char *)&num, sizeof(num));
-}
-
-#if DEBUG_LEVEL >= ALPHA
-#define write_binary_guard(out, id) \
- write_binary_number_nocheck<unsigned short>(out, id)
-#else
-#define write_binary_guard(in, id)
-#endif
-
-template <typename T>
-inline void write_binary_number(std::ostream& out, T num) {
- write_binary_guard(out, 0x2003);
- out.write((char *)&num, sizeof(num));
- write_binary_guard(out, 0x2004);
-}
-
-inline void write_binary_bool(std::ostream& out, bool num) {
- write_binary_guard(out, 0x2005);
+ write_guard(out, 0x2005);
unsigned char val = num ? 1 : 0;
- out.write((char *)&val, sizeof(val));
- write_binary_guard(out, 0x2006);
-}
-
-template <typename T>
-inline void write_binary_long(std::ostream& out, T num) {
- write_binary_guard(out, 0x2001);
-
- unsigned char len = 4;
- if (((unsigned long)num) < 0x00000100UL)
- len = 1;
- else if (((unsigned long)num) < 0x00010000UL)
- len = 2;
- else if (((unsigned long)num) < 0x01000000UL)
- len = 3;
- write_binary_number_nocheck<unsigned char>(out, len);
-
- unsigned char temp;
- if (len > 3) {
- temp = (((unsigned long)num) & 0xFF000000UL) >> 24;
- write_binary_number_nocheck(out, temp);
- }
- if (len > 2) {
- temp = (((unsigned long)num) & 0x00FF0000UL) >> 16;
- write_binary_number_nocheck(out, temp);
- }
- if (len > 1) {
- temp = (((unsigned long)num) & 0x0000FF00UL) >> 8;
- write_binary_number_nocheck(out, temp);
- }
-
- temp = (((unsigned long)num) & 0x000000FFUL);
- write_binary_number_nocheck(out, temp);
-
- write_binary_guard(out, 0x2002);
+ out.write(reinterpret_cast<char *>(&val), sizeof(val));
+ write_guard(out, 0x2006);
}
-inline void write_binary_string(std::ostream& out, const std::string& str)
+void write_string(std::ostream& out, const string& str)
{
- write_binary_guard(out, 0x3001);
+ write_guard(out, 0x3001);
unsigned long len = str.length();
if (len > 255) {
assert(len < 65536);
- write_binary_number_nocheck<unsigned char>(out, 0xff);
- write_binary_number_nocheck<unsigned short>(out, len);
+ write_number_nocheck<unsigned char>(out, 0xff);
+ write_number_nocheck<unsigned short>(out, len);
} else {
- write_binary_number_nocheck<unsigned char>(out, len);
+ write_number_nocheck<unsigned char>(out, len);
}
if (len)
out.write(str.c_str(), len);
- write_binary_guard(out, 0x3002);
-}
-
-void write_binary_amount(std::ostream& out, const amount_t& amt)
-{
- if (amt.commodity_)
- write_binary_long(out, amt.commodity_->ident);
- else
- write_binary_long<commodity_t::ident_t>(out, 0xffffffff);
-
- amt.write_quantity(out);
-}
-
-void write_binary_value(std::ostream& out, const value_t& val)
-{
- write_binary_long(out, (int)val.type);
-
- switch (val.type) {
- case value_t::BOOLEAN:
- write_binary_bool(out, *((bool *) val.data));
- break;
- case value_t::INTEGER:
- write_binary_long(out, *((long *) val.data));
- break;
- case value_t::DATETIME:
- write_binary_number(out, *((datetime_t *) val.data));
- break;
- case value_t::AMOUNT:
- write_binary_amount(out, *((amount_t *) val.data));
- break;
-
- case value_t::BALANCE:
- case value_t::BALANCE_PAIR:
- throw new error("Cannot write a balance to the binary cache");
- }
-}
-
-void write_binary_mask(std::ostream& out, mask_t * mask)
-{
- write_binary_number(out, mask->exclude);
- write_binary_string(out, mask->pattern);
-}
-
-void write_binary_value_expr(std::ostream& out, const value_expr_t * expr)
-{
- if (! expr) {
- write_binary_bool(out, false);
- return;
- }
- write_binary_bool(out, true);
- write_binary_number(out, expr->kind);
-
- if (expr->kind > value_expr_t::TERMINALS)
- write_binary_value_expr(out, expr->left);
-
- switch (expr->kind) {
- case value_expr_t::O_ARG:
- case value_expr_t::INDEX:
- write_binary_long(out, expr->arg_index);
- break;
- case value_expr_t::CONSTANT:
- write_binary_value(out, *expr->value);
- break;
-
- case value_expr_t::F_CODE_MASK:
- case value_expr_t::F_PAYEE_MASK:
- case value_expr_t::F_NOTE_MASK:
- case value_expr_t::F_ACCOUNT_MASK:
- case value_expr_t::F_SHORT_ACCOUNT_MASK:
- case value_expr_t::F_COMMODITY_MASK:
- if (expr->mask) {
- write_binary_bool(out, true);
- write_binary_mask(out, expr->mask);
- } else {
- write_binary_bool(out, false);
- }
- break;
-
- default:
- if (expr->kind > value_expr_t::TERMINALS)
- write_binary_value_expr(out, expr->right);
- break;
- }
-
-}
-
-void write_binary_transaction(std::ostream& out, transaction_t * xact,
- bool ignore_calculated)
-{
- write_binary_number(out, xact->_date);
- write_binary_number(out, xact->_date_eff);
- write_binary_long(out, xact->account->ident);
-
- if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) {
- write_binary_number<unsigned char>(out, 0);
- write_binary_amount(out, amount_t());
- }
- else if (xact->amount_expr) {
- write_binary_number<unsigned char>(out, 2);
- write_binary_value_expr(out, xact->amount_expr.get());
- write_binary_string(out, xact->amount_expr.expr);
- }
- else if (! xact->amount_expr.expr.empty()) {
- write_binary_number<unsigned char>(out, 1);
- write_binary_amount(out, xact->amount);
- write_binary_string(out, xact->amount_expr.expr);
- }
- else {
- write_binary_number<unsigned char>(out, 0);
- write_binary_amount(out, xact->amount);
- }
-
- if (xact->cost &&
- (! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
- write_binary_bool(out, true);
- write_binary_amount(out, *xact->cost);
- write_binary_string(out, xact->cost_expr);
- } else {
- write_binary_bool(out, false);
- }
-
- write_binary_number(out, xact->state);
- write_binary_number(out, xact->flags);
- write_binary_string(out, xact->note);
-
- write_binary_long(out, xact->beg_pos);
- write_binary_long(out, xact->beg_line);
- write_binary_long(out, xact->end_pos);
- write_binary_long(out, xact->end_line);
-}
-
-void write_binary_entry_base(std::ostream& out, entry_base_t * entry)
-{
- write_binary_long(out, entry->src_idx);
- write_binary_long(out, entry->beg_pos);
- write_binary_long(out, entry->beg_line);
- write_binary_long(out, entry->end_pos);
- write_binary_long(out, entry->end_line);
-
- bool ignore_calculated = false;
- for (transactions_list::const_iterator i = entry->transactions.begin();
- i != entry->transactions.end();
- i++)
- if ((*i)->amount_expr) {
- ignore_calculated = true;
- break;
- }
-
- write_binary_bool(out, ignore_calculated);
-
- write_binary_long(out, entry->transactions.size());
- for (transactions_list::const_iterator i = entry->transactions.begin();
- i != entry->transactions.end();
- i++)
- write_binary_transaction(out, *i, ignore_calculated);
-}
-
-void write_binary_entry(std::ostream& out, entry_t * entry)
-{
- write_binary_entry_base(out, entry);
- write_binary_number(out, entry->_date);
- write_binary_number(out, entry->_date_eff);
- write_binary_string(out, entry->code);
- write_binary_string(out, entry->payee);
-}
-
-void write_binary_auto_entry(std::ostream& out, auto_entry_t * entry)
-{
- write_binary_entry_base(out, entry);
- write_binary_value_expr(out, entry->predicate->predicate);
-}
-
-void write_binary_period_entry(std::ostream& out, period_entry_t * entry)
-{
- write_binary_entry_base(out, entry);
- write_binary_string(out, entry->period_string);
-}
-
-void write_binary_commodity_base(std::ostream& out, commodity_base_t * commodity)
-{
- commodity->ident = ++base_commodity_index;
-
- write_binary_string(out, commodity->symbol);
- write_binary_string(out, commodity->name);
- write_binary_string(out, commodity->note);
- write_binary_number(out, commodity->precision);
- write_binary_number(out, commodity->flags);
-}
-
-void write_binary_commodity_base_extra(std::ostream& out,
- commodity_base_t * commodity)
-{
- if (commodity->history && commodity->history->bogus_time)
- commodity->remove_price(commodity->history->bogus_time);
-
- if (! commodity->history) {
- write_binary_long<unsigned long>(out, 0);
- } else {
- write_binary_long<unsigned long>(out, commodity->history->prices.size());
- for (history_map::const_iterator i = commodity->history->prices.begin();
- i != commodity->history->prices.end();
- i++) {
- write_binary_number(out, (*i).first);
- write_binary_amount(out, (*i).second);
- }
- write_binary_number(out, commodity->history->last_lookup);
- }
-
- if (commodity->smaller) {
- write_binary_bool(out, true);
- write_binary_amount(out, *commodity->smaller);
- } else {
- write_binary_bool(out, false);
- }
-
- if (commodity->larger) {
- write_binary_bool(out, true);
- write_binary_amount(out, *commodity->larger);
- } else {
- write_binary_bool(out, false);
- }
-}
-
-void write_binary_commodity(std::ostream& out, commodity_t * commodity)
-{
- commodity->ident = ++commodity_index;
-
- write_binary_long(out, commodity->base->ident);
- write_binary_string(out, commodity->qualified_symbol);
-}
-
-void write_binary_commodity_annotated(std::ostream& out,
- commodity_t * commodity)
-{
- commodity->ident = ++commodity_index;
-
- write_binary_long(out, commodity->base->ident);
- write_binary_string(out, commodity->qualified_symbol);
-
- annotated_commodity_t * ann_comm =
- static_cast<annotated_commodity_t *>(commodity);
-
- write_binary_long(out, ann_comm->base->ident);
- write_binary_amount(out, ann_comm->price);
- write_binary_number(out, ann_comm->date);
- write_binary_string(out, ann_comm->tag);
-}
-
-static inline account_t::ident_t count_accounts(account_t * account)
-{
- account_t::ident_t count = 1;
-
- for (accounts_map::iterator i = account->accounts.begin();
- i != account->accounts.end();
- i++)
- count += count_accounts((*i).second);
-
- return count;
-}
-
-void write_binary_account(std::ostream& out, account_t * account)
-{
- account->ident = ++account_index;
-
- if (account->parent)
- write_binary_long(out, account->parent->ident);
- else
- write_binary_long<account_t::ident_t>(out, 0xffffffff);
-
- write_binary_string(out, account->name);
- write_binary_string(out, account->note);
- write_binary_number(out, account->depth);
-
- write_binary_long<account_t::ident_t>(out, account->accounts.size());
- for (accounts_map::iterator i = account->accounts.begin();
- i != account->accounts.end();
- i++)
- write_binary_account(out, (*i).second);
-}
-
-void write_binary_journal(std::ostream& out, journal_t * journal)
-{
- account_index =
- base_commodity_index =
- commodity_index = 0;
-
- write_binary_number_nocheck(out, binary_magic_number);
- write_binary_number_nocheck(out, format_version);
-
- // Write out the files that participated in this journal, so that
- // they can be checked for changes on reading.
-
- if (journal->sources.empty()) {
- write_binary_number<unsigned short>(out, 0);
- } else {
- write_binary_number<unsigned short>(out, journal->sources.size());
- for (strings_list::const_iterator i = journal->sources.begin();
- i != journal->sources.end();
- i++) {
- write_binary_string(out, *i);
- struct stat info;
- stat((*i).c_str(), &info);
- write_binary_number(out, std::time_t(info.st_mtime));
- }
-
- // Write out the price database that relates to this data file, so
- // that if it ever changes the cache can be invalidated.
- write_binary_string(out, journal->price_db);
- }
-
- ostream_pos_type data_val = out.tellp();
- write_binary_number<unsigned long>(out, 0);
-
- // Write out the accounts
-
- write_binary_long<account_t::ident_t>(out, count_accounts(journal->master));
- write_binary_account(out, journal->master);
-
- if (journal->basket) {
- write_binary_bool(out, true);
- write_binary_long(out, journal->basket->ident);
- } else {
- write_binary_bool(out, false);
- }
-
- // Write out the number of entries, transactions, and amounts
-
- write_binary_long<unsigned long>(out, journal->entries.size());
- write_binary_long<unsigned long>(out, journal->auto_entries.size());
- write_binary_long<unsigned long>(out, journal->period_entries.size());
-
- ostream_pos_type xacts_val = out.tellp();
- write_binary_number<unsigned long>(out, 0);
-
- ostream_pos_type bigints_val = out.tellp();
- write_binary_number<unsigned long>(out, 0);
-
- bigints_count = 0;
-
- // Write out the commodities
-
- write_binary_long<commodity_t::ident_t>
- (out, commodity_base_t::commodities.size());
-
- for (base_commodities_map::const_iterator i =
- commodity_base_t::commodities.begin();
- i != commodity_base_t::commodities.end();
- i++)
- write_binary_commodity_base(out, (*i).second);
-
- write_binary_long<commodity_t::ident_t>
- (out, commodity_t::commodities.size());
-
- for (commodities_map::const_iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
- i++) {
- if (! (*i).second->annotated) {
- write_binary_bool(out, false);
- write_binary_commodity(out, (*i).second);
- }
- }
-
- for (commodities_map::const_iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
- i++) {
- if ((*i).second->annotated) {
- write_binary_bool(out, true);
- write_binary_string(out, (*i).first); // the mapping key
- write_binary_commodity_annotated(out, (*i).second);
- }
- }
-
- // Write out the history and smaller/larger convertible links after
- // both the base and the main commodities have been written, since
- // the amounts in both will refer to the mains.
-
- for (base_commodities_map::const_iterator i =
- commodity_base_t::commodities.begin();
- i != commodity_base_t::commodities.end();
- i++)
- write_binary_commodity_base_extra(out, (*i).second);
-
- if (commodity_t::default_commodity)
- write_binary_long(out, commodity_t::default_commodity->ident);
- else
- write_binary_long<commodity_t::ident_t>(out, 0xffffffff);
-
- // Write out the entries and transactions
-
- unsigned long xact_count = 0;
-
- for (entries_list::const_iterator i = journal->entries.begin();
- i != journal->entries.end();
- i++) {
- write_binary_entry(out, *i);
- xact_count += (*i)->transactions.size();
- }
-
- for (auto_entries_list::const_iterator i = journal->auto_entries.begin();
- i != journal->auto_entries.end();
- i++) {
- write_binary_auto_entry(out, *i);
- xact_count += (*i)->transactions.size();
- }
-
- for (period_entries_list::const_iterator i = journal->period_entries.begin();
- i != journal->period_entries.end();
- i++) {
- write_binary_period_entry(out, *i);
- xact_count += (*i)->transactions.size();
- }
-
- // Back-patch the count for amounts
-
- unsigned long data_size = (((unsigned long) out.tellp()) -
- ((unsigned long) data_val) -
- sizeof(unsigned long));
- out.seekp(data_val);
- write_binary_number<unsigned long>(out, data_size);
- out.seekp(xacts_val);
- write_binary_number<unsigned long>(out, xact_count);
- out.seekp(bigints_val);
- write_binary_number<unsigned long>(out, bigints_count);
+ write_guard(out, 0x3002);
}
+} // namespace binary
} // namespace ledger
diff --git a/binary.h b/binary.h
index ca3254c9..864c6ea2 100644
--- a/binary.h
+++ b/binary.h
@@ -1,26 +1,269 @@
-#ifndef _BINARY_H
-#define _BINARY_H
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
-#include "journal.h"
-#include "parser.h"
+#ifndef BINARY_H
+#define BINARY_H
namespace ledger {
+namespace binary {
-class binary_parser_t : public parser_t
+template <typename T>
+inline void read_number_nocheck(std::istream& in, T& num) {
+ in.read(reinterpret_cast<char *>(&num), sizeof(num));
+}
+
+template <typename T>
+inline void read_number_nocheck(const char *& data, T& num) {
+ num = *reinterpret_cast<const T *>(data);
+ data += sizeof(T);
+}
+
+template <typename T>
+inline T read_number_nocheck(std::istream& in) {
+ T num;
+ read_number_nocheck(in, num);
+ return num;
+}
+
+template <typename T>
+inline T read_number_nocheck(const char *& data) {
+ T num;
+ read_number_nocheck(data, num);
+ return num;
+}
+
+#if DEBUG_LEVEL >= ALPHA
+#define read_guard(in, id) \
+ if (read_number_nocheck<unsigned short>(in) != id) \
+ assert(false);
+#else
+#define read_guard(in, id)
+#endif
+
+template <typename T>
+inline void read_number(std::istream& in, T& num) {
+ read_guard(in, 0x2003);
+ in.read(reinterpret_cast<char *>(&num), sizeof(num));
+ read_guard(in, 0x2004);
+}
+
+template <typename T>
+inline void read_number(const char *& data, T& num) {
+ read_guard(data, 0x2003);
+ num = *reinterpret_cast<const T *>(data);
+ data += sizeof(T);
+ read_guard(data, 0x2004);
+}
+
+template <typename T>
+inline T read_number(std::istream& in) {
+ T num;
+ read_number(in, num);
+ return num;
+}
+
+template <typename T>
+inline T read_number(const char *& data) {
+ T num;
+ read_number(data, num);
+ return num;
+}
+
+void read_bool(std::istream& in, bool& num);
+void read_bool(const char *& data, bool& num);
+
+inline bool read_bool(std::istream& in) {
+ bool num;
+ read_bool(in, num);
+ return num;
+}
+
+inline bool read_bool(const char *& data) {
+ bool num;
+ read_bool(data, num);
+ return num;
+}
+
+template <typename T>
+void read_long(std::istream& in, T& num)
+{
+ read_guard(in, 0x2001);
+
+ unsigned char len;
+ read_number_nocheck(in, len);
+
+ num = 0;
+ unsigned char temp;
+ if (len > 3) {
+ read_number_nocheck(in, temp);
+ num |= static_cast<unsigned long>(temp) << 24;
+ }
+ if (len > 2) {
+ read_number_nocheck(in, temp);
+ num |= static_cast<unsigned long>(temp) << 16;
+ }
+ if (len > 1) {
+ read_number_nocheck(in, temp);
+ num |= static_cast<unsigned long>(temp) << 8;
+ }
+
+ read_number_nocheck(in, temp);
+ num |= static_cast<unsigned long>(temp);
+
+ read_guard(in, 0x2002);
+}
+
+template <typename T>
+void read_long(const char *& data, T& num)
+{
+ read_guard(data, 0x2001);
+
+ unsigned char len;
+ read_number_nocheck(data, len);
+
+ num = 0;
+ unsigned char temp;
+ if (len > 3) {
+ read_number_nocheck(data, temp);
+ num |= static_cast<unsigned long>(temp) << 24;
+ }
+ if (len > 2) {
+ read_number_nocheck(data, temp);
+ num |= static_cast<unsigned long>(temp) << 16;
+ }
+ if (len > 1) {
+ read_number_nocheck(data, temp);
+ num |= static_cast<unsigned long>(temp) << 8;
+ }
+
+ read_number_nocheck(data, temp);
+ num |= static_cast<unsigned long>(temp);
+
+ read_guard(data, 0x2002);
+}
+
+template <typename T>
+inline T read_long(std::istream& in) {
+ T num;
+ read_long(in, num);
+ return num;
+}
+
+template <typename T>
+inline T read_long(const char *& data) {
+ T num;
+ read_long(data, num);
+ return num;
+}
+
+void read_string(std::istream& in, string& str);
+void read_string(const char *& data, string& str);
+void read_string(const char *& data, string * str);
+
+inline string read_string(std::istream& in) {
+ string temp;
+ read_string(in, temp);
+ return temp;
+}
+
+inline string read_string(const char *& data) {
+ string temp;
+ read_string(data, temp);
+ return temp;
+}
+
+
+template <typename T>
+inline void write_number_nocheck(std::ostream& out, T num) {
+ out.write(reinterpret_cast<char *>(&num), sizeof(num));
+}
+
+#if DEBUG_LEVEL >= ALPHA
+#define write_guard(out, id) \
+ write_number_nocheck<unsigned short>(out, id)
+#else
+#define write_guard(in, id)
+#endif
+
+template <typename T>
+inline void write_number(std::ostream& out, T num) {
+ write_guard(out, 0x2003);
+ out.write(reinterpret_cast<char *>(&num), sizeof(num));
+ write_guard(out, 0x2004);
+}
+
+void write_bool(std::ostream& out, bool num);
+
+template <typename T>
+void write_long(std::ostream& out, T num)
{
- public:
- virtual bool test(std::istream& in) const;
+ write_guard(out, 0x2001);
+
+ unsigned char len = 4;
+ if (static_cast<unsigned long>(num) < 0x00000100UL)
+ len = 1;
+ else if (static_cast<unsigned long>(num) < 0x00010000UL)
+ len = 2;
+ else if (static_cast<unsigned long>(num) < 0x01000000UL)
+ len = 3;
+ write_number_nocheck<unsigned char>(out, len);
+
+ unsigned char temp;
+ if (len > 3) {
+ temp = (static_cast<unsigned long>(num) & 0xFF000000UL) >> 24;
+ write_number_nocheck(out, temp);
+ }
+ if (len > 2) {
+ temp = (static_cast<unsigned long>(num) & 0x00FF0000UL) >> 16;
+ write_number_nocheck(out, temp);
+ }
+ if (len > 1) {
+ temp = (static_cast<unsigned long>(num) & 0x0000FF00UL) >> 8;
+ write_number_nocheck(out, temp);
+ }
+
+ temp = (static_cast<unsigned long>(num) & 0x000000FFUL);
+ write_number_nocheck(out, temp);
+
+ write_guard(out, 0x2002);
+}
- virtual unsigned int parse(std::istream& in,
- config_t& config,
- journal_t * journal,
- account_t * master = NULL,
- const std::string * original_file = NULL);
-};
+void write_string(std::ostream& out, const string& str);
-void write_binary_journal(std::ostream& out,
- journal_t * journal);
+template <typename T>
+inline void write_object(std::ostream& out, const T& journal) {
+ assert(false);
+}
+} // namespace binary
} // namespace ledger
-#endif // _BINARY_H
+#endif // BINARY_H
diff --git a/commodity.cc b/commodity.cc
new file mode 100644
index 00000000..76614f92
--- /dev/null
+++ b/commodity.cc
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file commodity.cc
+ * @author John Wiegley
+ * @date Thu Apr 26 15:19:46 2007
+ *
+ * @brief Types for dealing with commodities.
+ *
+ * This file defines member functions for flavors of commodity_t.
+ */
+
+#include "amount.h"
+#include "parser.h" // for parsing utility functions
+
+namespace ledger {
+
+void commodity_t::add_price(const moment_t& date,
+ const amount_t& price)
+{
+ if (! base->history)
+ base->history = history_t();
+
+ history_map::iterator i = base->history->prices.find(date);
+ if (i != base->history->prices.end()) {
+ (*i).second = price;
+ } else {
+ std::pair<history_map::iterator, bool> result
+ = base->history->prices.insert(history_map::value_type(date, price));
+ assert(result.second);
+ }
+}
+
+bool commodity_t::remove_price(const moment_t& date)
+{
+ if (base->history) {
+ history_map::size_type n = base->history->prices.erase(date);
+ if (n > 0) {
+ if (base->history->prices.empty())
+ base->history.reset();
+ return true;
+ }
+ }
+ return false;
+}
+
+optional<amount_t> commodity_t::value(const optional<moment_t>& moment)
+{
+ optional<moment_t> age;
+ optional<amount_t> price;
+
+ if (base->history) {
+ assert(base->history->prices.size() > 0);
+
+ if (! moment) {
+ history_map::reverse_iterator r = base->history->prices.rbegin();
+ age = (*r).first;
+ price = (*r).second;
+ } else {
+ history_map::iterator i = base->history->prices.lower_bound(*moment);
+ if (i == base->history->prices.end()) {
+ history_map::reverse_iterator r = base->history->prices.rbegin();
+ age = (*r).first;
+ price = (*r).second;
+ } else {
+ age = (*i).first;
+ if (*moment != *age) {
+ if (i != base->history->prices.begin()) {
+ --i;
+ age = (*i).first;
+ price = (*i).second;
+ } else {
+ age = none;
+ }
+ } else {
+ price = (*i).second;
+ }
+ }
+ }
+ }
+
+ if (! has_flags(COMMODITY_STYLE_NOMARKET) && parent().get_quote) {
+ if (optional<amount_t> quote = parent().get_quote
+ (*this, age, moment,
+ (base->history && base->history->prices.size() > 0 ?
+ (*base->history->prices.rbegin()).first : optional<moment_t>())))
+ return *quote;
+ }
+ return price;
+}
+
+commodity_t::operator bool() const
+{
+ return this != parent().null_commodity;
+}
+
+bool commodity_t::symbol_needs_quotes(const string& symbol)
+{
+ for (const char * p = symbol.c_str(); *p; p++)
+ if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
+ return true;
+
+ return false;
+}
+
+void commodity_t::parse_symbol(std::istream& in, string& symbol)
+{
+ // Invalid commodity characters:
+ // SPACE, TAB, NEWLINE, RETURN
+ // 0-9 . , ; - + * / ^ ? : & | ! =
+ // < > { } [ ] ( ) @
+
+ static int invalid_chars[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
+ /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
+ /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+ char buf[256];
+ char c = peek_next_nonws(in);
+ if (c == '"') {
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != '"');
+ if (c == '"')
+ in.get(c);
+ else
+ throw_(amount_error, "Quoted commodity symbol lacks closing quote");
+ } else {
+ READ_INTO(in, buf, 255, c, ! invalid_chars[(unsigned char)c]);
+ }
+ symbol = buf;
+}
+
+void commodity_t::parse_symbol(char *& p, string& symbol)
+{
+ if (*p == '"') {
+ char * q = std::strchr(p + 1, '"');
+ if (! q)
+ throw_(parse_error, "Quoted commodity symbol lacks closing quote");
+ symbol = string(p + 1, 0, q - p - 1);
+ p = q + 2;
+ } else {
+ char * q = next_element(p);
+ symbol = p;
+ if (q)
+ p = q;
+ else
+ p += symbol.length();
+ }
+ if (symbol.empty())
+ throw_(parse_error, "Failed to parse commodity");
+}
+
+bool commodity_t::valid() const
+{
+ if (symbol().empty() && this != parent().null_commodity) {
+ DEBUG("ledger.validate",
+ "commodity_t: symbol().empty() && this != null_commodity");
+ return false;
+ }
+
+ if (annotated && ! base) {
+ DEBUG("ledger.validate", "commodity_t: annotated && ! base");
+ return false;
+ }
+
+ if (precision() > 16) {
+ DEBUG("ledger.validate", "commodity_t: precision() > 16");
+ return false;
+ }
+
+ return true;
+}
+
+void annotation_t::parse(std::istream& in)
+{
+ do {
+ char buf[256];
+ char c = peek_next_nonws(in);
+ if (c == '{') {
+ if (price)
+ throw_(amount_error, "Commodity specifies more than one price");
+
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != '}');
+ if (c == '}')
+ in.get(c);
+ else
+ throw_(amount_error, "Commodity price lacks closing brace");
+
+ amount_t temp;
+ temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
+ temp.in_place_reduce();
+
+ // Since this price will maintain its own precision, make sure
+ // it is at least as large as the base commodity, since the user
+ // may have only specified {$1} or something similar.
+
+ if (temp.has_commodity() &&
+ temp.precision() < temp.commodity().precision())
+ temp = temp.round(); // no need to retain individual precision
+
+ price = temp;
+ }
+ else if (c == '[') {
+ if (date)
+ throw_(amount_error, "Commodity specifies more than one date");
+
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ']');
+ if (c == ']')
+ in.get(c);
+ else
+ throw_(amount_error, "Commodity date lacks closing bracket");
+
+ date = parse_datetime(buf);
+ }
+ else if (c == '(') {
+ if (tag)
+ throw_(amount_error, "Commodity specifies more than one tag");
+
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ')');
+ if (c == ')')
+ in.get(c);
+ else
+ throw_(amount_error, "Commodity tag lacks closing parenthesis");
+
+ tag = buf;
+ }
+ else {
+ break;
+ }
+ } while (true);
+
+ DEBUG("amounts.commodities",
+ "Parsed commodity annotations: " << std::endl << *this);
+}
+
+bool annotated_commodity_t::operator==(const commodity_t& comm) const
+{
+ // If the base commodities don't match, the game's up.
+ if (base != comm.base)
+ return false;
+
+ assert(annotated);
+ if (! comm.annotated)
+ return false;
+
+ if (details != as_annotated_commodity(comm).details)
+ return false;
+
+ return true;
+}
+
+commodity_t&
+annotated_commodity_t::strip_annotations(const bool _keep_price,
+ const bool _keep_date,
+ const bool _keep_tag)
+{
+ DEBUG("commodity.annotated.strip",
+ "Reducing commodity " << *this << std::endl
+ << " keep price " << _keep_price << " "
+ << " keep date " << _keep_date << " "
+ << " keep tag " << _keep_tag);
+
+ commodity_t * new_comm;
+
+ if ((_keep_price && details.price) ||
+ (_keep_date && details.date) ||
+ (_keep_tag && details.tag))
+ {
+ new_comm = parent().find_or_create
+ (referent(),
+ annotation_t(_keep_price ? details.price : none,
+ _keep_date ? details.date : none,
+ _keep_tag ? details.tag : none));
+ } else {
+ new_comm = parent().find_or_create(base_symbol());
+ }
+
+ assert(new_comm);
+ return *new_comm;
+}
+
+void annotated_commodity_t::write_annotations(std::ostream& out,
+ const annotation_t& info)
+{
+ if (info.price)
+ out << " {" << *info.price << '}';
+
+ if (info.date)
+ out << " [" << *info.date << ']';
+
+ if (info.tag)
+ out << " (" << *info.tag << ')';
+}
+
+bool compare_amount_commodities::operator()(const amount_t * left,
+ const amount_t * right) const
+{
+ commodity_t& leftcomm(left->commodity());
+ commodity_t& rightcomm(right->commodity());
+
+ int cmp = leftcomm.base_symbol().compare(rightcomm.base_symbol());
+ if (cmp != 0)
+ return cmp < 0;
+
+ if (! leftcomm.annotated) {
+ assert(rightcomm.annotated);
+ return true;
+ }
+ else if (! rightcomm.annotated) {
+ assert(leftcomm.annotated);
+ return false;
+ }
+ else {
+ annotated_commodity_t& aleftcomm(static_cast<annotated_commodity_t&>(leftcomm));
+ annotated_commodity_t& arightcomm(static_cast<annotated_commodity_t&>(rightcomm));
+
+ if (! aleftcomm.details.price && arightcomm.details.price)
+ return true;
+ if (aleftcomm.details.price && ! arightcomm.details.price)
+ return false;
+
+ if (aleftcomm.details.price && arightcomm.details.price) {
+ amount_t leftprice(*aleftcomm.details.price);
+ leftprice.in_place_reduce();
+ amount_t rightprice(*arightcomm.details.price);
+ rightprice.in_place_reduce();
+
+ if (leftprice.commodity() == rightprice.commodity()) {
+ return (leftprice - rightprice).sign() < 0;
+ } else {
+ // Since we have two different amounts, there's really no way
+ // to establish a true sorting order; we'll just do it based
+ // on the numerical values.
+ leftprice.clear_commodity();
+ rightprice.clear_commodity();
+ return (leftprice - rightprice).sign() < 0;
+ }
+ }
+
+ if (! aleftcomm.details.date && arightcomm.details.date)
+ return true;
+ if (aleftcomm.details.date && ! arightcomm.details.date)
+ return false;
+
+ if (aleftcomm.details.date && arightcomm.details.date) {
+ duration_t diff = *aleftcomm.details.date - *arightcomm.details.date;
+ return diff.is_negative();
+ }
+
+ if (! aleftcomm.details.tag && arightcomm.details.tag)
+ return true;
+ if (aleftcomm.details.tag && ! arightcomm.details.tag)
+ return false;
+
+ if (aleftcomm.details.tag && arightcomm.details.tag)
+ return *aleftcomm.details.tag < *arightcomm.details.tag;
+
+ assert(false);
+ return true;
+ }
+}
+
+commodity_pool_t::commodity_pool_t() : default_commodity(NULL)
+{
+ null_commodity = create("");
+ null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
+ COMMODITY_STYLE_BUILTIN);
+}
+
+commodity_t * commodity_pool_t::create(const string& symbol)
+{
+ shared_ptr<commodity_t::base_t>
+ base_commodity(new commodity_t::base_t(symbol));
+ std::auto_ptr<commodity_t> commodity(new commodity_t(this, base_commodity));
+
+ DEBUG("amounts.commodities", "Creating base commodity " << symbol);
+
+ // Create the "qualified symbol" version of this commodity's symbol
+ if (commodity_t::symbol_needs_quotes(symbol)) {
+ commodity->qualified_symbol = "\"";
+ *commodity->qualified_symbol += symbol;
+ *commodity->qualified_symbol += "\"";
+ }
+
+ DEBUG("amounts.commodities",
+ "Creating commodity '" << commodity->symbol() << "'");
+
+ // Start out the new commodity with the default commodity's flags
+ // and precision, if one has been defined.
+#if 0
+ // jww (2007-05-02): This doesn't do anything currently!
+ if (default_commodity)
+ commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
+ COMMODITY_STYLE_NOMARKET);
+#endif
+
+ commodity->ident = commodities.size();
+
+ commodities.push_back(commodity.get());
+ return commodity.release();
+}
+
+commodity_t * commodity_pool_t::find_or_create(const string& symbol)
+{
+ DEBUG("amounts.commodities", "Find-or-create commodity " << symbol);
+
+ commodity_t * commodity = find(symbol);
+ if (commodity)
+ return commodity;
+ return create(symbol);
+}
+
+commodity_t * commodity_pool_t::find(const string& symbol)
+{
+ DEBUG("amounts.commodities", "Find commodity " << symbol);
+
+ typedef commodity_pool_t::commodities_t::nth_index<1>::type
+ commodities_by_name;
+
+ commodities_by_name& name_index = commodities.get<1>();
+ commodities_by_name::const_iterator i = name_index.find(symbol);
+ if (i != name_index.end())
+ return *i;
+ else
+ return NULL;
+}
+
+commodity_t * commodity_pool_t::find(const commodity_t::ident_t ident)
+{
+ DEBUG("amounts.commodities", "Find commodity by ident " << ident);
+
+ typedef commodity_pool_t::commodities_t::nth_index<0>::type
+ commodities_by_ident;
+
+ commodities_by_ident& ident_index = commodities.get<0>();
+ return ident_index[ident];
+}
+
+commodity_t *
+commodity_pool_t::create(const string& symbol, const annotation_t& details)
+{
+ commodity_t * new_comm = create(symbol);
+ if (! new_comm)
+ return NULL;
+
+ if (details)
+ return find_or_create(*new_comm, details);
+ else
+ return new_comm;
+}
+
+namespace {
+ string make_qualified_name(const commodity_t& comm,
+ const annotation_t& details)
+ {
+ assert(details);
+
+ if (details.price && details.price->sign() < 0)
+ throw_(amount_error, "A commodity's price may not be negative");
+
+ std::ostringstream name;
+ comm.print(name);
+ annotated_commodity_t::write_annotations(name, details);
+
+ DEBUG("amounts.commodities", "make_qualified_name for "
+ << comm.qualified_symbol << std::endl << details);
+ DEBUG("amounts.commodities", "qualified_name is " << name.str());
+
+ return name.str();
+ }
+}
+
+commodity_t *
+commodity_pool_t::find(const string& symbol, const annotation_t& details)
+{
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
+
+ if (details) {
+ string name = make_qualified_name(*comm, details);
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
+ return ann_comm;
+ }
+ return NULL;
+ } else {
+ return comm;
+ }
+}
+
+commodity_t *
+commodity_pool_t::find_or_create(const string& symbol,
+ const annotation_t& details)
+{
+ commodity_t * comm = find(symbol);
+ if (! comm)
+ return NULL;
+
+ if (details)
+ return find_or_create(*comm, details);
+ else
+ return comm;
+}
+
+commodity_t *
+commodity_pool_t::create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key)
+{
+ assert(comm);
+ assert(details);
+ assert(! mapping_key.empty());
+
+ std::auto_ptr<commodity_t> commodity
+ (new annotated_commodity_t(&comm, details));
+
+ commodity->qualified_symbol = comm.symbol();
+ assert(! commodity->qualified_symbol->empty());
+
+ DEBUG("amounts.commodities", "Creating annotated commodity "
+ << "symbol " << commodity->symbol()
+ << " key " << mapping_key << std::endl << details);
+
+ // Add the fully annotated name to the map, so that this symbol may
+ // quickly be found again.
+ commodity->ident = commodities.size();
+ commodity->mapping_key_ = mapping_key;
+
+ commodities.push_back(commodity.get());
+ return commodity.release();
+}
+
+commodity_t * commodity_pool_t::find_or_create(commodity_t& comm,
+ const annotation_t& details)
+{
+ assert(comm);
+ assert(details);
+
+ string name = make_qualified_name(comm, details);
+ assert(! name.empty());
+
+ if (commodity_t * ann_comm = find(name)) {
+ assert(ann_comm->annotated && as_annotated_commodity(*ann_comm).details);
+ return ann_comm;
+ }
+ return create(comm, details, name);
+}
+
+} // namespace ledger
diff --git a/commodity.h b/commodity.h
new file mode 100644
index 00000000..767023e8
--- /dev/null
+++ b/commodity.h
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file commodity.h
+ * @author John Wiegley
+ * @date Wed Apr 18 22:05:53 2007
+ *
+ * @brief Types for handling commodities.
+ *
+ * This file contains one of the most basic types in Ledger:
+ * commodity_t, and its annotated cousin, annotated_commodity_t.
+ */
+#ifndef _COMMODITY_H
+#define _COMMODITY_H
+
+namespace ledger {
+
+class commodity_t
+ : public delegates_flags<>,
+ public equality_comparable1<commodity_t, noncopyable>
+{
+ friend class commodity_pool_t;
+
+ class base_t : public noncopyable, public supports_flags<>
+ {
+ public:
+ typedef std::map<const moment_t, amount_t> history_map;
+
+ struct history_t {
+ history_map prices;
+ ptime last_lookup;
+ };
+
+#define COMMODITY_STYLE_DEFAULTS 0x00
+#define COMMODITY_STYLE_SUFFIXED 0x01
+#define COMMODITY_STYLE_SEPARATED 0x02
+#define COMMODITY_STYLE_EUROPEAN 0x04
+#define COMMODITY_STYLE_THOUSANDS 0x08
+#define COMMODITY_STYLE_NOMARKET 0x10
+#define COMMODITY_STYLE_BUILTIN 0x20
+
+ string symbol;
+ amount_t::precision_t precision;
+ optional<string> name;
+ optional<string> note;
+ optional<history_t> history;
+ optional<amount_t> smaller;
+ optional<amount_t> larger;
+
+ public:
+ explicit base_t(const string& _symbol)
+ : supports_flags<>(COMMODITY_STYLE_DEFAULTS),
+ symbol(_symbol), precision(0) {
+ TRACE_CTOR(base_t, "const string&");
+ }
+ ~base_t() {
+ TRACE_DTOR(base_t);
+ }
+ };
+
+public:
+ static bool symbol_needs_quotes(const string& symbol);
+
+ typedef base_t::history_t history_t;
+ typedef base_t::history_map history_map;
+ typedef uint_least32_t ident_t;
+
+ shared_ptr<base_t> base;
+
+ commodity_pool_t * parent_;
+ ident_t ident;
+ optional<string> qualified_symbol;
+ optional<string> mapping_key_;
+ bool annotated;
+
+public:
+ explicit commodity_t(commodity_pool_t * _parent,
+ const shared_ptr<base_t>& _base)
+ : delegates_flags<>(*_base.get()), base(_base),
+ parent_(_parent), annotated(false) {
+ TRACE_CTOR(commodity_t, "");
+ }
+ virtual ~commodity_t() {
+ TRACE_DTOR(commodity_t);
+ }
+
+ operator bool() const;
+
+ virtual bool operator==(const commodity_t& comm) const {
+ if (comm.annotated)
+ return comm == *this;
+ return base.get() == comm.base.get();
+ }
+
+ commodity_pool_t& parent() const {
+ return *parent_;
+ }
+
+ string base_symbol() const {
+ return base->symbol;
+ }
+ string symbol() const {
+ return qualified_symbol ? *qualified_symbol : base_symbol();
+ }
+
+ string mapping_key() const {
+ if (mapping_key_)
+ return *mapping_key_;
+ else
+ return base_symbol();
+ }
+
+ optional<string> name() const {
+ return base->name;
+ }
+ void set_name(const optional<string>& arg = none) {
+ base->name = arg;
+ }
+
+ optional<string> note() const {
+ return base->note;
+ }
+ void set_note(const optional<string>& arg = none) {
+ base->note = arg;
+ }
+
+ amount_t::precision_t precision() const {
+ return base->precision;
+ }
+ void set_precision(amount_t::precision_t arg) {
+ base->precision = arg;
+ }
+
+ optional<amount_t> smaller() const {
+ return base->smaller;
+ }
+ void set_smaller(const optional<amount_t>& arg = none) {
+ base->smaller = arg;
+ }
+
+ optional<amount_t> larger() const {
+ return base->larger;
+ }
+ void set_larger(const optional<amount_t>& arg = none) {
+ base->larger = arg;
+ }
+
+ optional<history_t> history() const {
+ return base->history;
+ }
+
+ void add_price(const moment_t& date, const amount_t& price);
+ bool remove_price(const moment_t& date);
+
+ optional<amount_t> value(const optional<moment_t>& moment = none);
+
+ static void parse_symbol(std::istream& in, string& symbol);
+ static void parse_symbol(char *& p, string& symbol);
+ static string parse_symbol(std::istream& in) {
+ string temp;
+ parse_symbol(in, temp);
+ return temp;
+ }
+
+ void print(std::ostream& out) const {
+ out << symbol();
+ }
+
+ void read(std::istream& in);
+ void read(char *& data);
+ void write(std::ostream& out) const;
+
+ bool valid() const;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
+ comm.print(out);
+ return out;
+}
+
+struct annotation_t : public equality_comparable<annotation_t>
+{
+ optional<amount_t> price;
+ optional<moment_t> date;
+ optional<string> tag;
+
+ explicit annotation_t
+ (const optional<amount_t>& _price = none,
+ const optional<moment_t>& _date = none,
+ const optional<string>& _tag = none)
+ : price(_price), date(_date), tag(_tag) {}
+
+ operator bool() const {
+ return price || date || tag;
+ }
+
+ bool operator==(const annotation_t& rhs) const {
+ return (price == rhs.price &&
+ date == rhs.date &&
+ tag == rhs.tag);
+ }
+
+ void parse(std::istream& in);
+ void print(std::ostream& out) const {
+ out << "price " << (price ? price->to_string() : "NONE") << " "
+ << "date " << (date ? *date : moment_t()) << " "
+ << "tag " << (tag ? *tag : "NONE");
+ }
+
+ bool valid() const {
+ assert(*this);
+ return true;
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& out, const annotation_t& details) {
+ details.print(out);
+ return out;
+}
+
+class annotated_commodity_t
+ : public commodity_t,
+ public equality_comparable<annotated_commodity_t,
+ equality_comparable2<annotated_commodity_t, commodity_t,
+ noncopyable> >
+{
+public:
+ commodity_t * ptr;
+ annotation_t details;
+
+ explicit annotated_commodity_t(commodity_t * _ptr,
+ const annotation_t& _details)
+ : commodity_t(_ptr->parent_, _ptr->base), ptr(_ptr), details(_details) {
+ TRACE_CTOR(annotated_commodity_t, "");
+ annotated = true;
+ }
+ virtual ~annotated_commodity_t() {
+ TRACE_DTOR(annotated_commodity_t);
+ }
+
+ virtual bool operator==(const commodity_t& comm) const;
+ virtual bool operator==(const annotated_commodity_t& comm) const {
+ return *this == static_cast<const commodity_t&>(comm);
+ }
+
+ commodity_t& referent() {
+ return *ptr;
+ }
+ const commodity_t& referent() const {
+ return *ptr;
+ }
+
+ commodity_t& strip_annotations(const bool _keep_price,
+ const bool _keep_date,
+ const bool _keep_tag);
+
+ void write_annotations(std::ostream& out) const {
+ annotated_commodity_t::write_annotations(out, details);
+ }
+
+ static void write_annotations(std::ostream& out,
+ const annotation_t& info);
+};
+
+inline annotated_commodity_t&
+as_annotated_commodity(commodity_t& commodity) {
+ return downcast<annotated_commodity_t>(commodity);
+}
+inline const annotated_commodity_t&
+as_annotated_commodity(const commodity_t& commodity) {
+ return downcast<const annotated_commodity_t>(commodity);
+}
+
+
+struct compare_amount_commodities {
+ bool operator()(const amount_t * left, const amount_t * right) const;
+};
+
+class commodity_pool_t : public noncopyable
+{
+ /**
+ * The commodities collection in commodity_pool_t maintains pointers
+ * to all the commodities which have ever been created by the user,
+ * whether explicitly by calling the create methods of
+ * commodity_pool_t, or implicitly by parsing a commoditized amount.
+ *
+ * The `commodities' member variable represents a collection which
+ * is indexed by two vertices: first, and ordered sequence of unique
+ * integer which identify commodities by a numerical identifier; and
+ * second, by a hashed set of symbolic names which reflect how the
+ * commodity was referred to by the user.
+ */
+ typedef multi_index_container<
+ commodity_t *,
+ multi_index::indexed_by<
+ multi_index::random_access<>,
+ multi_index::hashed_unique<
+ multi_index::const_mem_fun<commodity_t,
+ string, &commodity_t::mapping_key> >
+ >
+ > commodities_t;
+
+ commodities_t commodities;
+
+public:
+ commodity_t * null_commodity;
+ commodity_t * default_commodity;
+
+private:
+ template<typename T>
+ struct first_initialized
+ {
+ typedef T result_type;
+
+ template<typename InputIterator>
+ T operator()(InputIterator first, InputIterator last) const
+ {
+ for (; first != last; first++)
+ if (*first)
+ return *first;
+ return T();
+ }
+ };
+
+public:
+ boost::function<optional<amount_t>
+ (commodity_t& commodity,
+ const optional<moment_t>& date,
+ const optional<moment_t>& moment,
+ const optional<moment_t>& last)> get_quote;
+
+ explicit commodity_pool_t();
+
+ ~commodity_pool_t() {
+ typedef commodity_pool_t::commodities_t::nth_index<0>::type
+ commodities_by_ident;
+
+ commodities_by_ident& ident_index = commodities.get<0>();
+ for (commodities_by_ident::iterator i = ident_index.begin();
+ i != ident_index.end();
+ i++)
+ checked_delete(*i);
+ }
+
+ commodity_t * create(const string& symbol);
+ commodity_t * find(const string& name);
+ commodity_t * find(const commodity_t::ident_t ident);
+ commodity_t * find_or_create(const string& symbol);
+
+ commodity_t * create(const string& symbol, const annotation_t& details);
+ commodity_t * find(const string& symbol, const annotation_t& details);
+ commodity_t * find_or_create(const string& symbol,
+ const annotation_t& details);
+
+ commodity_t * create(commodity_t& comm,
+ const annotation_t& details,
+ const string& mapping_key);
+
+ commodity_t * find_or_create(commodity_t& comm,
+ const annotation_t& details);
+};
+
+} // namespace ledger
+
+#endif // _COMMODITY_H
diff --git a/configure.in b/configure.in
index 038e80aa..c42a8c0b 100644
--- a/configure.in
+++ b/configure.in
@@ -1,9 +1,12 @@
-# -*- Autoconf -*-
+# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
-AC_PREREQ(2.59)
-AC_INIT(ledger, 2.6.0.90, johnw@newartisans.com)
-AM_INIT_AUTOMAKE(ledger, 2.6.0.90)
+AC_PREREQ(2.61)
+
+AC_INIT([ledger],[2.7],[johnw@newartisans.com])
+AC_CONFIG_SRCDIR(ledger)
+AM_INIT_AUTOMAKE([dist-bzip2])
+
AC_CONFIG_SRCDIR([main.cc])
AC_CONFIG_HEADER([acconf.h])
@@ -16,39 +19,68 @@ AM_PROG_LIBTOOL
# Checks for emacs lisp path
AM_PATH_LISPDIR
+# Check for options
+AC_ARG_ENABLE(debug,
+ [ --enable-debug Turn on debugging],
+ [case "${enableval}" in
+ yes) debug=true ;;
+ no) debug=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;;
+ esac],[debug=false])
+
+AM_CONDITIONAL(DEBUG, test x$debug = xtrue)
+
+AC_ARG_ENABLE(pch,
+ [ --enable-pch Use GCC 4.x pre-compiled headers],
+ [case "${enableval}" in
+ yes) pch=true ;;
+ no) pch=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-pch) ;;
+ esac],[pch=false])
+
+AM_CONDITIONAL(USE_PCH, test x$pch = xtrue)
+
+AC_ARG_WITH(boost-suffix,
+ [ --with-boost-suffix=X Append X to the Boost library names],
+ [BOOST_SUFFIX="-${withval}"],
+ [BOOST_SUFFIX=""])
+
+AC_SUBST([BOOST_SUFFIX], $BOOST_SUFFIX)
+
# check if UNIX pipes are available
AC_CACHE_CHECK(
[if pipes can be used],
[pipes_avail_cv_],
[AC_LANG_PUSH(C++)
- AC_TRY_LINK(
- [#include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>],
- [int status, pfd[2];
- status = pipe(pfd);
- status = fork();
- if (status < 0) {
- ;
- } else if (status == 0) {
- char *arg0;
-
- status = dup2(pfd[0], STDIN_FILENO);
-
- close(pfd[1]);
- close(pfd[0]);
-
- execlp("", arg0, (char *)0);
- perror("execl");
- exit(1);
- } else {
- close(pfd[0]);
- }],
- [pipes_avail_cv_=true],
- [pipes_avail_cv_=false])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <sys/types.h>
+ #include <sys/wait.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdio.h>]],
+ [[int status, pfd[2];
+ status = pipe(pfd);
+ status = fork();
+ if (status < 0) {
+ ;
+ } else if (status == 0) {
+ char *arg0;
+
+ status = dup2(pfd[0], STDIN_FILENO);
+
+ close(pfd[1]);
+ close(pfd[0]);
+
+ execlp("", arg0, (char *)0);
+ perror("execl");
+ exit(1);
+ } else {
+ close(pfd[0]);
+ }]])],
+ [pipes_avail=true],
+ [pipes_avail=false])
AC_LANG_POP])
if [test x$pipes_avail_cv_ = xtrue ]; then
@@ -62,18 +94,13 @@ AC_CACHE_CHECK(
[libgmp_save_libs=$LIBS
LIBS="-lgmp $LIBS"
AC_LANG_PUSH(C++)
- AC_TRY_LINK(
- [#include <gmp.h>],
- [mpz_t bar;
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <gmp.h>]], [[mpz_t bar;
mpz_init(bar);
- mpz_clear(bar);],
- [libgmp_avail_cv_=true],
- [libgmp_avail_cv_=false])
+ mpz_clear(bar);]])],[libgmp_avail=true],[libgmp_avail=false])
AC_LANG_POP
LIBS=$libgmp_save_libs])
-if [test x$libgmp_avail_cv_ = xtrue ]; then
- AM_CONDITIONAL(HAVE_GMP, true)
+if [test x$libgmp_avail = xtrue ]; then
LIBS="-lgmp $LIBS"
else
AC_MSG_FAILURE("Could not find gmp library (set CPPFLAGS and LDFLAGS?)")
@@ -173,6 +200,112 @@ else
AM_CONDITIONAL(HAVE_XMLPARSE, false)
fi
+# check for boost_regex
+AC_CACHE_CHECK(
+ [if boost_regex is available],
+ [boost_regex_avail],
+ [boost_regex_save_libs=$LIBS
+ LIBS="-lboost_regex$BOOST_SUFFIX $LIBS"
+ AC_LANG_PUSH(C++)
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <boost/regex.hpp>]],
+ [[boost::regex foo_regexp("Hello, world!");]])],
+ [boost_regex_avail=true],
+ [boost_regex_avail=false])
+ AC_LANG_POP
+ LIBS=$boost_regex_save_libs])
+
+if [test x$boost_regex_avail = xtrue ]; then
+ LIBS="-lboost_regex$BOOST_SUFFIX $LIBS"
+else
+ AC_MSG_FAILURE("Could not find boost_regex library (set CPPFLAGS and LDFLAGS?)")
+fi
+
+# check for boost_date_time
+AC_CACHE_CHECK(
+ [if boost_date_time is available],
+ [boost_date_time_cpplib_avail],
+ [boost_date_time_save_libs=$LIBS
+ LIBS="-lboost_date_time$BOOST_SUFFIX $LIBS"
+ AC_LANG_PUSH(C++)
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <boost/date_time/posix_time/posix_time.hpp>
+ #include <boost/date_time/gregorian/gregorian.hpp>
+ #include <boost/date_time/local_time_adjustor.hpp>
+ #include <boost/date_time/time_duration.hpp>
+
+ using namespace boost::posix_time;
+ using namespace boost::date_time;
+
+ #include <ctime>
+
+ inline ptime time_to_system_local(const ptime& when) {
+ struct std::tm tm_gmt = to_tm(when);
+ return from_time_t(mktime(&tm_gmt));
+ }]],
+ [[ptime t10 = ptime(boost::gregorian::from_string("2007-01-15"),
+ ptime::time_duration_type());
+
+ ptime t12 = time_to_system_local(t10);
+
+ return t10 != t12;]])],
+ [boost_date_time_cpplib_avail=true],
+ [boost_date_time_cpplib_avail=false])
+ AC_LANG_POP
+ LIBS=$boost_date_time_save_libs])
+
+if [test x$boost_date_time_cpplib_avail = xtrue ]; then
+ LIBS="-lboost_date_time$BOOST_SUFFIX $LIBS"
+else
+ AC_MSG_FAILURE("Could not find boost_date_time library (set CPPFLAGS and LDFLAGS?)")
+fi
+
+# check for boost_filesystem
+AC_CACHE_CHECK(
+ [if boost_filesystem is available],
+ [boost_filesystem_cpplib_avail],
+ [boost_filesystem_save_libs=$LIBS
+ LIBS="-lboost_filesystem$BOOST_SUFFIX -lboost_system$BOOST_SUFFIX $LIBS"
+ AC_LANG_PUSH(C++)
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <boost/filesystem/path.hpp>]],
+ [[boost::filesystem::path this_path("Hello");]])],
+ [boost_filesystem_cpplib_avail=true],
+ [boost_filesystem_cpplib_avail=false])
+ AC_LANG_POP
+ LIBS=$boost_filesystem_save_libs])
+
+if [test x$boost_filesystem_cpplib_avail = xtrue ]; then
+ LIBS="-lboost_filesystem$BOOST_SUFFIX -lboost_system$BOOST_SUFFIX $LIBS"
+else
+ AC_MSG_FAILURE("Could not find boost_filesystem library (set CPPFLAGS and LDFLAGS?)")
+fi
+
+## check for boost_signals
+#AC_CACHE_CHECK(
+# [if boost_signals is available],
+# [boost_signals_cpplib_avail],
+# [boost_signals_save_libs=$LIBS
+# LIBS="-lboost_signals$BOOST_SUFFIX $LIBS"
+# AC_LANG_PUSH(C++)
+# AC_LINK_IFELSE(
+# [AC_LANG_PROGRAM(
+# [[#include <boost/signal.hpp>]],
+# [[boost::signal<void (void)> this_signal;]])],
+# [boost_signals_cpplib_avail=true],
+# [boost_signals_cpplib_avail=false])
+# AC_LANG_POP
+# LIBS=$boost_signals_save_libs])
+#
+#if [test x$boost_signals_cpplib_avail = xtrue ]; then
+# LIBS="-lboost_signals$BOOST_SUFFIX $LIBS"
+#else
+# AC_MSG_FAILURE("Could not find boost_signals library (set CPPFLAGS and LDFLAGS?)")
+#fi
+
# check for libofx
AC_ARG_ENABLE(ofx,
[ --enable-ofx Turn on support for OFX/OCF parsing],
@@ -181,6 +314,7 @@ AC_ARG_ENABLE(ofx,
no) ofx=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-ofx) ;;
esac],[ofx=true])
+
AM_CONDITIONAL(USE_OFX, test x$ofx = xtrue)
if [test x$ofx = xtrue ]; then
@@ -190,11 +324,12 @@ if [test x$ofx = xtrue ]; then
[libofx_save_libs=$LIBS
LIBS="-lofx $LIBS"
AC_LANG_PUSH(C++)
- AC_TRY_LINK(
- [#include <libofx.h>],
- [ LibofxContextPtr libofx_context = libofx_get_new_context();],
- [libofx_avail_cv_=true],
- [libofx_avail_cv_=false])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <libofx.h>]],
+ [[LibofxContextPtr libofx_context = libofx_get_new_context();]])],
+ [libofx_avail=true],
+ [libofx_avail=false])
AC_LANG_POP
LIBS=$libofx_save_libs])
@@ -216,6 +351,7 @@ AC_ARG_ENABLE(python,
no) python=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-python) ;;
esac],[python=false])
+
AM_CONDITIONAL(USE_PYTHON, test x$python = xtrue)
if [test x$python = xtrue ]; then
@@ -225,23 +361,25 @@ if [test x$python = xtrue ]; then
[if boost_python is available],
[boost_python_cpplib_avail_cv_],
[boost_python_save_libs=$LIBS
- LIBS="-lboost_python -lpython$PYTHON_VERSION $LIBS"
+ LIBS="-lboost_python$BOOST_SUFFIX -lpython$PYTHON_VERSION $LIBS"
AC_LANG_PUSH(C++)
- AC_TRY_LINK(
- [#include <boost/python.hpp>
- using namespace boost::python;
- class foo {};
- BOOST_PYTHON_MODULE(samp) {
- class_< foo > ("foo") ;
- }],
- [return 0],
- [boost_python_cpplib_avail_cv_=true],
- [boost_python_cpplib_avail_cv_=false])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <boost/python.hpp>
+ using namespace boost::python;
+ class foo {};
+ BOOST_PYTHON_MODULE(samp) {
+ class_< foo > ("foo") ;
+ }]],
+ [[return 0]])],
+ [boost_python_cpplib_avail=true],
+ [boost_python_cpplib_avail=false])
AC_LANG_POP
LIBS=$boost_python_save_libs])
- if [test x$boost_python_cpplib_avail_cv_ = xtrue ]; then
+
+ if [test x$boost_python_cpplib_avail = xtrue ]; then
AM_CONDITIONAL(HAVE_BOOST_PYTHON, true)
- LIBS="-lboost_python -lpython$PYTHON_VERSION $LIBS"
+ LIBS="-lboost_python$BOOST_SUFFIX -lpython$PYTHON_VERSION $LIBS"
else
AM_CONDITIONAL(HAVE_BOOST_PYTHON, false)
fi
@@ -252,19 +390,39 @@ else
AM_CONDITIONAL(HAVE_BOOST_PYTHON, false)
fi
-# Check for options
-AC_ARG_ENABLE(debug,
- [ --enable-debug Turn on debugging],
- [case "${enableval}" in
- yes) debug=true ;;
- no) debug=false ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;;
- esac],[debug=false])
-AM_CONDITIONAL(DEBUG, test x$debug = xtrue)
+# check for CppUnit
+AC_CACHE_CHECK(
+ [if cppunit is available],
+ [cppunit_avail],
+ [cppunit_save_libs=$LIBS
+ LIBS="-lcppunit $LIBS"
+ AC_LANG_PUSH(C++)
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <cppunit/CompilerOutputter.h>
+ #include <cppunit/TestResult.h>
+ #include <cppunit/TestResultCollector.h>
+ #include <cppunit/TestRunner.h>
+ #include <cppunit/TextTestProgressListener.h>
+ #include <cppunit/BriefTestProgressListener.h>
+ #include <cppunit/XmlOutputter.h>
+ #include <cppunit/extensions/TestFactoryRegistry.h>]],
+ [[CPPUNIT_NS::TestResult controller;
+ CPPUNIT_NS::TestResultCollector result;]])],
+ [cppunit_avail=true],
+ [cppunit_avail=false])
+ AC_LANG_POP
+ LIBS=$cppunit_save_libs])
+
+if [test x$cppunit_avail = xtrue ]; then
+ AM_CONDITIONAL(HAVE_CPPUNIT, true)
+else
+ AM_CONDITIONAL(HAVE_CPPUNIT, false)
+fi
# Checks for header files.
-AC_STDC_HEADERS
-AC_HAVE_HEADERS(sys/stat.h)
+AC_HEADER_STDC
+AC_CHECK_HEADERS([sys/stat.h langinfo.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
@@ -272,9 +430,8 @@ AC_TYPE_SIZE_T
AC_STRUCT_TM
# Checks for library functions.
-#AC_FUNC_ERROR_AT_LINE
AC_HEADER_STDC
-AC_CHECK_FUNCS([access mktime realpath stat strftime strptime getpwuid getpwnam])
+AC_CHECK_FUNCS([access mktime realpath getpwuid getpwnam nl_langinfo])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/datetime.cc b/datetime.cc
deleted file mode 100644
index 040c3046..00000000
--- a/datetime.cc
+++ /dev/null
@@ -1,363 +0,0 @@
-#if defined(__GNUG__) && __GNUG__ < 3
-#define _XOPEN_SOURCE
-#endif
-
-#include "debug.h"
-#include "datetime.h"
-
-#include <ctime>
-#include <cctype>
-
-date_t date_t::now(std::time(NULL));
-int date_t::current_year = date_t::now.year();
-std::string date_t::input_format;
-std::string date_t::output_format = "%Y/%m/%d";
-
-const char * date_t::formats[] = {
- "%Y/%m/%d",
- "%m/%d",
- "%Y.%m.%d",
- "%m.%d",
- "%Y-%m-%d",
- "%m-%d",
- "%a",
- "%A",
- "%b",
- "%B",
- "%Y",
- NULL
-};
-
-datetime_t datetime_t::now(std::time(NULL));
-
-namespace {
- static std::time_t base = -1;
- static int base_year = -1;
-
- static const int month_days[12] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
- };
-
- bool parse_date_mask(const char * date_str, struct std::tm * result);
- bool parse_date(const char * date_str, std::time_t * result,
- const int year = -1);
- bool quick_parse_date(const char * date_str, std::time_t * result);
-}
-
-date_t::date_t(const std::string& _when)
-{
- if (! quick_parse_date(_when.c_str(), &when))
- throw new date_error
- (std::string("Invalid date string: ") + _when);
-}
-
-datetime_t::datetime_t(const std::string& _when)
-{
- if (const char * p = std::strchr(_when.c_str(), ' ')) {
- date_t date(std::string(_when, 0, p - _when.c_str()));
-
- struct std::tm moment = *std::localtime(&date.when);
- if (! strptime(++p, "%H:%M:%S", &moment))
- throw new datetime_error
- (std::string("Invalid date/time string: ") + _when);
-
- when = std::mktime(&moment);
- } else {
- when = date_t(_when).when;
- }
-}
-
-datetime_t interval_t::first(const datetime_t& moment) const
-{
- datetime_t quant(begin);
-
- if (moment && moment > quant) {
- // Find an efficient starting point for the upcoming while loop.
- // We want a date early enough that the range will be correct, but
- // late enough that we don't spend hundreds of thousands of loops
- // skipping through time.
-
- struct std::tm * desc = std::localtime(&moment.when);
-
- if (years)
- desc->tm_mon = 0;
- desc->tm_mday = 1;
-
- desc->tm_hour = 0;
- desc->tm_min = 0;
- desc->tm_sec = 0;
- desc->tm_isdst = -1;
-
- quant = std::mktime(desc);
-
- datetime_t temp;
- while (moment >= (temp = increment(quant))) {
- if (quant == temp)
- break;
- quant = temp;
- }
- }
-
- return quant;
-}
-
-datetime_t interval_t::increment(const datetime_t& moment) const
-{
- struct std::tm * desc = std::localtime(&moment.when);
-
- if (years)
- desc->tm_year += years;
- if (months)
- desc->tm_mon += months;
- if (days)
- desc->tm_mday += days;
-
- desc->tm_hour += hours;
- desc->tm_min += minutes;
- desc->tm_sec += seconds;
-
- desc->tm_isdst = -1;
-
- return std::mktime(desc);
-}
-
-namespace {
- void parse_inclusion_specifier(const std::string& word,
- datetime_t * begin, datetime_t * end)
- {
- struct std::tm when;
-
- if (! parse_date_mask(word.c_str(), &when))
- throw new datetime_error(std::string("Could not parse date mask: ") + word);
-
- when.tm_hour = 0;
- when.tm_min = 0;
- when.tm_sec = 0;
- when.tm_isdst = -1;
-
- bool saw_year = true;
- bool saw_mon = true;
- bool saw_day = true;
-
- if (when.tm_year == -1) {
- when.tm_year = date_t::current_year;
- saw_year = false;
- }
- if (when.tm_mon == -1) {
- when.tm_mon = 0;
- saw_mon = false;
- } else {
- saw_year = false; // don't increment by year if month used
- }
- if (when.tm_mday == -1) {
- when.tm_mday = 1;
- saw_day = false;
- } else {
- saw_mon = false; // don't increment by month if day used
- saw_year = false; // don't increment by year if day used
- }
-
- if (begin) {
- *begin = std::mktime(&when);
- if (end)
- *end = interval_t(saw_day ? 86400 : 0, saw_mon ? 1 : 0,
- saw_year ? 1 : 0).increment(*begin);
- }
- else if (end) {
- *end = std::mktime(&when);
- }
- }
-
- inline void read_lower_word(std::istream& in, std::string& word) {
- in >> word;
- for (int i = 0, l = word.length(); i < l; i++)
- word[i] = std::tolower(word[i]);
- }
-
- void parse_date_words(std::istream& in, std::string& word,
- datetime_t * begin, datetime_t * end)
- {
- std::string type;
-
- bool mon_spec = false;
- char buf[32];
-
- if (word == "this" || word == "last" || word == "next") {
- type = word;
- if (! in.eof())
- read_lower_word(in, word);
- else
- word = "month";
- } else {
- type = "this";
- }
-
- if (word == "month") {
- std::strftime(buf, 31, "%B", datetime_t::now.localtime());
- word = buf;
- mon_spec = true;
- }
- else if (word == "year") {
- std::strftime(buf, 31, "%Y", datetime_t::now.localtime());
- word = buf;
- }
-
- parse_inclusion_specifier(word, begin, end);
-
- if (type == "last") {
- if (mon_spec) {
- if (begin)
- *begin = interval_t(0, -1, 0).increment(*begin);
- if (end)
- *end = interval_t(0, -1, 0).increment(*end);
- } else {
- if (begin)
- *begin = interval_t(0, 0, -1).increment(*begin);
- if (end)
- *end = interval_t(0, 0, -1).increment(*end);
- }
- }
- else if (type == "next") {
- if (mon_spec) {
- if (begin)
- *begin = interval_t(0, 1, 0).increment(*begin);
- if (end)
- *end = interval_t(0, 1, 0).increment(*end);
- } else {
- if (begin)
- *begin = interval_t(0, 0, 1).increment(*begin);
- if (end)
- *end = interval_t(0, 0, 1).increment(*end);
- }
- }
- }
-}
-
-void interval_t::parse(std::istream& in)
-{
- std::string word;
-
- while (! in.eof()) {
- read_lower_word(in, word);
- if (word == "every") {
- read_lower_word(in, word);
- if (std::isdigit(word[0])) {
- int quantity = std::atol(word.c_str());
- read_lower_word(in, word);
- if (word == "days")
- days = quantity;
- else if (word == "weeks")
- days = 7 * quantity;
- else if (word == "months")
- months = quantity;
- else if (word == "quarters")
- months = 3 * quantity;
- else if (word == "years")
- years = quantity;
- else if (word == "hours")
- hours = quantity;
- else if (word == "minutes")
- minutes = quantity;
- else if (word == "seconds")
- seconds = quantity;
- }
- else if (word == "day")
- days = 1;
- else if (word == "week")
- days = 7;
- else if (word == "month")
- months = 1;
- else if (word == "quarter")
- months = 3;
- else if (word == "year")
- years = 1;
- else if (word == "hour")
- hours = 1;
- else if (word == "minute")
- minutes = 1;
- else if (word == "second")
- seconds = 1;
- }
- else if (word == "daily")
- days = 1;
- else if (word == "weekly")
- days = 7;
- else if (word == "biweekly")
- days = 14;
- else if (word == "monthly")
- months = 1;
- else if (word == "bimonthly")
- months = 2;
- else if (word == "quarterly")
- months = 3;
- else if (word == "yearly")
- years = 1;
- else if (word == "hourly")
- hours = 1;
- else if (word == "this" || word == "last" || word == "next") {
- parse_date_words(in, word, &begin, &end);
- }
- else if (word == "in") {
- read_lower_word(in, word);
- parse_date_words(in, word, &begin, &end);
- }
- else if (word == "from" || word == "since") {
- read_lower_word(in, word);
- parse_date_words(in, word, &begin, NULL);
- }
- else if (word == "to" || word == "until") {
- read_lower_word(in, word);
- parse_date_words(in, word, NULL, &end);
- }
- else {
- parse_inclusion_specifier(word, &begin, &end);
- }
- }
-}
-
-namespace {
- bool parse_date_mask(const char * date_str, struct std::tm * result)
- {
- if (! date_t::input_format.empty()) {
- std::memset(result, INT_MAX, sizeof(struct std::tm));
- if (strptime(date_str, date_t::input_format.c_str(), result))
- return true;
- }
- for (const char ** f = date_t::formats; *f; f++) {
- std::memset(result, INT_MAX, sizeof(struct std::tm));
- if (strptime(date_str, *f, result))
- return true;
- }
- return false;
- }
-
- bool parse_date(const char * date_str, std::time_t * result, const int year)
- {
- struct std::tm when;
-
- if (! parse_date_mask(date_str, &when))
- return false;
-
- when.tm_hour = 0;
- when.tm_min = 0;
- when.tm_sec = 0;
-
- if (when.tm_year == -1)
- when.tm_year = ((year == -1) ? date_t::current_year : (year - 1900));
-
- if (when.tm_mon == -1)
- when.tm_mon = 0;
-
- if (when.tm_mday == -1)
- when.tm_mday = 1;
-
- *result = std::mktime(&when);
-
- return true;
- }
-
- bool quick_parse_date(const char * date_str, std::time_t * result)
- {
- return parse_date(date_str, result, date_t::current_year + 1900);
- }
-}
diff --git a/datetime.h b/datetime.h
deleted file mode 100644
index 9f85d3f2..00000000
--- a/datetime.h
+++ /dev/null
@@ -1,314 +0,0 @@
-#ifndef _DATETIME_H
-#define _DATETIME_H
-
-#include <ctime>
-#include <sstream>
-
-#include "error.h"
-
-class date_error : public error {
- public:
- date_error(const std::string& reason) throw() : error(reason) {}
- virtual ~date_error() throw() {}
-};
-
-struct interval_t;
-class datetime_t;
-
-class date_t
-{
- date_t(const datetime_t& _when);
-
- protected:
- std::time_t when;
-
- public:
- static date_t now;
- static const char * formats[];
- static int current_year;
- static std::string input_format;
- static std::string output_format;
-
- date_t() : when(0) {}
- date_t(const date_t& _when) : when(_when.when) {}
-
- date_t(const std::time_t _when) : when(_when) {
-#if 0
- struct std::tm * moment = std::localtime(&_when);
- moment->tm_hour = 0;
- moment->tm_min = 0;
- moment->tm_sec = 0;
- when = std::mktime(moment);
-#endif
- }
- date_t(const interval_t& period);
- date_t(const std::string& _when);
-
- virtual ~date_t() {}
-
- date_t& operator=(const date_t& _when) {
- when = _when.when;
- return *this;
- }
- date_t& operator=(const std::time_t _when) {
- return *this = date_t(_when);
- }
- date_t& operator=(const datetime_t& _when) {
- return *this = date_t(_when);
- }
- date_t& operator=(const interval_t& period) {
- return *this = date_t(period);
- }
- date_t& operator=(const std::string& _when) {
- return *this = date_t(_when);
- }
-
- date_t& operator+=(const interval_t& period);
-
- long operator-=(const date_t& date) {
- return (when - date.when) / 86400;
- }
-
- virtual date_t& operator+=(const long days) {
- // jww (2006-03-26): This is not accurate enough when DST is in effect!
- assert(0);
- when += days * 86400;
- return *this;
- }
- virtual date_t& operator-=(const long days) {
- assert(0);
- when -= days * 86400;
- return *this;
- }
-
-#define DEF_DATE_OP(OP) \
- bool operator OP(const date_t& other) const { \
- return when OP other.when; \
- }
-
- DEF_DATE_OP(<)
- DEF_DATE_OP(<=)
- DEF_DATE_OP(>)
- DEF_DATE_OP(>=)
- DEF_DATE_OP(==)
- DEF_DATE_OP(!=)
-
- operator bool() const {
- return when != 0;
- }
- operator std::time_t() {
- return when;
- }
- operator std::string() const {
- return to_string();
- }
-
- std::string to_string(const std::string& format = output_format) const {
- char buf[64];
- std::strftime(buf, 63, format.c_str(), localtime());
- return buf;
- }
-
- int year() const {
- return localtime()->tm_year + 1900;
- }
- int month() const {
- return localtime()->tm_mon + 1;
- }
- int day() const {
- return localtime()->tm_mday;
- }
- int wday() const {
- return localtime()->tm_wday;
- }
-
- std::tm * localtime() const {
- return std::localtime(&when);
- }
-
- void write(std::ostream& out,
- const std::string& format = output_format) const {
- out << to_string(format);
- }
-
- friend class datetime_t;
- friend struct interval_t;
-};
-
-inline long operator-(const date_t& left, const date_t& right) {
- date_t temp(left);
- temp -= right;
- return (long)temp;
-}
-
-inline date_t operator+(const date_t& left, const long days) {
- date_t temp(left);
- temp += days;
- return temp;
-}
-
-inline date_t operator-(const date_t& left, const long days) {
- date_t temp(left);
- temp -= days;
- return temp;
-}
-
-inline std::ostream& operator<<(std::ostream& out, const date_t& moment) {
- moment.write(out);
- return out;
-}
-
-class datetime_error : public error {
- public:
- datetime_error(const std::string& reason) throw() : error(reason) {}
- virtual ~datetime_error() throw() {}
-};
-
-class datetime_t : public date_t
-{
- public:
- static datetime_t now;
-
- datetime_t() : date_t() {}
- datetime_t(const datetime_t& _when) : date_t(_when.when) {}
- datetime_t(const date_t& _when) : date_t(_when) {}
-
- datetime_t(const std::time_t _when) : date_t(_when) {}
- datetime_t(const std::string& _when);
-
- datetime_t& operator=(const datetime_t& _when) {
- when = _when.when;
- return *this;
- }
- datetime_t& operator=(const date_t& _when) {
- when = _when.when;
- return *this;
- }
- datetime_t& operator=(const std::time_t _when) {
- return *this = datetime_t(_when);
- }
- datetime_t& operator=(const std::string& _when) {
- return *this = datetime_t(_when);
- }
-
- long operator-=(const datetime_t& date) {
- return when - date.when;
- }
-
- virtual datetime_t& operator+=(const long secs) {
- when += secs;
- return *this;
- }
- virtual datetime_t& operator-=(const long secs) {
- when -= secs;
- return *this;
- }
-
-#define DEF_DATETIME_OP(OP) \
- bool operator OP(const datetime_t& other) const { \
- return when OP other.when; \
- }
-
- DEF_DATETIME_OP(<)
- DEF_DATETIME_OP(<=)
- DEF_DATETIME_OP(>)
- DEF_DATETIME_OP(>=)
- DEF_DATETIME_OP(==)
- DEF_DATETIME_OP(!=)
-
- int hour() const {
- return localtime()->tm_hour;
- }
- int min() const {
- return localtime()->tm_min;
- }
- int sec() const {
- return localtime()->tm_sec;
- }
-};
-
-inline long operator-(const datetime_t& left, const datetime_t& right) {
- std::time_t left_time(left);
- std::time_t right_time(right);
- return left_time - right_time;
-}
-
-inline datetime_t operator+(const datetime_t& left, const long seconds) {
- datetime_t temp(left);
- temp += seconds;
- return temp;
-}
-
-inline datetime_t operator-(const datetime_t& left, const long seconds) {
- datetime_t temp(left);
- temp -= seconds;
- return temp;
-}
-
-inline std::ostream& operator<<(std::ostream& out,
- const datetime_t& moment) {
- char buf[64];
- std::strftime(buf, 63, (date_t::output_format + " %H:%M:%S").c_str(),
- moment.localtime());
- out << buf;
- return out;
-}
-
-struct interval_t
-{
- unsigned short years;
- unsigned short months;
- unsigned short days;
- unsigned short hours;
- unsigned short minutes;
- unsigned short seconds;
-
- datetime_t begin;
- datetime_t end;
-
- interval_t(int _days = 0, int _months = 0, int _years = 0,
- const date_t& _begin = date_t(),
- const date_t& _end = date_t())
- : years(_years), months(_months), days(_days),
- hours(0), minutes(0), seconds(0),
- begin(_begin), end(_end) {}
-
- interval_t(const std::string& desc)
- : years(0), months(0), days(0),
- hours(0), minutes(0), seconds(0) {
- std::istringstream stream(desc);
- parse(stream);
- }
-
- operator bool() const {
- return (years > 0 || months > 0 || days > 0 ||
- hours > 0 || minutes > 0 || seconds > 0);
- }
-
- void start(const datetime_t& moment) {
- begin = first(moment);
- }
- datetime_t first(const datetime_t& moment = datetime_t()) const;
- datetime_t increment(const datetime_t&) const;
-
- void parse(std::istream& in);
-};
-
-inline date_t::date_t(const interval_t& period) {
- when = period.first().when;
-}
-
-inline date_t& date_t::operator+=(const interval_t& period) {
- return *this = period.increment(*this);
-}
-
-inline date_t::date_t(const datetime_t& _when) {
- assert(0);
- struct std::tm * moment = _when.localtime();
- moment->tm_hour = 0;
- moment->tm_min = 0;
- moment->tm_sec = 0;
- when = std::mktime(moment);
-}
-
-#endif // _DATETIME_H
diff --git a/debug.cc b/debug.cc
deleted file mode 100644
index b3b140bc..00000000
--- a/debug.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-#include "debug.h"
-
-#ifdef DEBUG_ENABLED
-
-#include <map>
-#include <fstream>
-
-#include <unistd.h> // for the `write' method
-
-int offset = 0;
-
-std::map<void *, int> ptrs;
-
-#define PRINT_INC(x) { \
- char buf[128]; \
- std::sprintf(buf, "%d: %p: %s", ++offset, ptr, x); \
- write(1, buf, std::strlen(buf)); \
-}
-
-#define PRINT_DEC(x) { \
- char buf[128]; \
- std::sprintf(buf, "%d: %p: %s", --offset, ptr, x); \
- write(1, buf, std::strlen(buf)); \
-}
-
-void * operator new(std::size_t size) throw (std::bad_alloc) {
- void * ptr = std::malloc(size);
- if (DEBUG("debug.alloc")) {
- PRINT_INC("void * operator new(std::size_t size) throw (std::bad_alloc)\n");
- }
- return ptr;
-}
-void * operator new[](std::size_t size) throw (std::bad_alloc) {
- void * ptr = std::malloc(size);
- if (DEBUG("debug.alloc")) {
- PRINT_INC("void * operator new[](std::size_t) throw (std::bad_alloc)\n");
- }
- return ptr;
-}
-void * operator new(std::size_t size, const std::nothrow_t&) throw() {
- void * ptr = std::malloc(size);
- if (DEBUG("debug.alloc")) {
- PRINT_INC("void * operator new(std::size_t size, const std::nothrow_t&) throw()\n");
- }
- return ptr;
-}
-void * operator new[](std::size_t size, const std::nothrow_t&) throw() {
- void * ptr = std::malloc(size);
- if (DEBUG("debug.alloc")) {
- PRINT_INC("void * operator new[](std::size_t size, const std::nothrow_t&) throw()\n");
- }
- return ptr;
-}
-void operator delete(void * ptr) throw() {
- if (DEBUG("debug.alloc")) {
- PRINT_DEC("void operator delete(void * ptr) throw()\n");
- }
- std::free(ptr);
-}
-void operator delete[](void * ptr) throw() {
- if (DEBUG("debug.alloc")) {
- PRINT_DEC("void operator delete[](void * ptr) throw()\n");
- }
- std::free(ptr);
-}
-void operator delete(void * ptr, const std::nothrow_t&) throw() {
- if (DEBUG("debug.alloc")) {
- PRINT_DEC("void operator delete(void * ptr, const std::nothrow_t&) throw()\n");
- }
- std::free(ptr);
-}
-void operator delete[](void * ptr, const std::nothrow_t&) throw() {
- if (DEBUG("debug.alloc")) {
- PRINT_DEC("void operator delete[](void * ptr, const std::nothrow_t&) throw()\n");
- }
- std::free(ptr);
-}
-
-std::ostream * _debug_stream = &std::cerr;
-bool _free_debug_stream = false;
-
-bool _debug_active(const char * const cls) {
- if (char * debug = std::getenv("DEBUG_CLASS")) {
- static const char * error;
- static int erroffset;
- static int ovec[30];
- static pcre * class_regexp = pcre_compile(debug, PCRE_CASELESS,
- &error, &erroffset, NULL);
- return pcre_exec(class_regexp, NULL, cls, std::strlen(cls),
- 0, 0, ovec, 30) >= 0;
- }
- return false;
-}
-
-static struct init_streams {
- init_streams() {
- // If debugging is enabled and DEBUG_FILE is set, all debugging
- // output goes to that file.
- if (const char * p = std::getenv("DEBUG_FILE")) {
- _debug_stream = new std::ofstream(p);
- _free_debug_stream = true;
- }
- }
- ~init_streams() {
- if (_free_debug_stream && _debug_stream) {
- delete _debug_stream;
- _debug_stream = NULL;
- }
- }
-} _debug_init;
-
-#endif // DEBUG_ENABLED
-
-#if DEBUG_LEVEL >= BETA
-
-#include <string>
-
-void debug_assert(const std::string& reason,
- const std::string& file,
- unsigned long line)
-{
- throw new fatal_assert(reason, new file_context(file, line));
-}
-
-#endif
diff --git a/debug.h b/debug.h
deleted file mode 100644
index 81083ad3..00000000
--- a/debug.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef _DEBUG_H
-#define _DEBUG_H
-
-#define DEVELOPER 4
-#define ALPHA 3
-#define BETA 2
-#define RELEASE 1
-#define NO_SEATBELT 0
-
-#ifndef DEBUG_LEVEL
-#define DEBUG_LEVEL NO_SEATBELT
-#endif
-
-#if DEBUG_LEVEL >= RELEASE
-#include "error.h"
-
-#ifdef assert
-#undef assert
-#endif
-#if DEBUG_LEVEL >= BETA
-void debug_assert(const std::string& reason,
- const std::string& file,
- unsigned long line);
-#define assert(x) \
- if (! (x)) \
- debug_assert(#x, __FILE__, __LINE__)
-#else
-#define assert(x) \
- if (! (x)) \
- throw new fatal_assert(#x, new file_context(__FILE__, __LINE__))
-#endif
-#else
-#ifdef assert
-#undef assert
-#endif
-#define assert(x)
-#endif
-
-//////////////////////////////////////////////////////////////////////
-//
-// General debugging facilities
-//
-// - In developer level, all checking and debugging facilities are
-// active.
-//
-// - Alpha level does not include performance degrading
-// VALIDATE calls.
-//
-// - Beta level is like Alpha, but does not include debugging
-// facilities.
-//
-// - Release level does not include CONFIRM checks, but does include
-// assert calls.
-//
-// - Running with no seatbelt disables all checking except for normal
-// syntax and semantic error checking.
-
-#if DEBUG_LEVEL >= ALPHA
-
-#include <pcre.h>
-#include <cstring>
-#include <new>
-#include <iostream>
-#include <cstdlib>
-
-#include "datetime.h"
-
-#define DEBUG_ENABLED
-
-extern std::ostream * _debug_stream;
-extern bool _free_debug_stream;
-
-bool _debug_active(const char * const cls);
-
-#define DEBUG_CLASS(cls) static const char * const _debug_cls = (cls)
-
-#define DEBUG(cls) (_debug_active(cls))
-#define DEBUG_() DEBUG(_debug_cls)
-
-#define DEBUG_IF(cls) if (_debug_active(cls))
-#define DEBUG_IF_() if (_debug_active(_debug_cls))
-
-#define DEBUG_PRINT(cls, x) \
- if (_debug_stream && _debug_active(cls)) { \
- *_debug_stream << x << std::endl; \
- }
-#define DEBUG_PRINT_(x) DEBUG_PRINT(_debug_cls, x)
-
-#define DEBUG_PRINT_TIME(cls, x) { \
- DEBUG_PRINT(cls, #x << " is " << x); \
-}
-
-#define DEBUG_PRINT_TIME_(x) DEBUG_PRINT_TIME(_debug_cls, x)
-
-#define CONFIRM(x) assert(x)
-
-#if DEBUG_LEVEL == DEVELOPER
-#define VALIDATE(x) assert(x)
-#else
-#define VALIDATE(x)
-#endif
-
-void * operator new(std::size_t) throw (std::bad_alloc);
-void * operator new[](std::size_t) throw (std::bad_alloc);
-void operator delete(void*) throw();
-void operator delete[](void*) throw();
-void * operator new(std::size_t, const std::nothrow_t&) throw();
-void * operator new[](std::size_t, const std::nothrow_t&) throw();
-void operator delete(void*, const std::nothrow_t&) throw();
-void operator delete[](void*, const std::nothrow_t&) throw();
-
-#else // DEBUG_LEVEL
-
-#define DEBUG_CLASS(cls)
-#define DEBUG(cls) 0
-#define DEBUG_() 0
-#define DEBUG_IF(cls)
-#define DEBUG_IF_()
-#define DEBUG_PRINT(cls, x)
-#define DEBUG_PRINT_(x)
-#define DEBUG_PRINT_TIME(cls, x)
-#define DEBUG_PRINT_TIME_(x)
-
-#define VALIDATE(x)
-
-#if DEBUG_LEVEL == NO_SEATBELT
-
-#ifdef assert
-#undef assert
-#endif
-#define assert(x)
-#define CONFIRM(x)
-
-#elif DEBUG_LEVEL >= RELEASE
-
-#define CONFIRM(x)
-
-#elif DEBUG_LEVEL >= BETA
-
-#define CONFIRM(x) assert(x)
-
-#endif
-
-#endif // DEBUG_LEVEL
-
-#endif // _DEBUG_H
diff --git a/error.h b/error.h
index 7f77db0f..771e9f10 100644
--- a/error.h
+++ b/error.h
@@ -7,6 +7,8 @@
#include <sstream>
#include <list>
+namespace ledger {
+
class error_context
{
public:
@@ -63,15 +65,13 @@ class line_context : public error_context {
//////////////////////////////////////////////////////////////////////
-class str_exception : public std::exception {
- protected:
- std::string reason;
+class str_exception : public std::logic_error {
public:
std::list<error_context *> context;
- str_exception(const std::string& _reason,
+ str_exception(const std::string& why,
error_context * ctxt = NULL) throw()
- : reason(_reason) {
+ : std::logic_error(why) {
if (ctxt)
context.push_back(ctxt);
}
@@ -95,31 +95,36 @@ class str_exception : public std::exception {
(*i)->describe(out);
}
}
+};
- virtual const char* what() const throw() {
- return reason.c_str();
+#define DECLARE_EXCEPTION(kind, name) \
+ class name : public kind { \
+ public: \
+ name(const std::string& why, error_context * ctxt = NULL) throw() \
+ : kind(why, ctxt) {} \
}
-};
class error : public str_exception {
public:
- error(const std::string& reason, error_context * ctxt = NULL) throw()
- : str_exception(reason, ctxt) {}
+ error(const std::string& why, error_context * ctxt = NULL) throw()
+ : str_exception(why, ctxt) {}
virtual ~error() throw() {}
};
class fatal : public str_exception {
public:
- fatal(const std::string& reason, error_context * ctxt = NULL) throw()
- : str_exception(reason, ctxt) {}
+ fatal(const std::string& why, error_context * ctxt = NULL) throw()
+ : str_exception(why, ctxt) {}
virtual ~fatal() throw() {}
};
class fatal_assert : public fatal {
public:
- fatal_assert(const std::string& reason, error_context * ctxt = NULL) throw()
- : fatal(std::string("assertion failed '") + reason + "'", ctxt) {}
+ fatal_assert(const std::string& why, error_context * ctxt = NULL) throw()
+ : fatal(std::string("assertion failed '") + why + "'", ctxt) {}
virtual ~fatal_assert() throw() {}
};
+} // namespace ledger
+
#endif // _ERROR_H
diff --git a/fdstream.hpp b/fdstream.hpp
index a74a5781..ffcf5709 100644
--- a/fdstream.hpp
+++ b/fdstream.hpp
@@ -1,3 +1,34 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
/* The following code declares classes to read from and write to
* file descriptore or file handles.
*
diff --git a/flags.h b/flags.h
new file mode 100644
index 00000000..5ae8b60f
--- /dev/null
+++ b/flags.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FLAGS_H
+#define _FLAGS_H
+
+template <typename T = boost::uint_least8_t>
+class supports_flags
+{
+public:
+ typedef T flags_t;
+
+protected:
+ flags_t flags_;
+
+public:
+ supports_flags() : flags_(0) {}
+ supports_flags(const flags_t arg) : flags_(arg) {}
+
+ flags_t flags() const {
+ return flags_;
+ }
+ bool has_flags(const flags_t arg) const {
+ return flags_ & arg;
+ }
+
+ void set_flags(const flags_t arg) {
+ flags_ = arg;
+ }
+ void clear_flags() {
+ flags_ = 0;
+ }
+ void add_flags(const flags_t arg) {
+ flags_ |= arg;
+ }
+ void drop_flags(const flags_t arg) {
+ flags_ &= ~arg;
+ }
+};
+
+template <typename T = boost::uint_least8_t>
+class delegates_flags : public boost::noncopyable
+{
+public:
+ typedef T flags_t;
+
+protected:
+ supports_flags<T>& flags_;
+
+public:
+ delegates_flags() : flags_() {}
+ delegates_flags(supports_flags<T>& arg) : flags_(arg) {}
+
+ flags_t flags() const {
+ return flags_.flags();
+ }
+ bool has_flags(const flags_t arg) const {
+ return flags_.has_flags(arg);
+ }
+
+ void set_flags(const flags_t arg) {
+ flags_.set_flags(arg);
+ }
+ void clear_flags() {
+ flags_.clear_flags();
+ }
+ void add_flags(const flags_t arg) {
+ flags_.add_flags(arg);
+ }
+ void drop_flags(const flags_t arg) {
+ flags_.drop_flags(arg);
+ }
+};
+
+#endif // _FLAGS_H
diff --git a/journal.h b/journal.h
index a459160c..c2b8d030 100644
--- a/journal.h
+++ b/journal.h
@@ -7,12 +7,9 @@
#include <iostream>
#include "amount.h"
-#include "datetime.h"
#include "value.h"
#include "valexpr.h"
-#include "error.h"
-#include "debug.h"
-#include "util.h"
+#include "utils.h"
namespace ledger {
diff --git a/main.py b/main.py
new file mode 100644
index 00000000..57ba84c5
--- /dev/null
+++ b/main.py
@@ -0,0 +1,373 @@
+#!/usr/bin/env python
+
+# Ledger, the command-line accounting tool
+#
+# Copyright (c) 2003-2004, New Artisans LLC. All rights reserved.
+#
+# This program is made available under the terms of the BSD Public
+# License. See the LICENSE file included with the distribution for
+# details and disclaimer.
+#
+# This script provides a Python front-end to the ledger library, and
+# replicates the functionality of the C++ front-end, main.cc. It is
+# provided as an example, and as a starting point for creating custom
+# front-ends based on the Ledger module. See the documentation for an
+# API reference, and how to use this module.
+
+import os
+import sys
+import string
+import time
+
+true, false = 1, 0
+
+from ledger import *
+
+# Create the main journal object, into which all entries will be
+# recorded. Once done, the 'journal' may be iterated to yield those
+# entries, in the same order as which they appeared in the journal
+# file.
+
+journal = Journal ()
+
+# This call registers all of the default command-line options that
+# Ledger supports into the option handling mechanism. Skip this call
+# if you wish to do all of your own processing -- in which case simply
+# modify the 'config' object however you like.
+
+add_config_option_handlers ()
+
+averages = {}
+compute_monthly_avg = false
+
+def get_index (xact):
+ return time.strftime ("%Y/%m", time.localtime (xact.entry.date))
+
+class ComputeMonthlyAvg (TransactionHandler):
+ def __call__ (self, xact):
+ global averages
+ index = get_index (xact)
+ if not averages.has_key(index):
+ averages[index] = [Value (), 0]
+ add_transaction_to (xact, averages[index][0])
+ averages[index][1] += 1
+ TransactionHandler.__call__ (self, xact)
+
+def monthly_avg (details):
+ index = get_index (xact)
+ return averages[index][0] / averages[index][1]
+
+def show_monthly_averages (arg):
+ global compute_monthly_avg
+ compute_monthly_avg = true
+ config.report_period = "monthly";
+ config.total_expr = "@monthly_avg()"
+
+add_option_handler ("monthly-avg", "", show_monthly_averages)
+
+# Process the command-line arguments, test whether caching should be
+# enabled, and then process any option settings from the execution
+# environment. Some historical environment variable names are also
+# supported.
+
+args = process_arguments (sys.argv[1:])
+config.use_cache = not config.data_file
+process_environment (os.environ, "LEDGER_")
+
+if os.environ.has_key ("LEDGER"):
+ process_option ("file", os.getenv ("LEDGER"))
+if os.environ.has_key ("PRICE_HIST"):
+ process_option ("price-db", os.getenv ("PRICE_HIST"))
+if os.environ.has_key ("PRICE_EXP"):
+ process_option ("price-exp", os.getenv ("PRICE_EXP"))
+
+# If no argument remain, then no command word was given. Report the
+# default help text and exit.
+
+if len (args) == 0:
+ option_help ()
+ sys.exit (0)
+
+# The command word is in the first argument. Canonicalize it to a
+# unique, simple form that the remaining code can use to find out
+# which command was specified.
+
+command = args.pop (0);
+
+if command == "balance" or command == "bal" or command == "b":
+ command = "b"
+elif command == "register" or command == "reg" or command == "r":
+ command = "r"
+elif command == "print" or command == "p":
+ command = "p"
+elif command == "output":
+ command = "w"
+elif command == "emacs":
+ command = "x"
+elif command == "xml":
+ command = "X"
+elif command == "entry":
+ command = "e"
+elif command == "equity":
+ command = "E"
+elif command == "prices":
+ command = "P"
+elif command == "pricesdb":
+ command = "D";
+else:
+ print "Unrecognized command:", command
+ sys.exit (1)
+
+# Create all the parser objects to be used. They are all registered,
+# so that Ledger will try each one in turn whenever it is presented
+# with a data file. They are attempted in reverse order to their
+# registry. Note that Gnucash parsing is only available if the Ledger
+# module was built with such support (which requires the expat C
+# library).
+
+bin_parser = BinaryParser ()
+gnucash_parser = None
+xml_parser = None
+try: xml_parser = GnucashParser ()
+except: pass
+try: gnucash_parser = GnucashParser ()
+except: pass
+try: ofx_parser = OfxParser ()
+except: pass
+qif_parser = QifParser ()
+text_parser = TextualParser ()
+
+register_parser (bin_parser)
+if xml_parser:
+ register_parser (xml_parser)
+if gnucash_parser:
+ register_parser (gnucash_parser)
+if ofx_parser:
+ register_parser (ofx_parser)
+register_parser (qif_parser)
+register_parser (text_parser)
+
+# Parse all entries from the user specified locations (found in
+# 'config') into the journal object we created. The two parsers given
+# as explicit arguments indicate: the parser to be used for standard
+# input, and the parser to be used for cache files.
+
+parse_ledger_data (journal, bin_parser)
+
+# Now that everything has been correctly parsed (parse_ledger_data
+# would have thrown an exception if not), we can take time to further
+# process the configuration options. This changes the configuration a
+# bit based on previous option settings, the command word, and the
+# remaining arguments.
+
+config.process_options (command, args);
+
+# If the command is "e", use the method journal.derive_entry to create
+# a brand new entry based on the arguments given.
+
+new_entry = None
+if command == "e":
+ new_entry = derive_new_entry (journal, args)
+ if new_entry is None:
+ sys.exit (1)
+
+# Determine the format string to used, based on the command.
+
+if config.format_string:
+ format = config.format_string
+elif command == "b":
+ format = config.balance_format
+elif command == "r":
+ format = config.register_format
+elif command == "E":
+ format = config.equity_format
+elif command == "P":
+ min_val = 0
+ def vmin(d, val):
+ global min_val
+ if not min_val or val < min_val:
+ min_val = val
+ return val
+ return min_val
+
+ max_val = 0
+ def vmax(d, val):
+ global max_val
+ if not max_val or val > max_val:
+ max_val = val
+ return val
+ return max_val
+
+ format = config.prices_format
+elif command == "D":
+ format = config.pricesdb_format
+elif command == "w":
+ format = config.write_xact_format
+else:
+ format = config.print_format
+
+# Configure the output file
+
+if config.output_file:
+ out = open (config.output_file, "w")
+else:
+ out = sys.stdout
+
+# Set the final transaction handler: for balances and equity reports,
+# it will simply add the value of the transaction to the account's
+# xdata, which is used a bit later to report those totals. For all
+# other reports, the transaction data is sent to the configured output
+# location (default is sys.stdout).
+
+if command == "b" or command == "E":
+ handler = SetAccountValue ()
+elif command == "p" or command == "e":
+ handler = FormatEntries (out, format)
+elif command == "x":
+ handler = FormatEmacsTransactions (out)
+elif command == "X":
+ handler = FormatXmlEntries (out, config.show_totals)
+else:
+ handler = FormatTransactions (out, format)
+
+if command == "w":
+ write_textual_journal(journal, args, handler, out);
+else:
+ # Chain transaction filters on top of the base handler. Most of these
+ # filters customize the output for reporting. None of this is done
+ # for balance or equity reports, which don't need it.
+
+ if not (command == "b" or command == "E"):
+ if config.head_entries or config.tail_entries:
+ handler = TruncateEntries (handler, config.head_entries,
+ config.tail_entries)
+
+ if config.display_predicate:
+ handler = FilterTransactions (handler, config.display_predicate)
+
+ handler = CalcTransactions (handler)
+
+ if config.reconcile_balance:
+ reconcilable = False
+ if config.reconcile_balance == "<all>":
+ reconcilable = True
+ else:
+ target_balance = Value (config.reconcile_balance)
+
+ cutoff = time.time ()
+ if config.reconcile_date:
+ cutoff = parse_date (config.reconcile_date)
+
+ handler = ReconcileTransactions (handler, target_balance,
+ cutoff, reconcilable)
+
+ if config.sort_string:
+ handler = SortTransactions (handler, config.sort_string)
+
+ if config.show_revalued:
+ handler = ChangedValueTransactions (handler,
+ config.show_revalued_only)
+
+ if config.show_collapsed:
+ handler = CollapseTransactions (handler);
+
+ if config.show_subtotal and not (command == "b" or command == "E"):
+ handler = SubtotalTransactions (handler)
+
+ if config.days_of_the_week:
+ handler = DowTransactions (handler)
+ elif config.by_payee:
+ handler = ByPayeeTransactions (handler)
+
+ if config.report_period:
+ handler = IntervalTransactions (handler, config.report_period,
+ config.report_period_sort)
+ handler = SortTransactions (handler, "d")
+
+ if compute_monthly_avg:
+ handler = ComputeMonthlyAvg (handler)
+
+ # The next set of transaction filters are used by all reports.
+
+ if config.show_inverted:
+ handler = InvertTransactions (handler)
+
+ if config.show_related:
+ handler = RelatedTransactions (handler, config.show_all_related)
+
+ if config.predicate:
+ handler = FilterTransactions (handler, config.predicate)
+
+ if config.budget_flags:
+ handler = BudgetTransactions (handler, config.budget_flags)
+ handler.add_period_entries (journal)
+ elif config.forecast_limit:
+ handler = ForecastTransactions (handler, config.forecast_limit)
+ handler.add_period_entries (journal)
+
+ if config.comm_as_payee:
+ handler = SetCommAsPayee (handler)
+
+ # Walk the journal's entries, and pass each entry's transaction to the
+ # handler chain established above. And although a journal's entries
+ # can be walked using Python, it is significantly faster to do this
+ # simple walk in C++, using `walk_entries'.
+ #
+ # if command == "e":
+ # for xact in new_entry:
+ # handler (xact)
+ # else:
+ # for entry in journal:
+ # for xact in entry:
+ # handler (xact)
+
+ if command == "e":
+ walk_transactions (new_entry, handler)
+ elif command == "P" or command == "D":
+ walk_commodities (handler)
+ else:
+ walk_entries (journal, handler)
+
+ # Flush the handlers, causing them to output whatever data is still
+ # pending.
+
+ if command != "P" and command != "D":
+ handler.flush ()
+
+# For the balance and equity reports, the account totals now need to
+# be displayed. This is different from outputting transactions, in
+# that we are now outputting account totals to display a summary of
+# the transactions that were just walked.
+
+if command == "b":
+ acct_formatter = FormatAccount (out, format, config.display_predicate)
+ sum_accounts (journal.master)
+ walk_accounts (journal.master, acct_formatter, config.sort_string)
+ acct_formatter.final (journal.master)
+ acct_formatter.flush ()
+
+ if account_has_xdata (journal.master):
+ xdata = account_xdata (journal.master)
+ if not config.show_collapsed and xdata.total:
+ out.write("--------------------\n")
+ xdata.value = xdata.total
+ # jww (2005-02-15): yet to convert
+ #acct_formatter.format.format (out, details_t (journal.master))
+
+elif command == "E":
+ acct_formatter = FormatEquity (out, format, config.display_predicate)
+ sum_accounts (journal.master)
+ walk_accounts (journal.master, acct_formatter, config.sort_string)
+ acct_formatter.flush ()
+
+# If it were important to clean things up, we would have to clear out
+# the accumulated xdata at this point:
+
+#clear_all_xdata ()
+
+# If the cache is being used, and is dirty, update it now.
+
+if config.use_cache and config.cache_dirty and config.cache_file:
+ write_binary_journal (config.cache_file, journal);
+
+# We're done!
diff --git a/mask.cc b/mask.cc
index 972b24ce..959df8ea 100644
--- a/mask.cc
+++ b/mask.cc
@@ -1,14 +1,42 @@
-#include "mask.h"
-#include "debug.h"
-#include "util.h"
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
-#include <cstdlib>
+#include "mask.h"
-#include <pcre.h>
+namespace ledger {
-mask_t::mask_t(const std::string& pat) : exclude(false)
+mask_t::mask_t(const string& pat) : exclude(false)
{
const char * p = pat.c_str();
+
if (*p == '-') {
exclude = true;
p++;
@@ -20,34 +48,8 @@ mask_t::mask_t(const std::string& pat) : exclude(false)
while (std::isspace(*p))
p++;
}
- pattern = p;
-
- const char *error;
- int erroffset;
- regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
- &error, &erroffset, NULL);
- if (! regexp)
- throw new mask_error(std::string("Failed to compile regexp '") +
- pattern + "'");
-}
-mask_t::mask_t(const mask_t& m) : exclude(m.exclude), pattern(m.pattern)
-{
- const char *error;
- int erroffset;
- regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
- &error, &erroffset, NULL);
- assert(regexp);
-}
-
-mask_t::~mask_t() {
- pcre_free((pcre *)regexp);
+ expr.assign(p);
}
-bool mask_t::match(const std::string& str) const
-{
- static int ovec[30];
- int result = pcre_exec((pcre *)regexp, NULL,
- str.c_str(), str.length(), 0, 0, ovec, 30);
- return result >= 0 && ! exclude;
-}
+} // namespace ledger
diff --git a/mask.h b/mask.h
index 6d4790a3..daae014f 100644
--- a/mask.h
+++ b/mask.h
@@ -1,29 +1,55 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#ifndef _MASK_H
#define _MASK_H
-#include <string>
-#include <exception>
+#include "utils.h"
-#include "error.h"
+namespace ledger {
class mask_t
{
public:
- bool exclude;
- std::string pattern;
- void * regexp;
+ bool exclude;
+ boost::regex expr;
- explicit mask_t(const std::string& pattern);
- mask_t(const mask_t&);
- ~mask_t();
+ explicit mask_t(const string& pattern);
+ mask_t(const mask_t& m) : exclude(m.exclude), expr(m.expr) {}
- bool match(const std::string& str) const;
+ bool match(const string& str) const {
+ return boost::regex_match(str, expr) && ! exclude;
+ }
};
-class mask_error : public error {
- public:
- mask_error(const std::string& reason) throw() : error(reason) {}
- virtual ~mask_error() throw() {}
-};
+} // namespace ledger
#endif // _MASK_H
diff --git a/parser.h b/parser.h
index 6178d293..cea67e41 100644
--- a/parser.h
+++ b/parser.h
@@ -57,6 +57,85 @@ class parse_error : public error {
virtual ~parse_error() throw() {}
};
+/************************************************************************
+ *
+ * General utility parsing functions
+ */
+
+inline char * skip_ws(char * ptr) {
+ while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
+ ptr++;
+ return ptr;
+}
+
+inline char * next_element(char * buf, bool variable = false) {
+ for (char * p = buf; *p; p++) {
+ if (! (*p == ' ' || *p == '\t'))
+ continue;
+
+ if (! variable) {
+ *p = '\0';
+ return skip_ws(p + 1);
+ }
+ else if (*p == '\t') {
+ *p = '\0';
+ return skip_ws(p + 1);
+ }
+ else if (*(p + 1) == ' ') {
+ *p = '\0';
+ return skip_ws(p + 2);
+ }
+ }
+ return NULL;
+}
+
+inline char peek_next_nonws(std::istream& in) {
+ char c = in.peek();
+ while (! in.eof() && std::isspace(c)) {
+ in.get(c);
+ c = in.peek();
+ }
+ return c;
+}
+
+#define READ_INTO(str, targ, size, var, cond) { \
+ char * _p = targ; \
+ var = str.peek(); \
+ while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
+ str.get(var); \
+ if (str.eof()) \
+ break; \
+ if (var == '\\') { \
+ str.get(var); \
+ if (in.eof()) \
+ break; \
+ } \
+ *_p++ = var; \
+ var = str.peek(); \
+ } \
+ *_p = '\0'; \
+}
+
+#define READ_INTO_(str, targ, size, var, idx, cond) { \
+ char * _p = targ; \
+ var = str.peek(); \
+ while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
+ str.get(var); \
+ if (str.eof()) \
+ break; \
+ idx++; \
+ if (var == '\\') { \
+ str.get(var); \
+ if (in.eof()) \
+ break; \
+ idx++; \
+ } \
+ *_p++ = var; \
+ var = str.peek(); \
+ } \
+ *_p = '\0'; \
+}
+
} // namespace ledger
#endif // _PARSER_H
diff --git a/pushvar.h b/pushvar.h
new file mode 100644
index 00000000..98944481
--- /dev/null
+++ b/pushvar.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file pushvar.h
+ * @author John Wiegley
+ * @date Sun May 6 20:10:52 2007
+ *
+ * @brief Adds a facility to C++ for handling "scoped yet global".
+ */
+
+#ifndef _PUSHVAR_H
+#define _PUSHVAR_H
+
+template <typename T>
+class push_variable : public boost::noncopyable
+{
+ T& var;
+ T prev;
+ bool enabled;
+
+public:
+ explicit push_variable(T& _var)
+ : var(_var), prev(var), enabled(true) {}
+ explicit push_variable(T& _var, const T& value)
+ : var(_var), prev(var), enabled(true) {
+ var = value;
+ }
+ ~push_variable() {
+ if (enabled)
+ var = prev;
+ }
+
+ void clear() {
+ enabled = false;
+ }
+};
+
+#endif // _PUSHVAR_H
diff --git a/py_amount.cc b/py_amount.cc
new file mode 100644
index 00000000..07be1512
--- /dev/null
+++ b/py_amount.cc
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+#include "amount.h"
+
+#include <boost/python/exception_translator.hpp>
+#include <boost/python/implicit.hpp>
+#include <boost/python/args.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+amount_t py_round_0(const amount_t& amount) {
+ return amount.round();
+}
+amount_t py_round_1(const amount_t& amount, amount_t::precision_t prec) {
+ return amount.round(prec);
+}
+
+double py_to_double_0(amount_t& amount) {
+ return amount.to_double();
+}
+double py_to_double_1(amount_t& amount, bool no_check) {
+ return amount.to_double(no_check);
+}
+
+long py_to_long_0(amount_t& amount) {
+ return amount.to_long();
+}
+long py_to_long_1(amount_t& amount, bool no_check) {
+ return amount.to_long(no_check);
+}
+
+boost::optional<amount_t> py_value_0(const amount_t& amount) {
+ return amount.value();
+}
+boost::optional<amount_t> py_value_1(const amount_t& amount,
+ const boost::optional<moment_t>& moment) {
+ return amount.value(moment);
+}
+
+void py_parse_2(amount_t& amount, object in, unsigned char flags) {
+ if (PyFile_Check(in.ptr())) {
+ pyifstream instr(reinterpret_cast<PyFileObject *>(in.ptr()));
+ amount.parse(instr, flags);
+ } else {
+ PyErr_SetString(PyExc_IOError,
+ "Argument to amount.parse(file) is not a file object");
+ }
+}
+void py_parse_1(amount_t& amount, object in) {
+ py_parse_2(amount, in, 0);
+}
+
+void py_parse_str_1(amount_t& amount, const string& str) {
+ amount.parse(str);
+}
+void py_parse_str_2(amount_t& amount, const string& str, unsigned char flags) {
+ amount.parse(str, flags);
+}
+
+void py_read_1(amount_t& amount, object in) {
+ if (PyFile_Check(in.ptr())) {
+ pyifstream instr(reinterpret_cast<PyFileObject *>(in.ptr()));
+ amount.read(instr);
+ } else {
+ PyErr_SetString(PyExc_IOError,
+ "Argument to amount.parse(file) is not a file object");
+ }
+}
+void py_read_2(amount_t& amount, const std::string& str) {
+ const char * p = str.c_str();
+ amount.read(p);
+}
+
+#define EXC_TRANSLATOR(type) \
+ void exc_translate_ ## type(const type& err) { \
+ PyErr_SetString(PyExc_ArithmeticError, err.what()); \
+ }
+
+EXC_TRANSLATOR(amount_error)
+
+void export_amount()
+{
+ scope().attr("AMOUNT_PARSE_NO_MIGRATE") = AMOUNT_PARSE_NO_MIGRATE;
+ scope().attr("AMOUNT_PARSE_NO_REDUCE") = AMOUNT_PARSE_NO_REDUCE;
+
+ class_< amount_t > ("amount")
+#if 0
+ .def("initialize", &amount_t::initialize)
+ .staticmethod("initialize")
+ .def("shutdown", &amount_t::shutdown)
+ .staticmethod("shutdown")
+#endif
+
+ .add_static_property("current_pool",
+ make_getter(&amount_t::current_pool,
+ return_value_policy<reference_existing_object>()))
+
+ .add_static_property("keep_base", &amount_t::keep_base)
+
+ .add_static_property("keep_price", &amount_t::keep_price)
+ .add_static_property("keep_date", &amount_t::keep_date)
+ .add_static_property("keep_tag", &amount_t::keep_tag)
+
+ .add_static_property("stream_fullstrings", &amount_t::stream_fullstrings)
+
+ .def(init<double>())
+ .def(init<long>())
+ .def(init<std::string>())
+
+ .def("exact", &amount_t::exact, args("value"),
+ "Construct an amount object whose display precision is always equal to its\n\
+internal precision.")
+ .staticmethod("exact")
+
+ .def(init<amount_t>())
+
+ .def("compare", &amount_t::compare)
+
+ .def(self == self)
+ .def(self == long())
+ .def(long() == self)
+ .def(self == double())
+ .def(double() == self)
+
+ .def(self != self)
+ .def(self != long())
+ .def(long() != self)
+ .def(self != double())
+ .def(double() != self)
+
+ .def(! self)
+
+ .def(self < self)
+ .def(self < long())
+ .def(long() < self)
+ .def(self < double())
+ .def(double() < self)
+
+ .def(self <= self)
+ .def(self <= long())
+ .def(long() <= self)
+ .def(self <= double())
+ .def(double() <= self)
+
+ .def(self > self)
+ .def(self > long())
+ .def(long() > self)
+ .def(self > double())
+ .def(double() > self)
+
+ .def(self >= self)
+ .def(self >= long())
+ .def(long() >= self)
+ .def(self >= double())
+ .def(double() >= self)
+
+ .def(self += self)
+ .def(self += long())
+ .def(self += double())
+
+ .def(self + self)
+ .def(self + long())
+ .def(long() + self)
+ .def(self + double())
+ .def(double() + self)
+
+ .def(self -= self)
+ .def(self -= long())
+ .def(self -= double())
+
+ .def(self - self)
+ .def(self - long())
+ .def(long() - self)
+ .def(self - double())
+ .def(double() - self)
+
+ .def(self *= self)
+ .def(self *= long())
+ .def(self *= double())
+
+ .def(self * self)
+ .def(self * long())
+ .def(long() * self)
+ .def(self * double())
+ .def(double() * self)
+
+ .def(self /= self)
+ .def(self /= long())
+ .def(self /= double())
+
+ .def(self / self)
+ .def(self / long())
+ .def(long() / self)
+ .def(self / double())
+ .def(double() / self)
+
+ .add_property("precision", &amount_t::precision)
+
+ .def("negate", &amount_t::negate)
+ .def("in_place_negate", &amount_t::in_place_negate,
+ return_value_policy<reference_existing_object>())
+ .def(- self)
+
+ .def("abs", &amount_t::abs)
+ .def("__abs__", &amount_t::abs)
+
+ .def("round", py_round_0)
+ .def("round", py_round_1)
+ .def("unround", &amount_t::unround)
+
+ .def("reduce", &amount_t::reduce)
+ .def("in_place_reduce", &amount_t::in_place_reduce,
+ return_value_policy<reference_existing_object>())
+
+ .def("unreduce", &amount_t::unreduce)
+ .def("in_place_unreduce", &amount_t::in_place_unreduce,
+ return_value_policy<reference_existing_object>())
+
+ .def("value", py_value_0)
+ .def("value", py_value_1)
+
+ .def("sign", &amount_t::sign)
+ .def("__nonzero__", &amount_t::is_nonzero)
+ .def("is_nonzero", &amount_t::is_nonzero)
+ .def("is_zero", &amount_t::is_zero)
+ .def("is_realzero", &amount_t::is_realzero)
+ .def("is_null", &amount_t::is_null)
+
+ .def("to_double", py_to_double_0)
+ .def("to_double", py_to_double_1)
+ .def("__float__", py_to_double_0)
+ .def("to_long", py_to_long_0)
+ .def("to_long", py_to_long_1)
+ .def("__int__", py_to_long_0)
+ .def("to_string", &amount_t::to_string)
+ .def("__str__", &amount_t::to_string)
+ .def("to_fullstring", &amount_t::to_fullstring)
+ .def("__repr__", &amount_t::to_fullstring)
+
+ .def("fits_in_double", &amount_t::fits_in_double)
+ .def("fits_in_long", &amount_t::fits_in_long)
+
+ .add_property("quantity_string", &amount_t::quantity_string)
+
+ .add_property("commodity",
+ make_function(&amount_t::commodity,
+ return_value_policy<reference_existing_object>()),
+ make_function(&amount_t::set_commodity,
+ with_custodian_and_ward<1, 2>()))
+
+ .def("has_commodity", &amount_t::has_commodity)
+ .def("clear_commodity", &amount_t::clear_commodity)
+ .add_property("number", &amount_t::number)
+
+ .def("annotate_commodity", &amount_t::annotate_commodity)
+ .def("commodity_annotated", &amount_t::commodity_annotated)
+ .add_property("annotation_details", &amount_t::annotation_details)
+ .def("strip_annotations", &amount_t::strip_annotations)
+
+ .def("parse", py_parse_1)
+ .def("parse", py_parse_2)
+ .def("parse", py_parse_str_1)
+ .def("parse", py_parse_str_2)
+
+ .def("parse_conversion", &amount_t::parse_conversion)
+ .staticmethod("parse_conversion")
+
+ .def("read", py_read_1)
+ .def("read", py_read_2)
+ .def("write", &amount_t::write)
+
+ .def("valid", &amount_t::valid)
+ ;
+
+ register_optional_to_python<amount_t>();
+
+ implicitly_convertible<double, amount_t>();
+ implicitly_convertible<long, amount_t>();
+ implicitly_convertible<string, amount_t>();
+
+#define EXC_TRANSLATE(type) \
+ register_exception_translator<type>(&exc_translate_ ## type);
+
+ EXC_TRANSLATE(amount_error);
+}
+
+} // namespace ledger
diff --git a/py_commodity.cc b/py_commodity.cc
new file mode 100644
index 00000000..0dab3cd3
--- /dev/null
+++ b/py_commodity.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+#include "amount.h"
+
+#include <boost/python/exception_translator.hpp>
+#include <boost/python/implicit.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+void export_commodity()
+{
+ scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS;
+ scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
+ scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
+ scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN;
+ scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS;
+ scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
+ scope().attr("COMMODITY_STYLE_BUILTIN") = COMMODITY_STYLE_BUILTIN;
+
+ class_< commodity_t, bases<>,
+ commodity_t, boost::noncopyable > ("commodity", no_init)
+ .def(self == self)
+
+ .def("drop_flags", &commodity_t::drop_flags)
+
+ .add_property("precision", &commodity_t::precision)
+ ;
+}
+
+} // namespace ledger
diff --git a/py_times.cc b/py_times.cc
new file mode 100644
index 00000000..173f21fa
--- /dev/null
+++ b/py_times.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+
+#include <boost/cast.hpp>
+#include <boost/python/module.hpp>
+#include <boost/python/def.hpp>
+#include <boost/python/to_python_converter.hpp>
+
+#include <Python.h>
+#include <datetime.h>
+
+// jww (2007-05-04): Convert time duration objects to PyDelta
+
+namespace ledger {
+
+using namespace boost::python;
+
+typedef boost::gregorian::date date;
+
+struct date_to_python
+{
+ static PyObject* convert(const date& dte)
+ {
+ PyDateTime_IMPORT;
+ return PyDate_FromDate(dte.year(), dte.month(), dte.day());
+ }
+};
+
+struct date_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ PyDateTime_IMPORT;
+ if (PyDate_Check(obj_ptr)) return obj_ptr;
+ return 0;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ PyDateTime_IMPORT;
+ int y = PyDateTime_GET_YEAR(obj_ptr);
+ int m = PyDateTime_GET_MONTH(obj_ptr);
+ int d = PyDateTime_GET_DAY(obj_ptr);
+ date* dte = new date(y,m,d);
+ data->convertible = (void*)dte;
+ }
+};
+
+typedef register_python_conversion<date, date_to_python, date_from_python>
+ date_python_conversion;
+
+
+struct datetime_to_python
+{
+ static PyObject* convert(const moment_t& moment)
+ {
+ PyDateTime_IMPORT;
+ date dte = moment.date();
+ moment_t::time_duration_type tod = moment.time_of_day();
+ return PyDateTime_FromDateAndTime(dte.year(), dte.month(), dte.day(),
+ tod.hours(), tod.minutes(), tod.seconds(),
+ tod.total_microseconds() % 1000000);
+ }
+};
+
+struct datetime_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ PyDateTime_IMPORT;
+ if(PyDateTime_Check(obj_ptr)) return obj_ptr;
+ return 0;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ PyDateTime_IMPORT;
+ int y = PyDateTime_GET_YEAR(obj_ptr);
+ int m = PyDateTime_GET_MONTH(obj_ptr);
+ int d = PyDateTime_GET_DAY(obj_ptr);
+ int h = PyDateTime_DATE_GET_HOUR(obj_ptr);
+ int min = PyDateTime_DATE_GET_MINUTE(obj_ptr);
+ int s = PyDateTime_DATE_GET_SECOND(obj_ptr);
+ moment_t* moment = new moment_t(date(y,m,d),
+ moment_t::time_duration_type(h, min, s));
+ data->convertible = (void*)moment;
+ }
+};
+
+typedef register_python_conversion<moment_t, datetime_to_python, datetime_from_python>
+ datetime_python_conversion;
+
+void export_times()
+{
+ date_python_conversion();
+ datetime_python_conversion();
+
+ register_optional_to_python<moment_t>();
+}
+
+} // namespace ledger
diff --git a/py_utils.cc b/py_utils.cc
new file mode 100644
index 00000000..50ce9712
--- /dev/null
+++ b/py_utils.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+
+#include <boost/python/module.hpp>
+#include <boost/python/def.hpp>
+#include <boost/python/to_python_converter.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+struct bool_to_python
+{
+ static PyObject * convert(const bool truth)
+ {
+ if (truth)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+ }
+};
+
+struct bool_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyBool_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr,
+ converter::rvalue_from_python_stage1_data* data)
+ {
+ void* storage = ((converter::rvalue_from_python_storage<bool>*) data)->storage.bytes;
+ if (obj_ptr == Py_True)
+ new (storage) bool(true);
+ else
+ new (storage) bool(false);
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<bool, bool_to_python, bool_from_python>
+ bool_python_conversion;
+
+
+struct string_to_python
+{
+ static PyObject* convert(const string& str)
+ {
+ return incref(object(*boost::polymorphic_downcast<const std::string *>(&str)).ptr());
+ }
+};
+
+struct string_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyString_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ const char* value = PyString_AsString(obj_ptr);
+ if (value == 0) throw_error_already_set();
+ void* storage = ((converter::rvalue_from_python_storage<string>*) data)->storage.bytes;
+ new (storage) string(value);
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<string, string_to_python, string_from_python>
+ string_python_conversion;
+
+
+struct istream_to_python
+{
+ static PyObject* convert(const std::istream& str)
+ {
+ return incref(boost::python::detail::none());
+ }
+};
+
+struct istream_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyFile_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ void* storage = ((converter::rvalue_from_python_storage<pyifstream>*) data)->storage.bytes;
+ new (storage) pyifstream(reinterpret_cast<PyFileObject *>(obj_ptr));
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<std::istream, istream_to_python, istream_from_python>
+ istream_python_conversion;
+
+
+struct ostream_to_python
+{
+ static PyObject* convert(const std::ostream& str)
+ {
+ return incref(boost::python::detail::none());
+ }
+};
+
+struct ostream_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyFile_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ void* storage = ((converter::rvalue_from_python_storage<pyofstream>*) data)->storage.bytes;
+ new (storage) pyofstream(reinterpret_cast<PyFileObject *>(obj_ptr));
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<std::ostream, ostream_to_python, ostream_from_python>
+ ostream_python_conversion;
+
+
+void export_utils()
+{
+ bool_python_conversion();
+ string_python_conversion();
+ istream_python_conversion();
+ ostream_python_conversion();
+}
+
+} // namespace ledger
diff --git a/pyfstream.h b/pyfstream.h
new file mode 100644
index 00000000..f4650e0a
--- /dev/null
+++ b/pyfstream.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PYFSTREAM_H
+#define _PYFSTREAM_H
+
+// pyofstream
+// - a stream that writes on a Python file object
+
+class pyoutbuf : public std::streambuf {
+ protected:
+ PyFileObject * fo; // Python file object
+ public:
+ // constructor
+ pyoutbuf (PyFileObject * _fo) : fo(_fo) {}
+
+ protected:
+ // write one character
+ virtual int_type overflow (int_type c) {
+ if (c != EOF) {
+ char z[2];
+ z[0] = c;
+ z[1] = '\0';
+ if (PyFile_WriteString(z, (PyObject *)fo) < 0) {
+ return EOF;
+ }
+ }
+ return c;
+ }
+
+ // write multiple characters
+ virtual std::streamsize xsputn (const char* s, std::streamsize num) {
+ char * buf = new char[num + 1];
+ std::strncpy(buf, s, num);
+ buf[num] = '\0';
+ if (PyFile_WriteString(buf, (PyObject *)fo) < 0)
+ num = 0;
+ delete[] buf;
+ return num;
+ }
+};
+
+class pyofstream : public std::ostream {
+ protected:
+ pyoutbuf buf;
+ public:
+ pyofstream (PyFileObject * fo) : std::ostream(0), buf(fo) {
+ rdbuf(&buf);
+ }
+};
+
+// pyifstream
+// - a stream that reads on a file descriptor
+
+class pyinbuf : public std::streambuf {
+ protected:
+ PyFileObject * fo; // Python file object
+ protected:
+ /* data buffer:
+ * - at most, pbSize characters in putback area plus
+ * - at most, bufSize characters in ordinary read buffer
+ */
+ static const int pbSize = 4; // size of putback area
+ static const int bufSize = 1024; // size of the data buffer
+ char buffer[bufSize + pbSize]; // data buffer
+
+ public:
+ /* constructor
+ * - initialize file descriptor
+ * - initialize empty data buffer
+ * - no putback area
+ * => force underflow()
+ */
+ pyinbuf (PyFileObject * _fo) : fo(_fo) {
+ setg (buffer+pbSize, // beginning of putback area
+ buffer+pbSize, // read position
+ buffer+pbSize); // end position
+ }
+
+ protected:
+ // insert new characters into the buffer
+ virtual int_type underflow () {
+#ifndef _MSC_VER
+ using std::memmove;
+#endif
+
+ // is read position before end of buffer?
+ if (gptr() < egptr()) {
+ return traits_type::to_int_type(*gptr());
+ }
+
+ /* process size of putback area
+ * - use number of characters read
+ * - but at most size of putback area
+ */
+ int numPutback;
+ numPutback = gptr() - eback();
+ if (numPutback > pbSize) {
+ numPutback = pbSize;
+ }
+
+ /* copy up to pbSize characters previously read into
+ * the putback area
+ */
+ memmove (buffer+(pbSize-numPutback), gptr()-numPutback,
+ numPutback);
+
+ // read at most bufSize new characters
+ int num;
+ PyObject *line = PyFile_GetLine((PyObject *)fo, bufSize);
+ if (! line || ! PyString_Check(line)) {
+ // ERROR or EOF
+ return EOF;
+ }
+
+ num = PyString_Size(line);
+ if (num == 0)
+ return EOF;
+
+ memmove (buffer+pbSize, PyString_AsString(line), num);
+
+ // reset buffer pointers
+ setg (buffer+(pbSize-numPutback), // beginning of putback area
+ buffer+pbSize, // read position
+ buffer+pbSize+num); // end of buffer
+
+ // return next character
+ return traits_type::to_int_type(*gptr());
+ }
+};
+
+class pyifstream : public std::istream {
+ protected:
+ pyinbuf buf;
+ public:
+ pyifstream (PyFileObject * fo) : std::istream(0), buf(fo) {
+ rdbuf(&buf);
+ }
+};
+
+#endif // _PYFSTREAM_H
diff --git a/pyinterp.cc b/pyinterp.cc
new file mode 100644
index 00000000..d521a0ee
--- /dev/null
+++ b/pyinterp.cc
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+
+#include <boost/python/module_init.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+void export_utils();
+void export_times();
+void export_amount();
+void export_commodity();
+#if 0
+void export_balance();
+void export_value();
+void export_journal();
+void export_parser();
+void export_option();
+void export_walk();
+void export_report();
+void export_format();
+void export_valexpr();
+#endif
+
+void initialize_for_python()
+{
+ export_utils();
+ export_times();
+ export_amount();
+ export_commodity();
+#if 0
+ export_balance();
+ export_value();
+ export_journal();
+ export_parser();
+ export_option();
+ export_walk();
+ export_format();
+ export_report();
+ export_valexpr();
+#endif
+}
+
+struct python_run
+{
+ object result;
+
+ python_run(python_interpreter_t * intepreter,
+ const string& str, int input_mode)
+ : result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode,
+ intepreter->nspace.ptr(),
+ intepreter->nspace.ptr())))) {}
+ operator object() {
+ return result;
+ }
+};
+
+python_interpreter_t::python_interpreter_t(xml::xpath_t::scope_t& parent)
+ : xml::xpath_t::symbol_scope_t(parent),
+ mmodule(borrowed(PyImport_AddModule("__main__"))),
+ nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get()))))
+{
+ Py_Initialize();
+ boost::python::detail::init_module("ledger", &initialize_for_python);
+}
+
+object python_interpreter_t::import(const string& str)
+{
+ assert(Py_IsInitialized());
+
+ try {
+ PyObject * mod = PyImport_Import(PyString_FromString(str.c_str()));
+ if (! mod)
+ throw_(std::logic_error, "Failed to import Python module " << str);
+
+ object newmod(handle<>(borrowed(mod)));
+
+#if 1
+ // Import all top-level entries directly into the main namespace
+ dict m_nspace(handle<>(borrowed(PyModule_GetDict(mod))));
+ nspace.update(m_nspace);
+#else
+ nspace[string(PyModule_GetName(mod))] = newmod;
+#endif
+ return newmod;
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::logic_error, "Importing Python module " << str);
+ }
+ return object();
+}
+
+object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
+{
+ bool first = true;
+ string buffer;
+ buffer.reserve(4096);
+
+ while (! in.eof()) {
+ char buf[256];
+ in.getline(buf, 255);
+ if (buf[0] == '!')
+ break;
+ if (first)
+ first = false;
+ else
+ buffer += "\n";
+ buffer += buf;
+ }
+
+ try {
+ int input_mode;
+ switch (mode) {
+ case PY_EVAL_EXPR: input_mode = Py_eval_input; break;
+ case PY_EVAL_STMT: input_mode = Py_single_input; break;
+ case PY_EVAL_MULTI: input_mode = Py_file_input; break;
+ }
+ assert(Py_IsInitialized());
+ return python_run(this, buffer, input_mode);
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::logic_error, "Evaluating Python code");
+ }
+ return object();
+}
+
+object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
+{
+ try {
+ int input_mode;
+ switch (mode) {
+ case PY_EVAL_EXPR: input_mode = Py_eval_input; break;
+ case PY_EVAL_STMT: input_mode = Py_single_input; break;
+ case PY_EVAL_MULTI: input_mode = Py_file_input; break;
+ }
+ assert(Py_IsInitialized());
+ return python_run(this, str, input_mode);
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::logic_error, "Evaluating Python code");
+ }
+ return object();
+}
+
+value_t python_interpreter_t::functor_t::operator()
+ (xml::xpath_t::call_scope_t& args)
+{
+ try {
+ if (! PyCallable_Check(func.ptr())) {
+ return extract<value_t>(func.ptr());
+ } else {
+ if (args.size() > 0) {
+ list arglist;
+ if (args.value().is_sequence())
+ foreach (const value_t& value, args.value().as_sequence())
+ arglist.append(value);
+ else
+ arglist.append(args.value());
+
+ if (PyObject * val =
+ PyObject_CallObject(func.ptr(),
+ boost::python::tuple(arglist).ptr())) {
+ value_t result = extract<value_t>(val)();
+ Py_DECREF(val);
+ return result;
+ }
+ else if (PyObject * err = PyErr_Occurred()) {
+ PyErr_Print();
+ throw_(xml::xpath_t::calc_error,
+ "While calling Python function '" /*<< name() <<*/ "': " << err);
+ } else {
+ assert(false);
+ }
+ } else {
+ return call<value_t>(func.ptr());
+ }
+ }
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(xml::xpath_t::calc_error,
+ "While calling Python function '" /*<< name() <<*/ "'");
+ }
+ return NULL_VALUE;
+}
+
+value_t python_interpreter_t::lambda_t::operator()
+ (xml::xpath_t::call_scope_t& args)
+{
+ try {
+ assert(args.size() == 1);
+ value_t item = args[0];
+ assert(item.is_xml_node());
+ return call<value_t>(func.ptr(), item.as_xml_node());
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(xml::xpath_t::calc_error,
+ "While evaluating Python lambda expression");
+ }
+ return NULL_VALUE;
+}
+
+} // namespace ledger
diff --git a/pyinterp.h b/pyinterp.h
new file mode 100644
index 00000000..3d69d972
--- /dev/null
+++ b/pyinterp.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PYINTERP_H
+#define _PYINTERP_H
+
+#include "xpath.h"
+
+#include <boost/python.hpp>
+#include <Python.h>
+
+namespace ledger {
+
+class python_interpreter_t : public xml::xpath_t::symbol_scope_t
+{
+ boost::python::handle<> mmodule;
+
+ public:
+ boost::python::dict nspace;
+
+ python_interpreter_t(xml::xpath_t::scope_t& parent);
+
+ virtual ~python_interpreter_t() {
+ Py_Finalize();
+ }
+
+ boost::python::object import(const string& name);
+
+ enum py_eval_mode_t {
+ PY_EVAL_EXPR,
+ PY_EVAL_STMT,
+ PY_EVAL_MULTI
+ };
+
+ boost::python::object eval(std::istream& in,
+ py_eval_mode_t mode = PY_EVAL_EXPR);
+ boost::python::object eval(const string& str,
+ py_eval_mode_t mode = PY_EVAL_EXPR);
+ boost::python::object eval(const char * c_str,
+ py_eval_mode_t mode = PY_EVAL_EXPR) {
+ string str(c_str);
+ return eval(str, mode);
+ }
+
+ class functor_t {
+ protected:
+ boost::python::object func;
+ public:
+ functor_t(const string& name, boost::python::object _func) : func(_func) {}
+ virtual ~functor_t() {}
+ virtual value_t operator()(xml::xpath_t::call_scope_t& args);
+ };
+
+ virtual xml::xpath_t::ptr_op_t lookup(const string& name) {
+ if (boost::python::object func = eval(name))
+ return WRAP_FUNCTOR(functor_t(name, func));
+ else
+ return xml::xpath_t::symbol_scope_t::lookup(name);
+ }
+
+ class lambda_t : public functor_t {
+ public:
+ lambda_t(boost::python::object code) : functor_t("<lambda>", code) {}
+ virtual value_t operator()(xml::xpath_t::call_scope_t& args);
+ };
+};
+
+} // namespace ledger
+
+#endif // _PYINTERP_H
diff --git a/pyledger.cc b/pyledger.cc
new file mode 100644
index 00000000..ebbdc82e
--- /dev/null
+++ b/pyledger.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pyledger.h>
+
+using namespace boost::python;
+
+ledger::session_t python_session;
+
+namespace ledger {
+ extern void initialize_for_python();
+}
+
+BOOST_PYTHON_MODULE(ledger)
+{
+ ledger::set_session_context(&python_session);
+ ledger::initialize_for_python();
+}
diff --git a/pyledger.h b/pyledger.h
new file mode 100644
index 00000000..3ab82558
--- /dev/null
+++ b/pyledger.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PYLEDGER_H
+#define _PYLEDGER_H
+
+//////////////////////////////////////////////////////////////////////
+//
+// Ledger Accounting Tool (with Python support via Boost.Python)
+//
+// A command-line tool for general double-entry accounting.
+//
+// Copyright (c) 2003,2004 John Wiegley <johnw@newartisans.com>
+//
+
+#include <ledger.h>
+#include <pyinterp.h>
+
+#endif // _PYLEDGER_H
diff --git a/pyutils.h b/pyutils.h
new file mode 100644
index 00000000..41bbbfde
--- /dev/null
+++ b/pyutils.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PY_UTILS_H
+#define _PY_UTILS_H
+
+#include "pyfstream.h"
+
+template <typename T, typename TfromPy>
+struct object_from_python
+{
+ object_from_python() {
+ boost::python::converter::registry::push_back
+ (&TfromPy::convertible, &TfromPy::construct,
+ boost::python::type_id<T>());
+ }
+};
+
+template <typename T, typename TtoPy, typename TfromPy>
+struct register_python_conversion
+{
+ register_python_conversion() {
+ boost::python::to_python_converter<T, TtoPy>();
+ object_from_python<T, TfromPy>();
+ }
+};
+
+template <typename T>
+struct register_optional_to_python : public boost::noncopyable
+{
+ struct optional_to_python
+ {
+ static PyObject * convert(const boost::optional<T>& value)
+ {
+ return boost::python::incref
+ (value ? boost::python::to_python_value<T>()(*value) :
+ boost::python::detail::none());
+ }
+ };
+
+ struct optional_from_python
+ {
+ static void * convertible(PyObject * source)
+ {
+ using namespace boost::python::converter;
+
+ if (source == Py_None)
+ return source;
+
+ const registration& converters(registered<T>::converters);
+
+ if (implicit_rvalue_convertible_from_python(source, converters)) {
+ rvalue_from_python_stage1_data data =
+ rvalue_from_python_stage1(source, converters);
+ return rvalue_from_python_stage2(source, data, converters);
+ }
+ return NULL;
+ }
+
+ static void construct(PyObject * source,
+ boost::python::converter::rvalue_from_python_stage1_data * data)
+ {
+ using namespace boost::python::converter;
+
+ void * const storage = ((rvalue_from_python_storage<T> *) data)->storage.bytes;
+
+ if (data->convertible == source) // == None
+ new (storage) boost::optional<T>(); // A Boost uninitialized value
+ else
+ new (storage) boost::optional<T>(*static_cast<T *>(data->convertible));
+
+ data->convertible = storage;
+ }
+ };
+
+ explicit register_optional_to_python() {
+ register_python_conversion<boost::optional<T>,
+ optional_to_python, optional_from_python>();
+ }
+};
+
+//boost::python::register_ptr_to_python< boost::shared_ptr<Base> >();
+
+#endif // _PY_UTILS_H
diff --git a/setup.py b/setup.py
index 9e2cbbd5..b8137e4e 100755
--- a/setup.py
+++ b/setup.py
@@ -4,15 +4,17 @@ from distutils.core import setup, Extension
import os
-libs = ["amounts", "boost_python", "gmp"]
+defines = [('PYTHON_MODULE', 1)]
+libs = os.environ["PYLIBS"].split()
-setup(name = "Amounts",
- version = "2.6.0.90",
- description = "Amounts and Commodities Library",
+setup(name = "Ledger",
+ version = "2.7",
+ description = "Ledger Accounting Library",
author = "John Wiegley",
author_email = "johnw@newartisans.com",
- url = "http://www.newartisans.com/johnw/",
+ url = "http://www.newartisans.com/software/ledger.html",
ext_modules = [
- Extension("amounts", ["amounts.cc"],
- define_macros = [('PYTHON_MODULE', 1)],
- libraries = libs)])
+ Extension("ledger",
+ [os.path.join(os.environ['SRCDIR'],
+ "src", "python", "pyledger.cc")],
+ define_macros = defines, libraries = libs)])
diff --git a/system.hh b/system.hh
new file mode 100644
index 00000000..1621ac85
--- /dev/null
+++ b/system.hh
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYSTEM_HH
+#define _SYSTEM_HH
+
+/**
+ * @file system.hh
+ * @author John Wiegley
+ * @date Mon Apr 23 03:43:05 2007
+ *
+ * @brief All system headers needed by Ledger.
+ *
+ * These are collected here so that a pre-compiled header can be made.
+ * None of these header files (with the exception of acconf.h, when
+ * configure is re-run) are expected to change.
+ */
+
+#include "acconf.h"
+
+#if defined(__GNUG__) && __GNUG__ < 3
+#define _XOPEN_SOURCE
+#endif
+
+#include <algorithm>
+#include <exception>
+#include <stdexcept>
+#include <iostream>
+#include <streambuf>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <iterator>
+#include <list>
+#include <map>
+#include <memory>
+#include <new>
+#include <stack>
+#include <string>
+#include <vector>
+
+#if defined(__GNUG__) && __GNUG__ < 3
+namespace std {
+ inline ostream & right (ostream & i) {
+ i.setf(i.right, i.adjustfield);
+ return i;
+ }
+ inline ostream & left (ostream & i) {
+ i.setf(i.left, i.adjustfield);
+ return i;
+ }
+}
+#endif
+
+#include <cassert>
+#include <cctype>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+
+#if defined __FreeBSD__ && __FreeBSD__ <= 4
+// FreeBSD has a broken isspace macro, so don't use it
+#undef isspace(c)
+#endif
+
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#if defined(HAVE_GETPWUID) || defined(HAVE_GETPWNAM)
+#include <pwd.h>
+#endif
+
+#if defined(HAVE_NL_LANGINFO)
+#include <langinfo.h>
+#endif
+
+#include <gmp.h>
+
+extern "C" {
+#if defined(HAVE_EXPAT)
+#include <expat.h> // expat XML parser
+#elif defined(HAVE_XMLPARSE)
+#include <xmlparse.h> // expat XML parser
+#endif
+}
+
+#if defined(HAVE_LIBOFX)
+#include <libofx.h>
+#endif
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/any.hpp>
+#include <boost/cast.hpp>
+#include <boost/current_function.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/filesystem/convenience.hpp>
+#include <boost/filesystem/exception.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/foreach.hpp>
+#include <boost/function.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/key_extractors.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/random_access_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/operators.hpp>
+#include <boost/optional.hpp>
+#include <boost/ptr_container/ptr_list.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/regex.hpp>
+#include <boost/variant.hpp>
+
+#endif // _SYSTEM_HH
diff --git a/times.cc b/times.cc
new file mode 100644
index 00000000..fc6f2f1b
--- /dev/null
+++ b/times.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "utils.h"
+
+namespace ledger {
+
+#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK
+const ptime time_now = boost::posix_time::microsec_clock::universal_time();
+#else
+const ptime time_now = boost::posix_time::second_clock::universal_time();
+#endif
+const date date_now = boost::gregorian::day_clock::universal_day();
+
+#ifdef SUPPORT_DATE_AND_TIME
+const moment_t& now(time_now);
+#else
+const moment_t& now(date_now);
+#endif
+
+bool day_before_month = false;
+static bool day_before_month_initialized = false;
+
+moment_t parse_datetime(const char * str)
+{
+ if (! day_before_month_initialized) {
+#ifdef HAVE_NL_LANGINFO
+ const char * d_fmt = nl_langinfo(D_FMT);
+ if (d_fmt && std::strlen(d_fmt) > 1 && d_fmt[1] == 'd')
+ day_before_month = true;
+ day_before_month_initialized = true;
+#endif
+ }
+#if 0
+ return parse_abs_datetime(in);
+#else
+ int year = ((str[0] - '0') * 1000 +
+ (str[1] - '0') * 100 +
+ (str[2] - '0') * 10 +
+ (str[3] - '0'));
+
+ int mon = ((str[5] - '0') * 10 +
+ (str[6] - '0'));
+
+ int day = ((str[8] - '0') * 10 +
+ (str[9] - '0'));
+
+ return moment_t(boost::gregorian::date(year, mon, day));
+#endif
+}
+
+} // namespace ledger
diff --git a/times.h b/times.h
new file mode 100644
index 00000000..de1dd26a
--- /dev/null
+++ b/times.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TIMES_H
+#define _TIMES_H
+
+namespace ledger {
+
+#define SUPPORT_DATE_AND_TIME 1
+#ifdef SUPPORT_DATE_AND_TIME
+
+typedef boost::posix_time::ptime moment_t;
+typedef moment_t::time_duration_type duration_t;
+
+inline bool is_valid_moment(const moment_t& moment) {
+ return ! moment.is_not_a_date_time();
+}
+
+#else // SUPPORT_DATE_AND_TIME
+
+typedef boost::gregorian::date moment_t;
+typedef boost::gregorian::date_duration duration_t;
+
+inline bool is_valid_moment(const moment_t& moment) {
+ return ! moment.is_not_a_date();
+}
+
+#endif // SUPPORT_DATE_AND_TIME
+
+extern const moment_t& now;
+
+DECLARE_EXCEPTION(error, datetime_error);
+
+class interval_t
+{
+public:
+ interval_t() {}
+ interval_t(const string&) {}
+
+ operator bool() const {
+ return false;
+ }
+
+ void start(const moment_t&) {}
+ moment_t next() const { return moment_t(); }
+
+ void parse(std::istream&) {}
+};
+
+#if 0
+inline moment_t ptime_local_to_utc(const moment_t& when) {
+ struct std::tm tm_gmt = to_tm(when);
+ return boost::posix_time::from_time_t(std::mktime(&tm_gmt));
+}
+
+// jww (2007-04-18): I need to make a general parsing function
+// instead, and then make these into private methods.
+inline moment_t ptime_from_local_date_string(const string& date_string) {
+ return ptime_local_to_utc(moment_t(boost::gregorian::from_string(date_string),
+ time_duration()));
+}
+
+inline moment_t ptime_from_local_time_string(const string& time_string) {
+ return ptime_local_to_utc(boost::posix_time::time_from_string(time_string));
+}
+#endif
+
+moment_t parse_datetime(const char * str);
+
+inline moment_t parse_datetime(const string& str) {
+ return parse_datetime(str.c_str());
+}
+
+extern const ptime time_now;
+extern const date date_now;
+extern bool day_before_month;
+
+#if 0
+struct intorchar
+{
+ int ival;
+ string sval;
+
+ intorchar() : ival(-1) {}
+ intorchar(int val) : ival(val) {}
+ intorchar(const string& val) : ival(-1), sval(val) {}
+ intorchar(const intorchar& o) : ival(o.ival), sval(o.sval) {}
+};
+
+ledger::moment_t parse_abs_datetime(std::istream& input);
+#endif
+
+} // namespace ledger
+
+#endif // _TIMES_H
diff --git a/timing.h b/timing.h
deleted file mode 100644
index 7e1029ea..00000000
--- a/timing.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef _TIMING_H
-#define _TIMING_H
-
-#include "debug.h"
-
-#include <ctime>
-
-namespace ledger {
-
-class timing_t
-{
- public:
- std::clock_t begin;
- std::clock_t cumulative;
- std::string file;
- unsigned long line;
- std::string symbol;
- std::string category;
-
- timing_t(const std::string& _symbol, const std::string& _category)
- : begin(0), cumulative(0), symbol(_symbol), category(_category) {}
-
- timing_t(const std::string& _symbol)
- : begin(0), cumulative(0), symbol(_symbol) {}
-
- ~timing_t() {
- std::string cls = "timing.results.";
- cls += symbol;
- DEBUG_PRINT(cls.c_str(), file << ":" << line << ": "
- << category << " = "
- << (double(cumulative) / double(CLOCKS_PER_SEC)) << "s");
- }
-
- void start(const std::string& _file, unsigned long _line) {
- file = _file;
- line = _line;
- begin = std::clock();
- }
- void start() {
- begin = std::clock();
- }
-
- void stop() {
- cumulative += std::clock() - begin;
- }
-};
-
-#ifdef DEBUG_ENABLED
-#define TIMER_DEF(sym, cat) static timing_t sym(#sym, cat)
-#define TIMER_DEF_(sym) static timing_t sym(#sym, #sym)
-#define TIMER_START(sym) sym.start(__FILE__, __LINE__)
-#define TIMER_STOP(sym) sym.stop()
-#else
-#define TIMER_DEF(sym, cat)
-#define TIMER_DEF_(sym)
-#define TIMER_START(sym)
-#define TIMER_STOP(sym)
-#endif
-
-} // namespace ledger
-
-#endif // _TIMING_H
diff --git a/tuples.hpp b/tuples.hpp
new file mode 100644
index 00000000..523846a7
--- /dev/null
+++ b/tuples.hpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Copyright 2004-2007 Roman Yakovenko.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TUPLES_HPP_16_JAN_2007
+#define TUPLES_HPP_16_JAN_2007
+
+#include "boost/python.hpp"
+#include "boost/tuple/tuple.hpp"
+#include "boost/python/object.hpp" //len function
+#include <boost/mpl/int.hpp>
+#include <boost/mpl/next.hpp>
+
+/**
+ * Converts boost::tuples::tuple<...> to\from Python tuple
+ *
+ * The conversion is done "on-the-fly", you should only register the conversion
+ * with your tuple classes.
+ * For example:
+ *
+ * typedef boost::tuples::tuple< int, double, std::string > triplet;
+ * boost::python::register_tuple< triplet >();
+ *
+ * That's all. After this point conversion to\from next types will be handled
+ * by Boost.Python library:
+ *
+ * triplet
+ * triplet& ( return type only )
+ * const triplet
+ * const triplet&
+ *
+ * Implementation description.
+ * The conversion uses Boost.Python custom r-value converters. r-value converters
+ * is very powerful and undocumented feature of the library. The only documentation
+ * we have is http://boost.org/libs/python/doc/v2/faq.html#custom_string .
+ *
+ * The conversion consists from two parts: "to" and "from".
+ *
+ * "To" conversion
+ * The "to" part is pretty easy and well documented ( http://docs.python.org/api/api.html ).
+ * You should use Python C API to create an instance of a class and than you
+ * initialize the relevant members of the instance.
+ *
+ * "From" conversion
+ * Lets start from analyzing one of the use case Boost.Python library have to
+ * deal with:
+ *
+ * void do_smth( const triplet& arg ){...}
+ *
+ * In order to allow calling this function from Python, the library should keep
+ * parameter "arg" alive until the function returns. In other words, the library
+ * should provide instances life-time management. The provided interface is not
+ * ideal and could be improved. You have to implement two functions:
+ *
+ * void* convertible( PyObject* obj )
+ * Checks whether the "obj" could be converted to an instance of the desired
+ * class. If true, the function should return "obj", otherwise NULL
+ *
+ * void construct( PyObject* obj, converter::rvalue_from_python_stage1_data* data)
+ * Constructs the instance of the desired class. This function will be called
+ * if and only if "convertible" function returned true. The first argument
+ * is Python object, which was passed as parameter to "convertible" function.
+ * The second object is some kind of memory allocator for one object. Basically
+ * it keeps a memory chunk. You will use the memory for object allocation.
+ *
+ * For some unclear for me reason, the library implements "C style Inheritance"
+ * ( http://www.embedded.com/97/fe29712.htm ). So, in order to create new
+ * object in the storage you have to cast to the "right" class:
+ *
+ * typedef converter::rvalue_from_python_storage<your_type_t> storage_t;
+ * storage_t* the_storage = reinterpret_cast<storage_t*>( data );
+ * void* memory_chunk = the_storage->storage.bytes;
+ *
+ * "memory_chunk" points to the memory, where the instance will be allocated.
+ *
+ * In order to create object at specific location, you should use placement new
+ * operator:
+ *
+ * your_type_t* instance = new (memory_chunk) your_type_t();
+ *
+ * Now, you can continue to initialize the instance.
+ *
+ * instance->set_xyz = read xyz from obj
+ *
+ * If "your_type_t" constructor requires some arguments, "read" the Python
+ * object before you call the constructor:
+ *
+ * xyz_type xyz = read xyz from obj
+ * your_type_t* instance = new (memory_chunk) your_type_t(xyz);
+ *
+ * Hint:
+ * In most case you don't really need\have to work with C Python API. Let
+ * Boost.Python library to do some work for you!
+ *
+ **/
+
+namespace boost{ namespace python{
+
+namespace details{
+
+//Small helper function, introduced to allow short syntax for index incrementing
+template< int index>
+typename mpl::next< mpl::int_< index > >::type increment_index(){
+ typedef typename mpl::next< mpl::int_< index > >::type next_index_type;
+ return next_index_type();
+}
+
+}
+
+template< class TTuple >
+struct to_py_tuple{
+
+ typedef mpl::int_< tuples::length< TTuple >::value > length_type;
+
+ static PyObject* convert(const TTuple& c_tuple){
+ list values;
+ //add all c_tuple items to "values" list
+ convert_impl( c_tuple, values, mpl::int_< 0 >(), length_type() );
+ //create Python tuple from the list
+ return incref( python::tuple( values ).ptr() );
+ }
+
+private:
+
+ template< int index, int length >
+ static void
+ convert_impl( const TTuple &c_tuple, list& values, mpl::int_< index >, mpl::int_< length > ) {
+ values.append( c_tuple.template get< index >() );
+ convert_impl( c_tuple, values, details::increment_index<index>(), length_type() );
+ }
+
+ template< int length >
+ static void
+ convert_impl( const TTuple&, list& values, mpl::int_< length >, mpl::int_< length >)
+ {}
+
+};
+
+
+template< class TTuple>
+struct from_py_sequence{
+
+ typedef TTuple tuple_type;
+
+ typedef mpl::int_< tuples::length< TTuple >::value > length_type;
+
+ static void*
+ convertible(PyObject* py_obj){
+
+ if( !PySequence_Check( py_obj ) ){
+ return 0;
+ }
+
+ if( !PyObject_HasAttrString( py_obj, "__len__" ) ){
+ return 0;
+ }
+
+ python::object py_sequence( handle<>( borrowed( py_obj ) ) );
+
+ if( tuples::length< TTuple >::value != len( py_sequence ) ){
+ return 0;
+ }
+
+ if( convertible_impl( py_sequence, mpl::int_< 0 >(), length_type() ) ){
+ return py_obj;
+ }
+ else{
+ return 0;
+ }
+ }
+
+ static void
+ construct( PyObject* py_obj, converter::rvalue_from_python_stage1_data* data){
+ typedef converter::rvalue_from_python_storage<TTuple> storage_t;
+ storage_t* the_storage = reinterpret_cast<storage_t*>( data );
+ void* memory_chunk = the_storage->storage.bytes;
+ TTuple* c_tuple = new (memory_chunk) TTuple();
+ data->convertible = memory_chunk;
+
+ python::object py_sequence( handle<>( borrowed( py_obj ) ) );
+ construct_impl( py_sequence, *c_tuple, mpl::int_< 0 >(), length_type() );
+ }
+
+ static TTuple to_c_tuple( PyObject* py_obj ){
+ if( !convertible( py_obj ) ){
+ throw std::runtime_error( "Unable to construct boost::tuples::tuple from Python object!" );
+ }
+ TTuple c_tuple;
+ python::object py_sequence( handle<>( borrowed( py_obj ) ) );
+ construct_impl( py_sequence, c_tuple, mpl::int_< 0 >(), length_type() );
+ return c_tuple;
+ }
+
+private:
+
+ template< int index, int length >
+ static bool
+ convertible_impl( const python::object& py_sequence, mpl::int_< index >, mpl::int_< length > ){
+
+ typedef typename tuples::element< index, TTuple>::type element_type;
+
+ object element = py_sequence[index];
+ extract<element_type> type_checker( element );
+ if( !type_checker.check() ){
+ return false;
+ }
+ else{
+ return convertible_impl( py_sequence, details::increment_index<index>(), length_type() );
+ }
+ }
+
+ template< int length >
+ static bool
+ convertible_impl( const python::object& py_sequence, mpl::int_< length >, mpl::int_< length > ){
+ return true;
+ }
+
+ template< int index, int length >
+ static void
+ construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< index >, mpl::int_< length > ){
+
+ typedef typename tuples::element< index, TTuple>::type element_type;
+
+ object element = py_sequence[index];
+ c_tuple.template get< index >() = extract<element_type>( element );
+
+ construct_impl( py_sequence, c_tuple, details::increment_index<index>(), length_type() );
+ }
+
+ template< int length >
+ static void
+ construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< length >, mpl::int_< length > )
+ {}
+
+};
+
+template< class TTuple>
+void register_tuple(){
+
+ to_python_converter< TTuple, to_py_tuple<TTuple> >();
+
+ converter::registry::push_back( &from_py_sequence<TTuple>::convertible
+ , &from_py_sequence<TTuple>::construct
+ , type_id<TTuple>() );
+};
+
+} } //boost::python
+
+#endif//TUPLES_HPP_16_JAN_2007
diff --git a/util.h b/util.h
deleted file mode 100644
index 21008a22..00000000
--- a/util.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef _UTIL_H
-#define _UTIL_H
-
-#include <iostream>
-
-#if defined __FreeBSD__ && __FreeBSD__ <=4
-// FreeBSD has a broken isspace macro, so dont use it
-#undef isspace(c)
-#endif
-
-#if defined(__GNUG__) && __GNUG__ < 3
-namespace std {
- inline ostream & right (ostream & i) {
- i.setf(i.right, i.adjustfield);
- return i;
- }
- inline ostream & left (ostream & i) {
- i.setf(i.left, i.adjustfield);
- return i;
- }
-}
-typedef unsigned long istream_pos_type;
-typedef unsigned long ostream_pos_type;
-#else
-typedef std::istream::pos_type istream_pos_type;
-typedef std::ostream::pos_type ostream_pos_type;
-#endif
-
-inline char * skip_ws(char * ptr) {
- while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
- ptr++;
- return ptr;
-}
-
-inline char peek_next_nonws(std::istream& in) {
- char c = in.peek();
- while (! in.eof() && std::isspace(c)) {
- in.get(c);
- c = in.peek();
- }
- return c;
-}
-
-#define READ_INTO(str, targ, size, var, cond) { \
- char * _p = targ; \
- var = str.peek(); \
- while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
- str.get(var); \
- if (str.eof()) \
- break; \
- if (var == '\\') { \
- str.get(var); \
- if (in.eof()) \
- break; \
- } \
- *_p++ = var; \
- var = str.peek(); \
- } \
- *_p = '\0'; \
-}
-
-#endif // _UTIL_H
diff --git a/utils.cc b/utils.cc
new file mode 100644
index 00000000..ad6f8a96
--- /dev/null
+++ b/utils.cc
@@ -0,0 +1,720 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "utils.h"
+
+/**********************************************************************
+ *
+ * Assertions
+ */
+
+#if defined(ASSERTS_ON)
+
+namespace ledger {
+
+DECLARE_EXCEPTION(fatal, assertion_failed);
+
+void debug_assert(const string& reason,
+ const string& func,
+ const string& file,
+ unsigned long line)
+{
+ std::ostringstream buf;
+ buf << "Assertion failed in \"" << file << "\", line " << line
+ << ": " << func << ": " << reason;
+ throw assertion_failed(buf.str());
+}
+
+} // namespace ledger
+
+#endif
+
+/**********************************************************************
+ *
+ * Verification (basically, very slow asserts)
+ */
+
+#if defined(VERIFY_ON)
+
+namespace ledger {
+
+bool verify_enabled = false;
+
+typedef std::pair<std::string, std::size_t> allocation_pair;
+typedef std::map<void *, allocation_pair> live_memory_map;
+typedef std::multimap<void *, allocation_pair> live_objects_map;
+
+typedef std::pair<unsigned int, std::size_t> count_size_pair;
+typedef std::map<std::string, count_size_pair> object_count_map;
+
+static live_memory_map * live_memory = NULL;
+static object_count_map * live_memory_count = NULL;
+static object_count_map * total_memory_count = NULL;
+
+static bool memory_tracing_active = false;
+
+static live_objects_map * live_objects = NULL;
+static object_count_map * live_object_count = NULL;
+static object_count_map * total_object_count = NULL;
+static object_count_map * total_ctor_count = NULL;
+
+void initialize_memory_tracing()
+{
+ memory_tracing_active = false;
+
+ live_memory = new live_memory_map;
+ live_memory_count = new object_count_map;
+ total_memory_count = new object_count_map;
+
+ live_objects = new live_objects_map;
+ live_object_count = new object_count_map;
+ total_object_count = new object_count_map;
+ total_ctor_count = new object_count_map;
+
+ memory_tracing_active = true;
+}
+
+void shutdown_memory_tracing()
+{
+ memory_tracing_active = false;
+
+ if (live_objects) {
+ IF_DEBUG("memory.counts")
+ report_memory(std::cerr, true);
+ else
+ IF_DEBUG("memory.counts.live")
+ report_memory(std::cerr);
+ else if (live_objects->size() > 0)
+ report_memory(std::cerr);
+ }
+
+ checked_delete(live_memory); live_memory = NULL;
+ checked_delete(live_memory_count); live_memory_count = NULL;
+ checked_delete(total_memory_count); total_memory_count = NULL;
+
+ checked_delete(live_objects); live_objects = NULL;
+ checked_delete(live_object_count); live_object_count = NULL;
+ checked_delete(total_object_count); total_object_count = NULL;
+ checked_delete(total_ctor_count); total_ctor_count = NULL;
+}
+
+inline void add_to_count_map(object_count_map& the_map,
+ const char * name, std::size_t size)
+{
+ object_count_map::iterator k = the_map.find(name);
+ if (k != the_map.end()) {
+ (*k).second.first++;
+ (*k).second.second += size;
+ } else {
+ std::pair<object_count_map::iterator, bool> result =
+ the_map.insert(object_count_map::value_type(name, count_size_pair(1, size)));
+ VERIFY(result.second);
+ }
+}
+
+std::size_t current_memory_size()
+{
+ std::size_t memory_size = 0;
+
+ for (object_count_map::const_iterator i = live_memory_count->begin();
+ i != live_memory_count->end();
+ i++)
+ memory_size += (*i).second.second;
+
+ return memory_size;
+}
+
+static void trace_new_func(void * ptr, const char * which, std::size_t size)
+{
+ memory_tracing_active = false;
+
+ if (! live_memory) return;
+
+ live_memory->insert
+ (live_memory_map::value_type(ptr, allocation_pair(which, size)));
+
+ add_to_count_map(*live_memory_count, which, size);
+ add_to_count_map(*total_memory_count, which, size);
+ add_to_count_map(*total_memory_count, "__ALL__", size);
+
+ memory_tracing_active = true;
+}
+
+static void trace_delete_func(void * ptr, const char * which)
+{
+ memory_tracing_active = false;
+
+ if (! live_memory) return;
+
+ // Ignore deletions of memory not tracked, since it's possible that
+ // a user (like boost) allocated a block of memory before memory
+ // tracking began, and then deleted it before memory tracking ended.
+ // If it really is a double-delete, the malloc library on OS/X will
+ // notify me.
+
+ live_memory_map::iterator i = live_memory->find(ptr);
+ if (i == live_memory->end())
+ return;
+
+ std::size_t size = (*i).second.second;
+ VERIFY((*i).second.first == which);
+
+ live_memory->erase(i);
+
+ object_count_map::iterator j = live_memory_count->find(which);
+ VERIFY(j != live_memory_count->end());
+
+ (*j).second.second -= size;
+ if (--(*j).second.first == 0)
+ live_memory_count->erase(j);
+
+ memory_tracing_active = true;
+}
+
+} // namespace ledger
+
+void * operator new(std::size_t size) throw (std::bad_alloc) {
+ void * ptr = std::malloc(size);
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_new_func(ptr, "new", size);
+ return ptr;
+}
+void * operator new(std::size_t size, const std::nothrow_t&) throw() {
+ void * ptr = std::malloc(size);
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_new_func(ptr, "new", size);
+ return ptr;
+}
+void * operator new[](std::size_t size) throw (std::bad_alloc) {
+ void * ptr = std::malloc(size);
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_new_func(ptr, "new[]", size);
+ return ptr;
+}
+void * operator new[](std::size_t size, const std::nothrow_t&) throw() {
+ void * ptr = std::malloc(size);
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_new_func(ptr, "new[]", size);
+ return ptr;
+}
+void operator delete(void * ptr) throw() {
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_delete_func(ptr, "new");
+ std::free(ptr);
+}
+void operator delete(void * ptr, const std::nothrow_t&) throw() {
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_delete_func(ptr, "new");
+ std::free(ptr);
+}
+void operator delete[](void * ptr) throw() {
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_delete_func(ptr, "new[]");
+ std::free(ptr);
+}
+void operator delete[](void * ptr, const std::nothrow_t&) throw() {
+ if (DO_VERIFY() && ledger::memory_tracing_active)
+ ledger::trace_delete_func(ptr, "new[]");
+ std::free(ptr);
+}
+
+namespace ledger {
+
+inline void report_count_map(std::ostream& out, object_count_map& the_map)
+{
+ for (object_count_map::iterator i = the_map.begin();
+ i != the_map.end();
+ i++)
+ out << " " << std::right << std::setw(12) << (*i).second.first
+ << " " << std::right << std::setw(12) << (*i).second.second
+ << " " << std::left << (*i).first
+ << std::endl;
+}
+
+std::size_t current_objects_size()
+{
+ std::size_t objects_size = 0;
+
+ for (object_count_map::const_iterator i = live_object_count->begin();
+ i != live_object_count->end();
+ i++)
+ objects_size += (*i).second.second;
+
+ return objects_size;
+}
+
+void trace_ctor_func(void * ptr, const char * cls_name, const char * args,
+ std::size_t cls_size)
+{
+ memory_tracing_active = false;
+
+ if (! live_objects) return;
+
+ static char name[1024];
+ std::strcpy(name, cls_name);
+ std::strcat(name, "(");
+ std::strcat(name, args);
+ std::strcat(name, ")");
+
+ DEBUG("memory.debug", "TRACE_CTOR " << ptr << " " << name);
+
+ live_objects->insert
+ (live_objects_map::value_type(ptr, allocation_pair(cls_name, cls_size)));
+
+ add_to_count_map(*live_object_count, cls_name, cls_size);
+ add_to_count_map(*total_object_count, cls_name, cls_size);
+ add_to_count_map(*total_object_count, "__ALL__", cls_size);
+ add_to_count_map(*total_ctor_count, name, cls_size);
+
+ memory_tracing_active = true;
+}
+
+void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size)
+{
+ memory_tracing_active = false;
+
+ if (! live_objects) return;
+
+ DEBUG("memory.debug", "TRACE_DTOR " << ptr << " " << cls_name);
+
+ live_objects_map::iterator i = live_objects->find(ptr);
+ VERIFY(i != live_objects->end());
+
+ int ptr_count = live_objects->count(ptr);
+ for (int x = 0; x < ptr_count; x++, i++) {
+ if ((*i).second.first == cls_name) {
+ live_objects->erase(i);
+ break;
+ }
+ }
+
+ object_count_map::iterator k = live_object_count->find(cls_name);
+ VERIFY(k != live_object_count->end());
+
+ (*k).second.second -= cls_size;
+ if (--(*k).second.first == 0)
+ live_object_count->erase(k);
+
+ memory_tracing_active = true;
+}
+
+void report_memory(std::ostream& out, bool report_all)
+{
+ if (! live_memory) return;
+
+ if (live_memory_count->size() > 0) {
+ out << "NOTE: There may be memory held by Boost "
+ << "and libstdc++ after ledger::shutdown()" << std::endl;
+ out << "Live memory count:" << std::endl;
+ report_count_map(out, *live_memory_count);
+ }
+
+ if (live_memory->size() > 0) {
+ out << "Live memory:" << std::endl;
+
+ for (live_memory_map::const_iterator i = live_memory->begin();
+ i != live_memory->end();
+ i++)
+ out << " " << std::right << std::setw(7) << (*i).first
+ << " " << std::right << std::setw(7) << (*i).second.second
+ << " " << std::left << (*i).second.first
+ << std::endl;
+ }
+
+ if (report_all && total_memory_count->size() > 0) {
+ out << "Total memory counts:" << std::endl;
+ report_count_map(out, *total_memory_count);
+ }
+
+ if (live_object_count->size() > 0) {
+ out << "Live object count:" << std::endl;
+ report_count_map(out, *live_object_count);
+ }
+
+ if (live_objects->size() > 0) {
+ out << "Live objects:" << std::endl;
+
+ for (live_objects_map::const_iterator i = live_objects->begin();
+ i != live_objects->end();
+ i++)
+ out << " " << std::right << std::setw(7) << (*i).first
+ << " " << std::right << std::setw(7) << (*i).second.second
+ << " " << std::left << (*i).second.first
+ << std::endl;
+ }
+
+ if (report_all) {
+ if (total_object_count->size() > 0) {
+ out << "Total object counts:" << std::endl;
+ report_count_map(out, *total_object_count);
+ }
+
+ if (total_ctor_count->size() > 0) {
+ out << "Total constructor counts:" << std::endl;
+ report_count_map(out, *total_ctor_count);
+ }
+ }
+}
+
+
+string::string() : std::string() {
+ TRACE_CTOR(string, "");
+}
+string::string(const string& str) : std::string(str) {
+ TRACE_CTOR(string, "const string&");
+}
+string::string(const std::string& str) : std::string(str) {
+ TRACE_CTOR(string, "const std::string&");
+}
+string::string(const int len, char x) : std::string(len, x) {
+ TRACE_CTOR(string, "const int, char");
+}
+string::string(const char * str) : std::string(str) {
+ TRACE_CTOR(string, "const char *");
+}
+string::string(const char * str, const char * end) : std::string(str, end) {
+ TRACE_CTOR(string, "const char *, const char *");
+}
+string::string(const string& str, int x) : std::string(str, x) {
+ TRACE_CTOR(string, "const string&, int");
+}
+string::string(const string& str, int x, int y) : std::string(str, x, y) {
+ TRACE_CTOR(string, "const string&, int, int");
+}
+string::string(const char * str, int x) : std::string(str, x) {
+ TRACE_CTOR(string, "const char *, int");
+}
+string::string(const char * str, int x, int y) : std::string(str, x, y) {
+ TRACE_CTOR(string, "const char *, int, int");
+}
+string::~string() {
+ TRACE_DTOR(string);
+}
+
+} // namespace ledger
+
+#endif // VERIFY_ON
+
+/**********************************************************************
+ *
+ * Logging
+ */
+
+#if defined(LOGGING_ON)
+
+namespace ledger {
+
+log_level_t _log_level = LOG_WARN;
+std::ostream * _log_stream = &std::cerr;
+std::ostringstream _log_buffer;
+
+#if defined(TRACING_ON)
+unsigned int _trace_level;
+#endif
+
+#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK
+#define CURRENT_TIME() boost::posix_time::microsec_clock::universal_time()
+#else
+#define CURRENT_TIME() boost::posix_time::second_clock::universal_time()
+#endif
+
+static inline void stream_memory_size(std::ostream& out, std::size_t size)
+{
+ if (size < 1024)
+ out << size << 'b';
+ else if (size < (1024 * 1024))
+ out << (double(size) / 1024.0) << 'K';
+ else if (size < (1024 * 1024 * 1024))
+ out << (double(size) / (1024.0 * 1024.0)) << 'M';
+ else
+ out << (double(size) / (1024.0 * 1024.0 * 1024.0)) << 'G';
+}
+
+static bool logger_has_run = false;
+static ptime logger_start;
+
+bool logger_func(log_level_t level)
+{
+ unsigned long appender = 0;
+
+ if (! logger_has_run) {
+ logger_has_run = true;
+ logger_start = CURRENT_TIME();
+
+ IF_VERIFY()
+ *_log_stream << " TIME OBJSZ MEMSZ" << std::endl;
+
+ appender = (logger_start - now).total_milliseconds();
+ }
+
+ *_log_stream << std::right << std::setw(5)
+ << (CURRENT_TIME() - logger_start).total_milliseconds();
+
+ IF_VERIFY() {
+ *_log_stream << std::right << std::setw(6) << std::setprecision(3);
+ stream_memory_size(*_log_stream, current_objects_size());
+ *_log_stream << std::right << std::setw(6) << std::setprecision(3);
+ stream_memory_size(*_log_stream, current_memory_size());
+ }
+
+ *_log_stream << " " << std::left << std::setw(7);
+
+ switch (level) {
+ case LOG_CRIT: *_log_stream << "[CRIT]"; break;
+ case LOG_FATAL: *_log_stream << "[FATAL]"; break;
+ case LOG_ASSERT: *_log_stream << "[ASSRT]"; break;
+ case LOG_ERROR: *_log_stream << "[ERROR]"; break;
+ case LOG_VERIFY: *_log_stream << "[VERFY]"; break;
+ case LOG_WARN: *_log_stream << "[WARN]"; break;
+ case LOG_INFO: *_log_stream << "[INFO]"; break;
+ case LOG_EXCEPT: *_log_stream << "[EXCPT]"; break;
+ case LOG_DEBUG: *_log_stream << "[DEBUG]"; break;
+ case LOG_TRACE: *_log_stream << "[TRACE]"; break;
+
+ case LOG_OFF:
+ case LOG_ALL:
+ assert(false);
+ break;
+ }
+
+ *_log_stream << ' ' << _log_buffer.str();
+
+ if (appender)
+ *_log_stream << " (" << appender << "ms startup)";
+
+ *_log_stream << std::endl;
+
+ _log_buffer.str("");
+
+ return true;
+}
+
+} // namespace ledger
+
+#if defined(DEBUG_ON)
+
+namespace ledger {
+
+optional<std::string> _log_category;
+
+} // namespace ledger
+
+#endif // DEBUG_ON
+#endif // LOGGING_ON
+
+/**********************************************************************
+ *
+ * Timers (allows log entries to specify cumulative time spent)
+ */
+
+#if defined(LOGGING_ON) && defined(TIMERS_ON)
+
+namespace ledger {
+
+struct timer_t {
+ log_level_t level;
+ ptime begin;
+ time_duration spent;
+ std::string description;
+ bool active;
+
+ timer_t(log_level_t _level, std::string _description)
+ : level(_level), begin(CURRENT_TIME()),
+ spent(time_duration(0, 0, 0, 0)),
+ description(_description), active(true) {}
+};
+
+typedef std::map<std::string, timer_t> timer_map;
+
+static timer_map timers;
+
+void start_timer(const char * name, log_level_t lvl)
+{
+#if defined(VERIFY_ON)
+ memory_tracing_active = false;
+#endif
+
+ timer_map::iterator i = timers.find(name);
+ if (i == timers.end()) {
+ timers.insert(timer_map::value_type(name, timer_t(lvl, _log_buffer.str())));
+ } else {
+ assert((*i).second.description == _log_buffer.str());
+ (*i).second.begin = CURRENT_TIME();
+ (*i).second.active = true;
+ }
+ _log_buffer.str("");
+
+#if defined(VERIFY_ON)
+ memory_tracing_active = true;
+#endif
+}
+
+void stop_timer(const char * name)
+{
+#if defined(VERIFY_ON)
+ memory_tracing_active = false;
+#endif
+
+ timer_map::iterator i = timers.find(name);
+ assert(i != timers.end());
+
+ (*i).second.spent += CURRENT_TIME() - (*i).second.begin;
+ (*i).second.active = false;
+
+#if defined(VERIFY_ON)
+ memory_tracing_active = true;
+#endif
+}
+
+void finish_timer(const char * name)
+{
+#if defined(VERIFY_ON)
+ memory_tracing_active = false;
+#endif
+
+ timer_map::iterator i = timers.find(name);
+ if (i == timers.end())
+ return;
+
+ time_duration spent = (*i).second.spent;
+ if ((*i).second.active) {
+ spent = CURRENT_TIME() - (*i).second.begin;
+ (*i).second.active = false;
+ }
+
+ _log_buffer << (*i).second.description << ' ';
+
+ bool need_paren =
+ (*i).second.description[(*i).second.description.size() - 1] != ':';
+
+ if (need_paren)
+ _log_buffer << '(';
+
+ _log_buffer << spent.total_milliseconds() << "ms";
+
+ if (need_paren)
+ _log_buffer << ')';
+
+ logger_func((*i).second.level);
+
+ timers.erase(i);
+
+#if defined(VERIFY_ON)
+ memory_tracing_active = true;
+#endif
+}
+
+} // namespace ledger
+
+#endif // LOGGING_ON && TIMERS_ON
+
+/**********************************************************************
+ *
+ * Exception handling
+ */
+
+namespace ledger {
+
+std::ostringstream _exc_buffer;
+/*ptr_list<context> context_stack;*/
+
+} // namespace ledger
+
+/**********************************************************************
+ *
+ * General utility functions
+ */
+
+namespace ledger {
+
+path expand_path(const path& pathname)
+{
+ if (pathname.empty())
+ return pathname;
+
+#if 1
+ return pathname;
+#else
+ // jww (2007-04-30): I need to port this code to use
+ // boost::filesystem::path
+ const char * pfx = NULL;
+ string::size_type pos = pathname.find_first_of('/');
+
+ if (pathname.length() == 1 || pos == 1) {
+ pfx = std::getenv("HOME");
+#ifdef HAVE_GETPWUID
+ if (! pfx) {
+ // Punt. We're trying to expand ~/, but HOME isn't set
+ struct passwd * pw = getpwuid(getuid());
+ if (pw)
+ pfx = pw->pw_dir;
+ }
+#endif
+ }
+#ifdef HAVE_GETPWNAM
+ else {
+ string user(pathname, 1, pos == string::npos ?
+ string::npos : pos - 1);
+ struct passwd * pw = getpwnam(user.c_str());
+ if (pw)
+ pfx = pw->pw_dir;
+ }
+#endif
+
+ // if we failed to find an expansion, return the path unchanged.
+
+ if (! pfx)
+ return pathname;
+
+ string result(pfx);
+
+ if (pos == string::npos)
+ return result;
+
+ if (result.length() == 0 || result[result.length() - 1] != '/')
+ result += '/';
+
+ result += pathname.substr(pos + 1);
+
+ return result;
+#endif
+}
+
+path resolve_path(const path& pathname)
+{
+ path temp = pathname;
+ if (temp.string()[0] == '~')
+ temp = expand_path(temp);
+ temp.normalize();
+ return temp;
+}
+
+} // namespace ledger
diff --git a/utils.h b/utils.h
new file mode 100644
index 00000000..47f36d89
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file utils.h
+ * @author John Wiegley
+ * @date Sun May 6 21:20:00 2007
+ *
+ * @brief This file contains general utility facilities used by Ledger.
+ *
+ * Ledger has need of the following utility code, which this file
+ * provides or includes in:
+ *
+ * - system headers
+ * - asserts
+ * - verification (basically, "heavy asserts")
+ * - tracing code
+ * - debug logging code
+ * - timing code
+ * - current error context
+ * - exception framework
+ * - date/time type
+ * - supports_flags<> for objects that use flags
+ * - push_variable<> for restoring variable values
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#if defined(DEBUG_MODE)
+#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE 1
+#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING 1
+#endif
+
+#include <system.hh>
+
+/**********************************************************************
+ *
+ * Default values
+ */
+
+#if defined(DEBUG_MODE)
+#define VERIFY_ON 1
+#define TRACING_ON 1
+#define DEBUG_ON 1
+#define TIMERS_ON 1
+#elif defined(NDEBUG)
+#define NO_ASSERTS 1
+#define NO_LOGGING 1
+#else
+#define VERIFY_ON 1 // compiled in, use --verify to enable
+#define TRACING_ON 1 // use --trace X to enable
+#define TIMERS_ON 1
+#endif
+
+/**********************************************************************
+ *
+ * Forward declarations
+ */
+
+namespace ledger {
+ using namespace boost;
+
+#if defined(VERIFY_ON)
+ class string;
+#else
+ typedef std::string string;
+#endif
+
+ typedef posix_time::ptime ptime;
+ typedef ptime::time_duration_type time_duration;
+ typedef gregorian::date date;
+ typedef gregorian::date_duration date_duration;
+ typedef posix_time::seconds seconds;
+
+ typedef boost::filesystem::path path;
+ typedef boost::filesystem::ifstream ifstream;
+ typedef boost::filesystem::ofstream ofstream;
+ typedef boost::filesystem::filesystem_error filesystem_error;
+}
+
+/**********************************************************************
+ *
+ * Assertions
+ */
+
+#ifdef assert
+#undef assert
+#endif
+
+#if ! defined(NO_ASSERTS)
+#define ASSERTS_ON 1
+#endif
+#if defined(ASSERTS_ON)
+
+namespace ledger {
+ void debug_assert(const string& reason, const string& func,
+ const string& file, unsigned long line);
+}
+
+#define assert(x) \
+ ((x) ? ((void)0) : debug_assert(#x, BOOST_CURRENT_FUNCTION, \
+ __FILE__, __LINE__))
+
+#else // ! ASSERTS_ON
+
+#define assert(x)
+
+#endif // ASSERTS_ON
+
+/**********************************************************************
+ *
+ * Verification (basically, very slow asserts)
+ */
+
+#if defined(VERIFY_ON)
+
+namespace ledger {
+
+extern bool verify_enabled;
+
+#define VERIFY(x) (ledger::verify_enabled ? assert(x) : ((void)0))
+#define DO_VERIFY() ledger::verify_enabled
+
+void initialize_memory_tracing();
+void shutdown_memory_tracing();
+
+std::size_t current_memory_size();
+std::size_t current_objects_size();
+
+void trace_ctor_func(void * ptr, const char * cls_name, const char * args,
+ std::size_t cls_size);
+void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size);
+
+#define TRACE_CTOR(cls, args) \
+ (DO_VERIFY() ? trace_ctor_func(this, #cls, args, sizeof(cls)) : ((void)0))
+#define TRACE_DTOR(cls) \
+ (DO_VERIFY() ? trace_dtor_func(this, #cls, sizeof(cls)) : ((void)0))
+
+void report_memory(std::ostream& out, bool report_all = false);
+
+/**
+ * This string type is a wrapper around std::string that allows us to
+ * trace constructor and destructor calls.
+ */
+class string : public std::string
+{
+public:
+ string();
+ string(const string& str);
+ string(const std::string& str);
+ string(const int len, char x);
+ string(const char * str);
+ string(const char * str, const char * end);
+ string(const string& str, int x);
+ string(const string& str, int x, int y);
+ string(const char * str, int x);
+ string(const char * str, int x, int y);
+ ~string();
+};
+
+inline string operator+(const string& __lhs, const string& __rhs)
+{
+ string __str(__lhs);
+ __str.append(__rhs);
+ return __str;
+}
+
+string operator+(const char* __lhs, const string& __rhs);
+string operator+(char __lhs, const string& __rhs);
+
+inline string operator+(const string& __lhs, const char* __rhs)
+{
+ string __str(__lhs);
+ __str.append(__rhs);
+ return __str;
+}
+
+inline string operator+(const string& __lhs, char __rhs)
+{
+ typedef string __string_type;
+ typedef string::size_type __size_type;
+ __string_type __str(__lhs);
+ __str.append(__size_type(1), __rhs);
+ return __str;
+}
+
+inline bool operator==(const string& __lhs, const string& __rhs)
+{ return __lhs.compare(__rhs) == 0; }
+
+inline bool operator==(const char* __lhs, const string& __rhs)
+{ return __rhs.compare(__lhs) == 0; }
+
+inline bool operator==(const string& __lhs, const char* __rhs)
+{ return __lhs.compare(__rhs) == 0; }
+
+inline bool operator!=(const string& __lhs, const string& __rhs)
+{ return __rhs.compare(__lhs) != 0; }
+
+inline bool operator!=(const char* __lhs, const string& __rhs)
+{ return __rhs.compare(__lhs) != 0; }
+
+inline bool operator!=(const string& __lhs, const char* __rhs)
+{ return __lhs.compare(__rhs) != 0; }
+
+} // namespace ledger
+
+#else // ! VERIFY_ON
+
+#define VERIFY(x)
+#define DO_VERIFY() true
+#define TRACE_CTOR(cls, args)
+#define TRACE_DTOR(cls)
+
+#endif // VERIFY_ON
+
+#define IF_VERIFY() if (DO_VERIFY())
+
+/**********************************************************************
+ *
+ * Logging
+ */
+
+#if ! defined(NO_LOGGING)
+#define LOGGING_ON 1
+#endif
+#if defined(LOGGING_ON)
+
+namespace ledger {
+
+enum log_level_t {
+ LOG_OFF = 0,
+ LOG_CRIT,
+ LOG_FATAL,
+ LOG_ASSERT,
+ LOG_ERROR,
+ LOG_VERIFY,
+ LOG_WARN,
+ LOG_INFO,
+ LOG_EXCEPT,
+ LOG_DEBUG,
+ LOG_TRACE,
+ LOG_ALL
+};
+
+extern log_level_t _log_level;
+extern std::ostream * _log_stream;
+extern std::ostringstream _log_buffer;
+
+bool logger_func(log_level_t level);
+
+#define LOGGER(cat) \
+ static const char * const _this_category = cat
+
+#if defined(TRACING_ON)
+
+extern unsigned int _trace_level;
+
+#define SHOW_TRACE(lvl) \
+ (ledger::_log_level >= ledger::LOG_TRACE && lvl <= ledger::_trace_level)
+#define TRACE(lvl, msg) \
+ (SHOW_TRACE(lvl) ? \
+ ((ledger::_log_buffer << msg), \
+ ledger::logger_func(ledger::LOG_TRACE)) : false)
+
+#else // TRACING_ON
+
+#define SHOW_TRACE(lvl) false
+#define TRACE(lvl, msg)
+
+#endif // TRACING_ON
+
+#if defined(DEBUG_ON)
+
+extern optional<std::string> _log_category;
+
+inline bool category_matches(const char * cat) {
+ return _log_category && starts_with(cat, *_log_category);
+}
+
+#define SHOW_DEBUG(cat) \
+ (ledger::_log_level >= ledger::LOG_DEBUG && ledger::category_matches(cat))
+#define SHOW_DEBUG_() SHOW_DEBUG(_this_category)
+
+#define DEBUG(cat, msg) \
+ (SHOW_DEBUG(cat) ? \
+ ((ledger::_log_buffer << msg), \
+ ledger::logger_func(ledger::LOG_DEBUG)) : false)
+#define DEBUG_(msg) DEBUG(_this_category, msg)
+
+#else // DEBUG_ON
+
+#define SHOW_DEBUG(cat) false
+#define SHOW_DEBUG_() false
+#define DEBUG(cat, msg)
+#define DEBUG_(msg)
+
+#endif // DEBUG_ON
+
+#define LOG_MACRO(level, msg) \
+ (ledger::_log_level >= level ? \
+ ((ledger::_log_buffer << msg), ledger::logger_func(level)) : false)
+
+#define SHOW_INFO() (ledger::_log_level >= ledger::LOG_INFO)
+#define SHOW_WARN() (ledger::_log_level >= ledger::LOG_WARN)
+#define SHOW_ERROR() (ledger::_log_level >= ledger::LOG_ERROR)
+#define SHOW_FATAL() (ledger::_log_level >= ledger::LOG_FATAL)
+#define SHOW_CRITICAL() (ledger::_log_level >= ledger::LOG_CRIT)
+
+#define INFO(msg) LOG_MACRO(ledger::LOG_INFO, msg)
+#define WARN(msg) LOG_MACRO(ledger::LOG_WARN, msg)
+#define ERROR(msg) LOG_MACRO(ledger::LOG_ERROR, msg)
+#define FATAL(msg) LOG_MACRO(ledger::LOG_FATAL, msg)
+#define CRITICAL(msg) LOG_MACRO(ledger::LOG_CRIT, msg)
+#define EXCEPTION(msg) LOG_MACRO(ledger::LOG_EXCEPT, msg)
+
+} // namespace ledger
+
+#else // ! LOGGING_ON
+
+#define LOGGER(cat)
+
+#define SHOW_TRACE(lvl) false
+#define SHOW_DEBUG(cat) false
+#define SHOW_DEBUG_() false
+#define SHOW_INFO() false
+#define SHOW_WARN() false
+#define SHOW_ERROR() false
+#define SHOW_FATAL() false
+#define SHOW_CRITICAL() false
+
+#define TRACE(lvl, msg)
+#define DEBUG(cat, msg)
+#define DEBUG_(msg)
+#define INFO(msg)
+#define WARN(msg)
+#define ERROR(msg)
+#define FATAL(msg)
+#define CRITICAL(msg)
+
+#endif // LOGGING_ON
+
+#define IF_TRACE(lvl) if (SHOW_TRACE(lvl))
+#define IF_DEBUG(cat) if (SHOW_DEBUG(cat))
+#define IF_DEBUG_() if (SHOW_DEBUG_())
+#define IF_INFO() if (SHOW_INFO())
+#define IF_WARN() if (SHOW_WARN())
+#define IF_ERROR() if (SHOW_ERROR())
+#define IF_FATAL() if (SHOW_FATAL())
+#define IF_CRITICAL() if (SHOW_CRITICAL())
+
+/**********************************************************************
+ *
+ * Timers (allows log entries to specify cumulative time spent)
+ */
+
+#if defined(LOGGING_ON) && defined(TIMERS_ON)
+
+namespace ledger {
+
+void start_timer(const char * name, log_level_t lvl);
+void stop_timer(const char * name);
+void finish_timer(const char * name);
+
+#if defined(TRACING_ON)
+#define TRACE_START(name, lvl, msg) \
+ (SHOW_TRACE(lvl) ? \
+ ((ledger::_log_buffer << msg), \
+ ledger::start_timer(#name, ledger::LOG_TRACE)) : ((void)0))
+#define TRACE_STOP(name, lvl) \
+ (SHOW_TRACE(lvl) ? ledger::stop_timer(#name) : ((void)0))
+#define TRACE_FINISH(name, lvl) \
+ (SHOW_TRACE(lvl) ? ledger::finish_timer(#name) : ((void)0))
+#else
+#define TRACE_START(name, lvl, msg)
+#define TRACE_STOP(name)
+#define TRACE_FINISH(name)
+#endif
+
+#if defined(DEBUG_ON)
+#define DEBUG_START(name, cat, msg) \
+ (SHOW_DEBUG(cat) ? \
+ ((ledger::_log_buffer << msg), \
+ ledger::start_timer(#name, ledger::LOG_DEBUG)) : ((void)0))
+#define DEBUG_START_(name, msg) \
+ DEBUG_START_(name, _this_category, msg)
+#define DEBUG_STOP(name, cat) \
+ (SHOW_DEBUG(cat) ? ledger::stop_timer(#name) : ((void)0))
+#define DEBUG_STOP_(name) \
+ DEBUG_STOP_(name, _this_category)
+#define DEBUG_FINISH(name, cat) \
+ (SHOW_DEBUG(cat) ? ledger::finish_timer(#name) : ((void)0))
+#define DEBUG_FINISH_(name) \
+ DEBUG_FINISH_(name, _this_category)
+#else
+#define DEBUG_START(name, cat, msg)
+#define DEBUG_START_(name, msg)
+#define DEBUG_STOP(name)
+#define DEBUG_FINISH(name)
+#endif
+
+#define INFO_START(name, msg) \
+ (SHOW_INFO() ? \
+ ((ledger::_log_buffer << msg), \
+ ledger::start_timer(#name, ledger::LOG_INFO)) : ((void)0))
+#define INFO_STOP(name) \
+ (SHOW_INFO() ? stop_timer(#name) : ((void)0))
+#define INFO_FINISH(name) \
+ (SHOW_INFO() ? finish_timer(#name) : ((void)0))
+
+} // namespace ledger
+
+#else // ! (LOGGING_ON && TIMERS_ON)
+
+#define TRACE_START(lvl, msg, name)
+#define TRACE_STOP(name)
+#define TRACE_FINISH(name)
+
+#define DEBUG_START(name, msg)
+#define DEBUG_START_(name, cat, msg)
+#define DEBUG_STOP(name)
+#define DEBUG_FINISH(name)
+
+#define INFO_START(name, msg)
+#define INFO_STOP(name)
+#define INFO_FINISH(name)
+
+#endif // TIMERS_ON
+
+/**********************************************************************
+ *
+ * Exception handling
+ */
+
+#include "error.h"
+
+namespace ledger {
+
+extern std::ostringstream _exc_buffer;
+
+template <typename T>
+inline void throw_func(const std::string& message) {
+ _exc_buffer.str("");
+ throw T(message);
+}
+
+#define throw_(cls, msg) \
+ ((_exc_buffer << msg), throw_func<cls>(_exc_buffer.str()))
+
+#if 0
+inline void throw_unexpected_error(char c, char wanted) {
+ if (c == -1) {
+ if (wanted)
+ throw new error(string("Missing '") + wanted + "'");
+ else
+ throw new error("Unexpected end of input");
+ } else {
+ if (wanted)
+ throw new error(string("Invalid char '") + c +
+ "' (wanted '" + wanted + "')");
+ else
+ throw new error(string("Invalid char '") + c + "'");
+ }
+}
+#else
+inline void throw_unexpected_error(char, char) {
+}
+#endif
+
+} // namespace ledger
+
+/**********************************************************************
+ *
+ * Date/time support classes
+ * General support for objects with "flags"
+ * Support for object serialization (binary read/write)
+ * Support for scoped execution and variable restoration
+ */
+
+#include "times.h"
+#include "flags.h"
+#include "binary.h"
+#include "pushvar.h"
+
+/**********************************************************************
+ *
+ * General utility functions
+ */
+
+#define foreach BOOST_FOREACH
+
+namespace ledger {
+
+template <typename T, typename U>
+inline T& downcast(U& object) {
+ return *polymorphic_downcast<T *>(&object);
+}
+
+path resolve_path(const path& pathname);
+
+#ifdef HAVE_REALPATH
+extern "C" char * realpath(const char *, char resolved_path[]);
+#endif
+
+inline const string& either_or(const string& first,
+ const string& second) {
+ return first.empty() ? second : first;
+}
+
+} // namespace ledger
+
+#endif // _UTILS_H
diff --git a/valgrind.sh b/valgrind.sh
new file mode 100755
index 00000000..fe292f44
--- /dev/null
+++ b/valgrind.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+VALGRIND=$(which valgrind 2>&1)
+
+if [ -x "$VALGRIND" ]; then
+ exec "$VALGRIND" --leak-check=full --show-reachable=yes "$@"
+else
+ exec "$@"
+fi
diff --git a/value.cc b/value.cc
index 641beb49..7a7249da 100644
--- a/value.cc
+++ b/value.cc
@@ -1,1248 +1,1286 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "value.h"
-#include "debug.h"
-#include "error.h"
namespace ledger {
-void value_t::destroy()
+intrusive_ptr<value_t::storage_t> value_t::true_value;
+intrusive_ptr<value_t::storage_t> value_t::false_value;
+
+void value_t::storage_t::destroy()
{
switch (type) {
case AMOUNT:
- ((amount_t *)data)->~amount_t();
+ reinterpret_cast<amount_t *>(data)->~amount_t();
break;
case BALANCE:
- ((balance_t *)data)->~balance_t();
+ checked_delete(*reinterpret_cast<balance_t **>(data));
break;
case BALANCE_PAIR:
- ((balance_pair_t *)data)->~balance_pair_t();
+ checked_delete(*reinterpret_cast<balance_pair_t **>(data));
+ break;
+ case STRING:
+ reinterpret_cast<string *>(data)->~string();
+ break;
+ case SEQUENCE:
+ checked_delete(*reinterpret_cast<sequence_t **>(data));
+ break;
+ case POINTER:
+ reinterpret_cast<boost::any *>(data)->~any();
break;
+
default:
break;
}
+ type = VOID;
}
-void value_t::simplify()
+void value_t::initialize()
{
- if (realzero()) {
- DEBUG_PRINT("amounts.values.simplify", "Zeroing type " << type);
- *this = 0L;
- return;
+#if 0
+ LOGGER("value.initialize");
+#endif
+
+ true_value = new storage_t;
+ true_value->type = BOOLEAN;
+ *reinterpret_cast<bool *>(true_value->data) = true;
+
+ false_value = new storage_t;
+ false_value->type = BOOLEAN;
+ *reinterpret_cast<bool *>(false_value->data) = false;
+
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(moment_t));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(long));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(amount_t));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *));
+ BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any));
+
+#if 0
+ DEBUG_(std::setw(3) << std::right << sizeof(bool)
+ << " sizeof(bool)");
+ DEBUG_(std::setw(3) << std::right << sizeof(moment_t)
+ << " sizeof(moment_t)");
+ DEBUG_(std::setw(3) << std::right << sizeof(long)
+ << " sizeof(long)");
+ DEBUG_(std::setw(3) << std::right << sizeof(amount_t)
+ << " sizeof(amount_t)");
+ DEBUG_(std::setw(3) << std::right << sizeof(balance_t *)
+ << " sizeof(balance_t *)");
+ DEBUG_(std::setw(3) << std::right << sizeof(balance_pair_t *)
+ << " sizeof(balance_pair_t *)");
+ DEBUG_(std::setw(3) << std::right << sizeof(string)
+ << " sizeof(string)");
+ DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *)
+ << " sizeof(sequence_t *)");
+ DEBUG_(std::setw(3) << std::right << sizeof(boost::any)
+ << " sizeof(boost::any)");
+#endif
+}
+
+void value_t::shutdown()
+{
+ true_value = intrusive_ptr<storage_t>();
+ false_value = intrusive_ptr<storage_t>();
+}
+
+void value_t::_dup()
+{
+ assert(storage);
+ if (storage->refc > 1) {
+ storage = new storage_t(*storage.get());
+
+ // If the data referenced by storage is an allocated pointer, we
+ // need to create a new object in order to achieve duplication.
+ switch (storage->type) {
+ case BALANCE:
+ *(balance_t **) storage->data =
+ new balance_t(**(balance_t **) storage->data);
+ break;
+ case BALANCE_PAIR:
+ *(balance_pair_t **) storage->data =
+ new balance_pair_t(**(balance_pair_t **) storage->data);
+ break;
+ case SEQUENCE:
+ *(sequence_t **) storage->data =
+ new sequence_t(**(sequence_t **) storage->data);
+ break;
+ default:
+ break; // everything else has been duplicated
+ }
}
+}
- if (type == BALANCE_PAIR &&
- (! ((balance_pair_t *) data)->cost ||
- ((balance_pair_t *) data)->cost->realzero())) {
- DEBUG_PRINT("amounts.values.simplify", "Reducing balance pair to balance");
- cast(BALANCE);
+value_t::operator bool() const
+{
+ switch (type()) {
+ case BOOLEAN:
+ return as_boolean();
+ case INTEGER:
+ return as_long();
+ case DATETIME:
+ return is_valid_moment(as_datetime());
+ case AMOUNT:
+ return as_amount();
+ case BALANCE:
+ return as_balance();
+ case BALANCE_PAIR:
+ return as_balance_pair();
+ case STRING:
+ return ! as_string().empty();
+ case SEQUENCE:
+ return ! as_sequence().empty();
+ case POINTER:
+ return ! as_any_pointer().empty();
+ default:
+ assert(false);
+ break;
}
+ assert(false);
+ return 0;
+}
- if (type == BALANCE &&
- ((balance_t *) data)->amounts.size() == 1) {
- DEBUG_PRINT("amounts.values.simplify", "Reducing balance to amount");
- cast(AMOUNT);
+bool value_t::to_boolean() const
+{
+ if (is_boolean()) {
+ return as_boolean();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(BOOLEAN);
+ return temp.as_boolean();
}
+}
- if (type == AMOUNT &&
- ! ((amount_t *) data)->commodity()) {
- DEBUG_PRINT("amounts.values.simplify", "Reducing amount to integer");
- cast(INTEGER);
+long value_t::to_long() const
+{
+ if (is_long()) {
+ return as_long();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(INTEGER);
+ return temp.as_long();
}
}
-value_t& value_t::operator=(const value_t& value)
+moment_t value_t::to_datetime() const
{
- if (this == &value)
- return *this;
+ if (is_datetime()) {
+ return as_datetime();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(DATETIME);
+ return temp.as_datetime();
+ }
+}
- destroy();
+amount_t value_t::to_amount() const
+{
+ if (is_amount()) {
+ return as_amount();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(AMOUNT);
+ return temp.as_amount();
+ }
+}
- switch (value.type) {
- case BOOLEAN:
- *((bool *) data) = *((bool *) value.data);
- break;
+balance_t value_t::to_balance() const
+{
+ if (is_balance()) {
+ return as_balance();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(BALANCE);
+ return temp.as_balance();
+ }
+}
- case INTEGER:
- *((long *) data) = *((long *) value.data);
- break;
+balance_pair_t value_t::to_balance_pair() const
+{
+ if (is_balance_pair()) {
+ return as_balance_pair();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(BALANCE_PAIR);
+ return temp.as_balance_pair();
+ }
+}
- case DATETIME:
- *((datetime_t *) data) = *((datetime_t *) value.data);
- break;
+string value_t::to_string() const
+{
+ if (is_string()) {
+ return as_string();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(STRING);
+ return temp.as_string();
+ }
+}
- case AMOUNT:
- new((amount_t *)data) amount_t(*((amount_t *) value.data));
- break;
+value_t::sequence_t value_t::to_sequence() const
+{
+ if (is_sequence()) {
+ return as_sequence();
+ } else {
+ value_t temp(*this);
+ temp.in_place_cast(SEQUENCE);
+ return temp.as_sequence();
+ }
+}
- case BALANCE:
- new((balance_t *)data) balance_t(*((balance_t *) value.data));
- break;
- case BALANCE_PAIR:
- new((balance_pair_t *)data) balance_pair_t(*((balance_pair_t *) value.data));
- break;
+void value_t::in_place_simplify()
+{
+ LOGGER("amounts.values.simplify");
- default:
- assert(0);
- break;
+ if (is_realzero()) {
+ DEBUG_("Zeroing type " << type());
+ set_long(0L);
+ return;
}
- type = value.type;
+ if (is_balance_pair() &&
+ (! as_balance_pair().cost || as_balance_pair().cost->is_realzero())) {
+ DEBUG_("Reducing balance pair to balance");
+ in_place_cast(BALANCE);
+ }
- return *this;
+ if (is_balance() && as_balance().amounts.size() == 1) {
+ DEBUG_("Reducing balance to amount");
+ in_place_cast(AMOUNT);
+ }
+
+#if 0
+ if (is_amount() && ! as_amount().has_commodity() &&
+ as_amount().fits_in_long()) {
+ DEBUG_("Reducing amount to integer");
+ in_place_cast(INTEGER);
+ }
+#endif
}
-value_t& value_t::operator+=(const value_t& value)
+value_t& value_t::operator+=(const value_t& val)
{
- if (value.type == BOOLEAN)
- throw new value_error("Cannot add a boolean to a value");
- else if (value.type == DATETIME)
- throw new value_error("Cannot add a date/time to a value");
-
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot add a value to a boolean");
+ if (is_string()) {
+ if (val.is_string())
+ as_string_lval() += val.as_string();
+ else
+ as_string_lval() += val.to_string();
+ return *this;
+ }
+ else if (is_sequence()) {
+ if (val.is_sequence()) {
+ sequence_t& seq(as_sequence_lval());
+ seq.insert(seq.end(), val.as_sequence().begin(),
+ val.as_sequence().end());
+ } else {
+ as_sequence_lval().push_back(val);
+ }
+ return *this;
+ }
- case INTEGER:
- switch (value.type) {
+ switch (type()) {
+ case DATETIME:
+ switch (val.type()) {
case INTEGER:
- *((long *) data) += *((long *) value.data);
- break;
+ as_datetime_lval() += date_duration(val.as_long());
+ return *this;
case AMOUNT:
- cast(AMOUNT);
- *((amount_t *) data) += *((amount_t *) value.data);
- break;
- case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) += *((balance_t *) value.data);
- break;
- case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) += *((balance_pair_t *) value.data);
- break;
+ as_datetime_lval() += date_duration(val.as_amount().to_long());
+ return *this;
default:
- assert(0);
break;
}
break;
- case DATETIME:
- switch (value.type) {
+ case INTEGER:
+ switch (val.type()) {
case INTEGER:
- *((datetime_t *) data) += *((long *) value.data);
- break;
+ as_long_lval() += val.as_long();
+ return *this;
case AMOUNT:
- *((datetime_t *) data) += long(*((amount_t *) value.data));
- break;
+ in_place_cast(AMOUNT);
+ as_amount_lval() += val.as_amount();
+ return *this;
case BALANCE:
- *((datetime_t *) data) += long(*((balance_t *) value.data));
- break;
+ in_place_cast(BALANCE);
+ as_balance_lval() += val.as_balance();
+ return *this;
case BALANCE_PAIR:
- *((datetime_t *) data) += long(*((balance_pair_t *) value.data));
- break;
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
default:
- assert(0);
break;
}
break;
case AMOUNT:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- if (*((long *) value.data) &&
- ((amount_t *) data)->commodity()) {
- cast(BALANCE);
- return *this += value;
+ if (as_amount().has_commodity()) {
+ in_place_cast(BALANCE);
+ return *this += val;
+ } else {
+ as_amount_lval() += val.as_long();
+ return *this;
}
- *((amount_t *) data) += *((long *) value.data);
break;
case AMOUNT:
- if (((amount_t *) data)->commodity() !=
- ((amount_t *) value.data)->commodity()) {
- cast(BALANCE);
- return *this += value;
+ if (as_amount().commodity() != val.as_amount().commodity()) {
+ in_place_cast(BALANCE);
+ return *this += val;
+ } else {
+ as_amount_lval() += val.as_amount();
+ return *this;
}
- *((amount_t *) data) += *((amount_t *) value.data);
break;
case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) += *((balance_t *) value.data);
- break;
+ in_place_cast(BALANCE);
+ as_balance_lval() += val.as_balance();
+ return *this;
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) += *((balance_pair_t *) value.data);
- break;
-
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
default:
- assert(0);
break;
}
break;
case BALANCE:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_t *) data) += *((long *) value.data);
- break;
+ as_balance_lval() += val.to_amount();
+ return *this;
case AMOUNT:
- *((balance_t *) data) += *((amount_t *) value.data);
- break;
+ as_balance_lval() += val.as_amount();
+ return *this;
case BALANCE:
- *((balance_t *) data) += *((balance_t *) value.data);
- break;
+ as_balance_lval() += val.as_balance();
+ return *this;
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) += *((balance_pair_t *) value.data);
- break;
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
default:
- assert(0);
break;
}
break;
case BALANCE_PAIR:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_pair_t *) data) += *((long *) value.data);
- break;
+ as_balance_pair_lval() += val.to_amount();
+ return *this;
case AMOUNT:
- *((balance_pair_t *) data) += *((amount_t *) value.data);
- break;
+ as_balance_pair_lval() += val.as_amount();
+ return *this;
case BALANCE:
- *((balance_pair_t *) data) += *((balance_t *) value.data);
- break;
+ as_balance_pair_lval() += val.as_balance();
+ return *this;
case BALANCE_PAIR:
- *((balance_pair_t *) data) += *((balance_pair_t *) value.data);
- break;
+ as_balance_pair_lval() += val.as_balance_pair();
+ return *this;
default:
- assert(0);
break;
}
break;
default:
- assert(0);
break;
}
+
+ throw_(value_error, "Cannot add " << label() << " to " << val.label());
+
return *this;
}
-value_t& value_t::operator-=(const value_t& value)
+value_t& value_t::operator-=(const value_t& val)
{
- if (value.type == BOOLEAN)
- throw new value_error("Cannot subtract a boolean from a value");
- else if (value.type == DATETIME && type != DATETIME)
- throw new value_error("Cannot subtract a date/time from a value");
-
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot subtract a value from a boolean");
+ if (is_sequence()) {
+ sequence_t& seq(as_sequence_lval());
+
+ if (val.is_sequence()) {
+ for (sequence_t::const_iterator i = val.as_sequence().begin();
+ i != val.as_sequence().end();
+ i++) {
+ sequence_t::iterator j = std::find(seq.begin(), seq.end(), *i);
+ if (j != seq.end())
+ seq.erase(j);
+ }
+ } else {
+ sequence_t::iterator i = std::find(seq.begin(), seq.end(), val);
+ if (i != seq.end())
+ seq.erase(i);
+ }
+ return *this;
+ }
- case INTEGER:
- switch (value.type) {
+ switch (type()) {
+ case DATETIME:
+ switch (val.type()) {
case INTEGER:
- *((long *) data) -= *((long *) value.data);
- break;
+ as_datetime_lval() -= date_duration(val.as_long());
+ return *this;
case AMOUNT:
- cast(AMOUNT);
- *((amount_t *) data) -= *((amount_t *) value.data);
- break;
- case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) -= *((balance_t *) value.data);
- break;
- case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
- break;
+ as_datetime_lval() -= date_duration(val.as_amount().to_long());
+ return *this;
default:
- assert(0);
break;
}
break;
- case DATETIME:
- switch (value.type) {
+ case INTEGER:
+ switch (val.type()) {
case INTEGER:
- *((datetime_t *) data) -= *((long *) value.data);
- break;
- case DATETIME: {
- long val = *((datetime_t *) data) - *((datetime_t *) value.data);
- cast(INTEGER);
- *((long *) data) = val;
- break;
- }
+ as_long_lval() -= val.as_long();
+ return *this;
case AMOUNT:
- *((datetime_t *) data) -= long(*((amount_t *) value.data));
- break;
+ in_place_cast(AMOUNT);
+ as_amount_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
case BALANCE:
- *((datetime_t *) data) -= long(*((balance_t *) value.data));
- break;
+ in_place_cast(BALANCE);
+ as_balance_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
case BALANCE_PAIR:
- *((datetime_t *) data) -= long(*((balance_pair_t *) value.data));
- break;
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
default:
- assert(0);
break;
}
break;
case AMOUNT:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- if (*((long *) value.data) &&
- ((amount_t *) data)->commodity()) {
- cast(BALANCE);
- return *this -= value;
+ if (as_amount().has_commodity()) {
+ in_place_cast(BALANCE);
+ *this -= val;
+ in_place_simplify();
+ return *this;
+ } else {
+ as_amount_lval() -= val.as_long();
+ in_place_simplify();
+ return *this;
}
- *((amount_t *) data) -= *((long *) value.data);
break;
case AMOUNT:
- if (((amount_t *) data)->commodity() !=
- ((amount_t *) value.data)->commodity()) {
- cast(BALANCE);
- return *this -= value;
+ if (as_amount().commodity() != val.as_amount().commodity()) {
+ in_place_cast(BALANCE);
+ *this -= val;
+ in_place_simplify();
+ return *this;
+ } else {
+ as_amount_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
}
- *((amount_t *) data) -= *((amount_t *) value.data);
break;
case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) -= *((balance_t *) value.data);
- break;
+ in_place_cast(BALANCE);
+ as_balance_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
- break;
-
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
default:
- assert(0);
break;
}
break;
case BALANCE:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_t *) data) -= *((long *) value.data);
- break;
+ as_balance_lval() -= val.to_amount();
+ in_place_simplify();
+ return *this;
case AMOUNT:
- *((balance_t *) data) -= *((amount_t *) value.data);
- break;
+ as_balance_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
case BALANCE:
- *((balance_t *) data) -= *((balance_t *) value.data);
- break;
+ as_balance_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
- break;
+ in_place_cast(BALANCE_PAIR);
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
default:
- assert(0);
break;
}
break;
case BALANCE_PAIR:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_pair_t *) data) -= *((long *) value.data);
- break;
+ as_balance_pair_lval() -= val.to_amount();
+ in_place_simplify();
+ return *this;
case AMOUNT:
- *((balance_pair_t *) data) -= *((amount_t *) value.data);
- break;
+ as_balance_pair_lval() -= val.as_amount();
+ in_place_simplify();
+ return *this;
case BALANCE:
- *((balance_pair_t *) data) -= *((balance_t *) value.data);
- break;
+ as_balance_pair_lval() -= val.as_balance();
+ in_place_simplify();
+ return *this;
case BALANCE_PAIR:
- *((balance_pair_t *) data) -= *((balance_pair_t *) value.data);
- break;
+ as_balance_pair_lval() -= val.as_balance_pair();
+ in_place_simplify();
+ return *this;
default:
- assert(0);
break;
}
break;
default:
- assert(0);
break;
}
- simplify();
+ throw_(value_error, "Cannot subtract " << label() << " from " << val.label());
return *this;
}
-value_t& value_t::operator*=(const value_t& value)
+value_t& value_t::operator*=(const value_t& val)
{
- if (value.type == BOOLEAN)
- throw new value_error("Cannot multiply a boolean by a value");
- else if (value.type == DATETIME)
- throw new value_error("Cannot multiply a date/time by a value");
-
- if (value.realzero()) {
- *this = 0L;
+ if (is_string()) {
+ string temp;
+ long count = val.to_long();
+ for (long i = 0; i < count; i++)
+ temp += as_string();
+ set_string(temp);
return *this;
}
+ else if (is_sequence()) {
+ value_t temp;
+ long count = val.to_long();
+ for (long i = 0; i < count; i++)
+ temp += as_sequence();
+ return *this = temp;
+ }
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot multiply a value by a boolean");
-
+ switch (type()) {
case INTEGER:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((long *) data) *= *((long *) value.data);
- break;
+ as_long_lval() *= val.as_long();
+ return *this;
case AMOUNT:
- cast(AMOUNT);
- *((amount_t *) data) *= *((amount_t *) value.data);
- break;
- case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) *= *((balance_t *) value.data);
- break;
- case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
- break;
+ set_amount(val.as_amount() * as_long());
+ return *this;
default:
- assert(0);
break;
}
break;
case AMOUNT:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((amount_t *) data) *= *((long *) value.data);
+ as_amount_lval() *= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (as_amount().commodity() == val.as_amount().commodity() ||
+ ! val.as_amount().has_commodity()) {
+ as_amount_lval() *= val.as_amount();
+ return *this;
+ }
break;
+ default:
+ break;
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() *= val.as_long();
+ return *this;
case AMOUNT:
- *((amount_t *) data) *= *((amount_t *) value.data);
+ if (! val.as_amount().has_commodity()) {
+ as_balance_lval() *= val.as_amount();
+ return *this;
+ }
break;
- case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) *= *((balance_t *) value.data);
+ default:
break;
- case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
+ }
+ break;
+
+ case BALANCE_PAIR:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_pair_lval() *= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (! val.as_amount().has_commodity()) {
+ as_balance_pair_lval() *= val.as_amount();
+ return *this;
+ }
break;
default:
- assert(0);
break;
}
break;
- case BALANCE:
- switch (value.type) {
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot multiply " << label() << " with " << val.label());
+
+ return *this;
+}
+
+value_t& value_t::operator/=(const value_t& val)
+{
+ switch (type()) {
+ case INTEGER:
+ switch (val.type()) {
case INTEGER:
- *((balance_t *) data) *= *((long *) value.data);
+ as_long_lval() /= val.as_long();
+ return *this;
+ case AMOUNT:
+ set_amount(val.as_amount() / as_long());
+ return *this;
+ default:
break;
+ }
+ break;
+
+ case AMOUNT:
+ switch (val.type()) {
+ case INTEGER:
+ as_amount_lval() /= val.as_long();
+ return *this;
+
case AMOUNT:
- *((balance_t *) data) *= *((amount_t *) value.data);
+ if (as_amount().commodity() == val.as_amount().commodity() ||
+ ! val.as_amount().has_commodity()) {
+ as_amount_lval() /= val.as_amount();
+ return *this;
+ }
break;
- case BALANCE:
- *((balance_t *) data) *= *((balance_t *) value.data);
+ default:
break;
- case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
+ }
+ break;
+
+ case BALANCE:
+ switch (val.type()) {
+ case INTEGER:
+ as_balance_lval() /= val.as_long();
+ return *this;
+ case AMOUNT:
+ if (! val.as_amount().has_commodity()) {
+ as_balance_lval() /= val.as_amount();
+ return *this;
+ }
break;
default:
- assert(0);
break;
}
break;
case BALANCE_PAIR:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_pair_t *) data) *= *((long *) value.data);
- break;
+ as_balance_pair_lval() /= val.as_long();
+ return *this;
case AMOUNT:
- *((balance_pair_t *) data) *= *((amount_t *) value.data);
- break;
- case BALANCE:
- *((balance_pair_t *) data) *= *((balance_t *) value.data);
- break;
- case BALANCE_PAIR:
- *((balance_pair_t *) data) *= *((balance_pair_t *) value.data);
+ if (! val.as_amount().has_commodity()) {
+ as_balance_pair_lval() /= val.as_amount();
+ return *this;
+ }
break;
default:
- assert(0);
break;
}
break;
default:
- assert(0);
break;
}
+
+ throw_(value_error, "Cannot divide " << label() << " by " << val.label());
+
return *this;
}
-value_t& value_t::operator/=(const value_t& value)
-{
- if (value.type == BOOLEAN)
- throw new value_error("Cannot divide a boolean by a value");
- else if (value.type == DATETIME)
- throw new value_error("Cannot divide a date/time by a value");
- switch (type) {
+bool value_t::operator==(const value_t& val) const
+{
+ switch (type()) {
case BOOLEAN:
- throw new value_error("Cannot divide a value by a boolean");
+ if (val.is_boolean())
+ return as_boolean() == val.as_boolean();
+ break;
+
+ case DATETIME:
+ if (val.is_datetime())
+ return as_datetime() == val.as_datetime();
+ break;
case INTEGER:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((long *) data) /= *((long *) value.data);
- break;
+ return as_long() == val.as_long();
case AMOUNT:
- cast(AMOUNT);
- *((amount_t *) data) /= *((amount_t *) value.data);
- break;
+ return val.as_amount() == to_amount();
case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) /= *((balance_t *) value.data);
- break;
+ return val.as_balance() == to_amount();
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
- break;
+ return val.as_balance_pair() == to_amount();
default:
- assert(0);
break;
}
break;
case AMOUNT:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((amount_t *) data) /= *((long *) value.data);
- break;
+ return as_amount() == val.as_long();
case AMOUNT:
- *((amount_t *) data) /= *((amount_t *) value.data);
- break;
+ return as_amount() == val.as_amount();
case BALANCE:
- cast(BALANCE);
- *((balance_t *) data) /= *((balance_t *) value.data);
- break;
+ return val.as_balance() == as_amount();
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
- break;
+ return val.as_balance_pair() == as_amount();
default:
- assert(0);
break;
}
break;
case BALANCE:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_t *) data) /= *((long *) value.data);
- break;
+ return as_balance() == val.to_amount();
case AMOUNT:
- *((balance_t *) data) /= *((amount_t *) value.data);
- break;
+ return as_balance() == val.as_amount();
case BALANCE:
- *((balance_t *) data) /= *((balance_t *) value.data);
- break;
+ return as_balance() == val.as_balance();
case BALANCE_PAIR:
- cast(BALANCE_PAIR);
- *((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
- break;
+ return val.as_balance_pair() == as_balance();
default:
- assert(0);
break;
}
break;
case BALANCE_PAIR:
- switch (value.type) {
+ switch (val.type()) {
case INTEGER:
- *((balance_pair_t *) data) /= *((long *) value.data);
- break;
+ return as_balance_pair() == val.to_amount();
case AMOUNT:
- *((balance_pair_t *) data) /= *((amount_t *) value.data);
- break;
+ return as_balance_pair() == val.as_amount();
case BALANCE:
- *((balance_pair_t *) data) /= *((balance_t *) value.data);
- break;
+ return as_balance_pair() == val.as_balance();
case BALANCE_PAIR:
- *((balance_pair_t *) data) /= *((balance_pair_t *) value.data);
- break;
+ return as_balance_pair() == val.as_balance_pair();
default:
- assert(0);
break;
}
break;
+ case STRING:
+ if (val.is_string())
+ return as_string() == val.as_string();
+ break;
+
+ case SEQUENCE:
+ if (val.is_sequence())
+ return as_sequence() == val.as_sequence();
+ break;
+
default:
- assert(0);
break;
}
- return *this;
-}
-#define DEF_VALUE_CMP_OP(OP) \
-bool value_t::operator OP(const value_t& value) \
-{ \
- switch (type) { \
- case BOOLEAN: \
- switch (value.type) { \
- case BOOLEAN: \
- return *((bool *) data) OP *((bool *) value.data); \
- \
- case INTEGER: \
- return *((bool *) data) OP bool(*((long *) value.data)); \
- \
- case DATETIME: \
- return *((bool *) data) OP bool(*((datetime_t *) value.data)); \
- \
- case AMOUNT: \
- return *((bool *) data) OP bool(*((amount_t *) value.data)); \
- \
- case BALANCE: \
- return *((bool *) data) OP bool(*((balance_t *) value.data)); \
- \
- case BALANCE_PAIR: \
- return *((bool *) data) OP bool(*((balance_pair_t *) value.data)); \
- \
- default: \
- assert(0); \
- break; \
- } \
- break; \
- \
- case INTEGER: \
- switch (value.type) { \
- case BOOLEAN: \
- return (*((long *) data) OP \
- ((long) *((bool *) value.data))); \
- \
- case INTEGER: \
- return (*((long *) data) OP *((long *) value.data)); \
- \
- case DATETIME: \
- return (*((long *) data) OP \
- ((long) *((datetime_t *) value.data))); \
- \
- case AMOUNT: \
- return (amount_t(*((long *) data)) OP \
- *((amount_t *) value.data)); \
- \
- case BALANCE: \
- return (balance_t(*((long *) data)) OP \
- *((balance_t *) value.data)); \
- \
- case BALANCE_PAIR: \
- return (balance_pair_t(*((long *) data)) OP \
- *((balance_pair_t *) value.data)); \
- \
- default: \
- assert(0); \
- break; \
- } \
- break; \
- \
- case DATETIME: \
- switch (value.type) { \
- case BOOLEAN: \
- throw new value_error("Cannot compare a date/time to a boolean"); \
- \
- case INTEGER: \
- return (*((datetime_t *) data) OP \
- datetime_t(*((long *) value.data))); \
- \
- case DATETIME: \
- return (*((datetime_t *) data) OP \
- *((datetime_t *) value.data)); \
- \
- case AMOUNT: \
- throw new value_error("Cannot compare a date/time to an amount"); \
- \
- case BALANCE: \
- throw new value_error("Cannot compare a date/time to a balance"); \
- \
- case BALANCE_PAIR: \
- throw new value_error("Cannot compare a date/time to a balance pair"); \
- \
- default: \
- assert(0); \
- break; \
- } \
- break; \
- \
- case AMOUNT: \
- switch (value.type) { \
- case BOOLEAN: \
- throw new value_error("Cannot compare an amount to a boolean"); \
- \
- case INTEGER: \
- return (*((amount_t *) data) OP \
- amount_t(*((long *) value.data))); \
- \
- case DATETIME: \
- throw new value_error("Cannot compare an amount to a date/time"); \
- \
- case AMOUNT: \
- return *((amount_t *) data) OP *((amount_t *) value.data); \
- \
- case BALANCE: \
- return (balance_t(*((amount_t *) data)) OP \
- *((balance_t *) value.data)); \
- \
- case BALANCE_PAIR: \
- return (balance_t(*((amount_t *) data)) OP \
- *((balance_pair_t *) value.data)); \
- \
- default: \
- assert(0); \
- break; \
- } \
- break; \
- \
- case BALANCE: \
- switch (value.type) { \
- case BOOLEAN: \
- throw new value_error("Cannot compare a balance to a boolean"); \
- \
- case INTEGER: \
- return *((balance_t *) data) OP *((long *) value.data); \
- \
- case DATETIME: \
- throw new value_error("Cannot compare a balance to a date/time"); \
- \
- case AMOUNT: \
- return *((balance_t *) data) OP *((amount_t *) value.data); \
- \
- case BALANCE: \
- return *((balance_t *) data) OP *((balance_t *) value.data); \
- \
- case BALANCE_PAIR: \
- return (*((balance_t *) data) OP \
- ((balance_pair_t *) value.data)->quantity); \
- \
- default: \
- assert(0); \
- break; \
- } \
- break; \
- \
- case BALANCE_PAIR: \
- switch (value.type) { \
- case BOOLEAN: \
- throw new value_error("Cannot compare a balance pair to a boolean"); \
- \
- case INTEGER: \
- return (((balance_pair_t *) data)->quantity OP \
- *((long *) value.data)); \
- \
- case DATETIME: \
- throw new value_error("Cannot compare a balance pair to a date/time"); \
- \
- case AMOUNT: \
- return (((balance_pair_t *) data)->quantity OP \
- *((amount_t *) value.data)); \
- \
- case BALANCE: \
- return (((balance_pair_t *) data)->quantity OP \
- *((balance_t *) value.data)); \
- \
- case BALANCE_PAIR: \
- return (*((balance_pair_t *) data) OP \
- *((balance_pair_t *) value.data)); \
- \
- default: \
- assert(0); \
- break; \
- } \
- break; \
- \
- default: \
- assert(0); \
- break; \
- } \
- return *this; \
-}
+ throw_(value_error, "Cannot compare " << label() << " to " << val.label());
-DEF_VALUE_CMP_OP(==)
-DEF_VALUE_CMP_OP(<)
-DEF_VALUE_CMP_OP(<=)
-DEF_VALUE_CMP_OP(>)
-DEF_VALUE_CMP_OP(>=)
+ return *this;
+}
-template <>
-value_t::operator long() const
+bool value_t::operator<(const value_t& val) const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot convert a boolean to an integer");
- case INTEGER:
- return *((long *) data);
+ switch (type()) {
case DATETIME:
- return *((datetime_t *) data);
- case AMOUNT:
- return *((amount_t *) data);
- case BALANCE:
- throw new value_error("Cannot convert a balance to an integer");
- case BALANCE_PAIR:
- throw new value_error("Cannot convert a balance pair to an integer");
-
- default:
- assert(0);
+ if (val.is_datetime())
+ return as_datetime() < val.as_datetime();
break;
- }
- assert(0);
- return 0;
-}
-template <>
-value_t::operator datetime_t() const
-{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot convert a boolean to a date/time");
case INTEGER:
- return *((long *) data);
- case DATETIME:
- return *((datetime_t *) data);
+ switch (val.type()) {
+ case INTEGER:
+ return as_long() < val.as_long();
+ case AMOUNT:
+ return val.as_amount() < as_long();
+ default:
+ break;
+ }
+ break;
+
case AMOUNT:
- throw new value_error("Cannot convert an amount to a date/time");
- case BALANCE:
- throw new value_error("Cannot convert a balance to a date/time");
- case BALANCE_PAIR:
- throw new value_error("Cannot convert a balance pair to a date/time");
+ switch (val.type()) {
+ case INTEGER:
+ return as_amount() < val.as_long();
+ case AMOUNT:
+ return as_amount() < val.as_amount();
+ default:
+ break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() < val.as_string();
+ break;
default:
- assert(0);
break;
}
- assert(0);
- return 0;
+
+ throw_(value_error, "Cannot compare " << label() << " to " << val.label());
+
+ return *this;
}
-template <>
-value_t::operator double() const
+#if 0
+bool value_t::operator>(const value_t& val) const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot convert a boolean to a double");
- case INTEGER:
- return *((long *) data);
+ switch (type()) {
case DATETIME:
- throw new value_error("Cannot convert a date/time to a double");
- case AMOUNT:
- return *((amount_t *) data);
- case BALANCE:
- throw new value_error("Cannot convert a balance to a double");
- case BALANCE_PAIR:
- throw new value_error("Cannot convert a balance pair to a double");
-
- default:
- assert(0);
+ if (val.is_datetime())
+ return as_datetime() > val.as_datetime();
break;
- }
- assert(0);
- return 0;
-}
-void value_t::cast(type_t cast_type)
-{
- switch (type) {
- case BOOLEAN:
- switch (cast_type) {
- case BOOLEAN:
- break;
+ case INTEGER:
+ switch (val.type()) {
case INTEGER:
- throw new value_error("Cannot convert a boolean to an integer");
- case DATETIME:
- throw new value_error("Cannot convert a boolean to a date/time");
+ return as_long() > val.as_long();
case AMOUNT:
- throw new value_error("Cannot convert a boolean to an amount");
- case BALANCE:
- throw new value_error("Cannot convert a boolean to a balance");
- case BALANCE_PAIR:
- throw new value_error("Cannot convert a boolean to a balance pair");
-
+ return val.as_amount() > as_long();
default:
- assert(0);
break;
}
break;
- case INTEGER:
- switch (cast_type) {
- case BOOLEAN:
- *((bool *) data) = *((long *) data);
- break;
+ case AMOUNT:
+ switch (val.type()) {
case INTEGER:
- break;
- case DATETIME:
- *((datetime_t *) data) = datetime_t(*((long *) data));
- break;
+ return as_amount() > val.as_long();
case AMOUNT:
- new((amount_t *)data) amount_t(*((long *) data));
- break;
- case BALANCE:
- new((balance_t *)data) balance_t(amount_t(*((long *) data)));
- break;
- case BALANCE_PAIR:
- new((balance_pair_t *)data) balance_pair_t(amount_t(*((long *) data)));
+ return as_amount() > val.as_amount();
+ default:
break;
+ }
+ break;
+
+ case STRING:
+ if (val.is_string())
+ return as_string() > val.as_string();
+ break;
+
+ default:
+ break;
+ }
+
+ throw_(value_error,
+ "Cannot compare " << label() << " to " << val.label());
+
+ return *this;
+}
+#endif
+
+void value_t::in_place_cast(type_t cast_type)
+{
+ if (type() == cast_type)
+ return;
+ if (cast_type == BOOLEAN) {
+ set_boolean(bool(*this));
+ return;
+ }
+ else if (cast_type == SEQUENCE) {
+ sequence_t temp;
+ if (! is_null())
+ temp.push_back(*this);
+ set_sequence(temp);
+ return;
+ }
+
+ switch (type()) {
+ case BOOLEAN:
+ switch (cast_type) {
+ case STRING:
+ set_string(as_boolean() ? "true" : "false");
+ return;
default:
- assert(0);
break;
}
break;
- case DATETIME:
+ case INTEGER:
switch (cast_type) {
- case BOOLEAN:
- *((bool *) data) = *((datetime_t *) data);
- break;
- case INTEGER:
- *((long *) data) = *((datetime_t *) data);
- break;
- case DATETIME:
- break;
case AMOUNT:
- throw new value_error("Cannot convert a date/time to an amount");
+ set_amount(as_long());
+ return;
case BALANCE:
- throw new value_error("Cannot convert a date/time to a balance");
+ set_balance(to_amount());
+ return;
case BALANCE_PAIR:
- throw new value_error("Cannot convert a date/time to a balance pair");
-
+ set_balance_pair(to_amount());
+ return;
+ case STRING:
+ set_string(lexical_cast<string>(as_long()));
+ return;
default:
- assert(0);
break;
}
break;
case AMOUNT:
switch (cast_type) {
- case BOOLEAN: {
- bool temp = *((amount_t *) data);
- destroy();
- *((bool *)data) = temp;
- break;
- }
- case INTEGER: {
- long temp = *((amount_t *) data);
- destroy();
- *((long *)data) = temp;
- break;
- }
- case DATETIME:
- throw new value_error("Cannot convert an amount to a date/time");
- case AMOUNT:
- break;
- case BALANCE: {
- amount_t temp = *((amount_t *) data);
- destroy();
- new((balance_t *)data) balance_t(temp);
- break;
- }
- case BALANCE_PAIR: {
- amount_t temp = *((amount_t *) data);
- destroy();
- new((balance_pair_t *)data) balance_pair_t(temp);
- break;
- }
-
+ case INTEGER:
+ set_long(as_amount().to_long());
+ return;
+ case BALANCE:
+ set_balance(as_amount());
+ return;
+ case BALANCE_PAIR:
+ set_balance_pair(as_amount());
+ return;
+ case STRING:
+ set_string(as_amount().to_string());
+ return;
default:
- assert(0);
break;
}
break;
case BALANCE:
switch (cast_type) {
- case BOOLEAN: {
- bool temp = *((balance_t *) data);
- destroy();
- *((bool *)data) = temp;
- break;
- }
- case INTEGER:
- throw new value_error("Cannot convert a balance to an integer");
- case DATETIME:
- throw new value_error("Cannot convert a balance to a date/time");
-
case AMOUNT: {
- balance_t * temp = (balance_t *) data;
- if (temp->amounts.size() == 1) {
- amount_t amt = (*temp->amounts.begin()).second;
- destroy();
- new((amount_t *)data) amount_t(amt);
+ const balance_t& temp(as_balance());
+ if (temp.amounts.size() == 1) {
+ set_amount((*temp.amounts.begin()).second);
+ return;
}
- else if (temp->amounts.size() == 0) {
- new((amount_t *)data) amount_t();
+ else if (temp.amounts.size() == 0) {
+ set_amount(0L);
+ return;
}
else {
- throw new value_error("Cannot convert a balance with "
- "multiple commodities to an amount");
+ throw_(value_error, "Cannot convert " << label() <<
+ " with multiple commodities to " << label(cast_type));
}
break;
}
- case BALANCE:
- break;
- case BALANCE_PAIR: {
- balance_t temp = *((balance_t *) data);
- destroy();
- new((balance_pair_t *)data) balance_pair_t(temp);
- break;
- }
-
+ case BALANCE_PAIR:
+ set_balance_pair(as_balance());
+ return;
default:
- assert(0);
break;
}
break;
case BALANCE_PAIR:
switch (cast_type) {
- case BOOLEAN: {
- bool temp = *((balance_pair_t *) data);
- destroy();
- *((bool *)data) = temp;
- break;
- }
- case INTEGER:
- throw new value_error("Cannot convert a balance pair to an integer");
- case DATETIME:
- throw new value_error("Cannot convert a balance pair to a date/time");
-
case AMOUNT: {
- balance_t * temp = &((balance_pair_t *) data)->quantity;
- if (temp->amounts.size() == 1) {
- amount_t amt = (*temp->amounts.begin()).second;
- destroy();
- new((amount_t *)data) amount_t(amt);
+ const balance_t& temp(as_balance_pair().quantity());
+ if (temp.amounts.size() == 1) {
+ set_amount((*temp.amounts.begin()).second);
+ return;
}
- else if (temp->amounts.size() == 0) {
- new((amount_t *)data) amount_t();
+ else if (temp.amounts.size() == 0) {
+ set_amount(0L);
+ return;
}
else {
- throw new value_error("Cannot convert a balance pair with "
- "multiple commodities to an amount");
+ throw_(value_error, "Cannot convert " << label() <<
+ " with multiple commodities to " << label(cast_type));
}
break;
}
- case BALANCE: {
- balance_t temp = ((balance_pair_t *) data)->quantity;
- destroy();
- new((balance_t *)data) balance_t(temp);
+ case BALANCE:
+ set_balance(as_balance_pair().quantity());
+ return;
+ default:
break;
}
- case BALANCE_PAIR:
- break;
+ break;
+ case STRING:
+ switch (cast_type) {
+ case INTEGER: {
+ if (all(as_string(), is_digit())) {
+ set_long(lexical_cast<long>(as_string()));
+ return;
+ } else {
+ throw_(value_error,
+ "Cannot convert string '" << *this << "' to an integer");
+ }
+ break;
+ }
+ case AMOUNT:
+ set_amount(amount_t(as_string()));
+ return;
default:
- assert(0);
break;
}
break;
default:
- assert(0);
break;
}
- type = cast_type;
+
+ throw_(value_error,
+ "Cannot convert " << label() << " to " << label(cast_type));
}
-void value_t::negate()
+void value_t::in_place_negate()
{
- switch (type) {
+ switch (type()) {
case BOOLEAN:
- *((bool *) data) = ! *((bool *) data);
- break;
+ set_boolean(! as_boolean());
+ return;
case INTEGER:
- *((long *) data) = - *((long *) data);
- break;
- case DATETIME:
- throw new value_error("Cannot negate a date/time");
+ set_long(- as_long());
+ return;
case AMOUNT:
- ((amount_t *) data)->negate();
- break;
+ as_amount_lval().in_place_negate();
+ return;
case BALANCE:
- ((balance_t *) data)->negate();
- break;
+ as_balance_lval().in_place_negate();
+ return;
case BALANCE_PAIR:
- ((balance_pair_t *) data)->negate();
- break;
-
+ as_balance_pair_lval().in_place_negate();
+ return;
default:
- assert(0);
break;
}
+
+ throw_(value_error, "Cannot negate " << label());
}
-void value_t::abs()
+bool value_t::is_realzero() const
{
- switch (type) {
+ switch (type()) {
case BOOLEAN:
- break;
+ return ! as_boolean();
case INTEGER:
- if (*((long *) data) < 0)
- *((long *) data) = - *((long *) data);
- break;
+ return as_long() == 0;
case DATETIME:
- break;
+ return ! is_valid_moment(as_datetime());
case AMOUNT:
- ((amount_t *) data)->abs();
- break;
+ return as_amount().is_realzero();
case BALANCE:
- ((balance_t *) data)->abs();
- break;
+ return as_balance().is_realzero();
case BALANCE_PAIR:
- ((balance_pair_t *) data)->abs();
- break;
+ return as_balance_pair().is_realzero();
+ case STRING:
+ return as_string().empty();
+ case SEQUENCE:
+ return as_sequence().empty();
+
+ case POINTER:
+ return as_any_pointer().empty();
default:
- assert(0);
+ assert(false);
break;
}
+ assert(false);
+ return true;
}
-value_t value_t::value(const datetime_t& moment) const
+value_t value_t::value(const optional<moment_t>& moment) const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot find the value of a boolean");
- case DATETIME:
- throw new value_error("Cannot find the value of a date/time");
+ switch (type()) {
case INTEGER:
return *this;
- case AMOUNT:
- return ((amount_t *) data)->value(moment);
- case BALANCE:
- return ((balance_t *) data)->value(moment);
- case BALANCE_PAIR:
- return ((balance_pair_t *) data)->quantity.value(moment);
+
+ case AMOUNT: {
+ if (optional<amount_t> val = as_amount().value(moment))
+ return *val;
+ return false;
+ }
+ case BALANCE: {
+ if (optional<balance_t> bal = as_balance().value(moment))
+ return *bal;
+ return false;
}
+ case BALANCE_PAIR: {
+ if (optional<balance_t> bal_pair =
+ as_balance_pair().quantity().value(moment))
+ return *bal_pair;
+ return false;
+ }
+
+ default:
+ break;
+ }
+
+ throw_(value_error, "Cannot find the value of " << label());
+ return value_t();
}
-void value_t::reduce()
+void value_t::in_place_reduce()
{
- switch (type) {
- case BOOLEAN:
- case DATETIME:
+ switch (type()) {
case INTEGER:
- break;
+ return;
case AMOUNT:
- ((amount_t *) data)->reduce();
- break;
+ as_amount_lval().in_place_reduce();
+ return;
case BALANCE:
- ((balance_t *) data)->reduce();
- break;
+ as_balance_lval().in_place_reduce();
+ return;
case BALANCE_PAIR:
- ((balance_pair_t *) data)->reduce();
+ as_balance_pair_lval().in_place_reduce();
+ return;
+ default:
break;
}
+
+ throw_(value_error, "Cannot reduce " << label());
}
-void value_t::round()
+value_t value_t::round() const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot round a boolean");
- case DATETIME:
- throw new value_error("Cannot round a date/time");
+ switch (type()) {
case INTEGER:
- break;
+ return *this;
case AMOUNT:
- *((amount_t *) data) = ((amount_t *) data)->round();
- break;
- case BALANCE:
- ((balance_t *) data)->round();
- break;
- case BALANCE_PAIR:
- ((balance_pair_t *) data)->round();
+ return as_amount().round();
+ default:
break;
}
+
+ throw_(value_error, "Cannot round " << label());
+ return value_t();
}
value_t value_t::unround() const
{
- value_t temp;
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot un-round a boolean");
- case DATETIME:
- throw new value_error("Cannot un-round a date/time");
+ switch (type()) {
case INTEGER:
- break;
+ return *this;
case AMOUNT:
- temp = ((amount_t *) data)->unround();
- break;
- case BALANCE:
- temp = ((balance_t *) data)->unround();
- break;
- case BALANCE_PAIR:
- temp = ((balance_pair_t *) data)->unround();
+ return as_amount().unround();
+ default:
break;
}
- return temp;
+
+ throw_(value_error, "Cannot unround " << label());
+ return value_t();
}
-value_t value_t::price() const
+value_t value_t::annotated_price() const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot find the price of a boolean");
- case INTEGER:
- return *this;
- case DATETIME:
- throw new value_error("Cannot find the price of a date/time");
+ switch (type()) {
+ case AMOUNT: {
+ optional<amount_t> temp = as_amount().annotation_details().price;
+ if (! temp)
+ return false;
+ return *temp;
+ }
- case AMOUNT:
- return ((amount_t *) data)->price();
+ default:
+ break;
+ }
- case BALANCE:
- return ((balance_t *) data)->price();
+ throw_(value_error, "Cannot find the annotated price of " << label());
+ return value_t();
+}
- case BALANCE_PAIR:
- return ((balance_pair_t *) data)->quantity.price();
+value_t value_t::annotated_date() const
+{
+ switch (type()) {
+ case DATETIME:
+ return *this;
+
+ case AMOUNT: {
+ optional<moment_t> temp = as_amount().annotation_details().date;
+ if (! temp)
+ return false;
+ return *temp;
+ }
default:
- assert(0);
break;
}
- assert(0);
+
+ throw_(value_error, "Cannot find the annotated date of " << label());
return value_t();
}
-value_t value_t::date() const
+value_t value_t::annotated_tag() const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot find the date of a boolean");
- case INTEGER:
- return datetime_t();
+ switch (type()) {
case DATETIME:
return *this;
- case AMOUNT:
- return datetime_t(((amount_t *) data)->date());
-
- case BALANCE:
- return datetime_t(((balance_t *) data)->date());
-
- case BALANCE_PAIR:
- return datetime_t(((balance_pair_t *) data)->quantity.date());
+ case AMOUNT: {
+ optional<string> temp = as_amount().annotation_details().tag;
+ if (! temp)
+ return false;
+ return value_t(*temp, true);
+ }
default:
- assert(0);
break;
}
- assert(0);
+
+ throw_(value_error, "Cannot find the annotated tag of " << label());
return value_t();
}
@@ -1250,445 +1288,147 @@ value_t value_t::strip_annotations(const bool keep_price,
const bool keep_date,
const bool keep_tag) const
{
- switch (type) {
+ switch (type()) {
+ case VOID:
case BOOLEAN:
case INTEGER:
case DATETIME:
+ case STRING:
+ case POINTER:
return *this;
+ case SEQUENCE: {
+ sequence_t temp;
+ foreach (const value_t& value, as_sequence())
+ temp.push_back(value.strip_annotations(keep_price, keep_date, keep_tag));
+ return temp;
+ }
+
case AMOUNT:
- return ((amount_t *) data)->strip_annotations
- (keep_price, keep_date, keep_tag);
+ return as_amount().strip_annotations(keep_price, keep_date, keep_tag);
case BALANCE:
- return ((balance_t *) data)->strip_annotations
- (keep_price, keep_date, keep_tag);
+ return as_balance().strip_annotations(keep_price, keep_date, keep_tag);
case BALANCE_PAIR:
- return ((balance_pair_t *) data)->quantity.strip_annotations
- (keep_price, keep_date, keep_tag);
+ return as_balance_pair().quantity().strip_annotations(keep_price, keep_date,
+ keep_tag);
default:
- assert(0);
+ assert(false);
break;
}
- assert(0);
+ assert(false);
return value_t();
}
value_t value_t::cost() const
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot find the cost of a boolean");
+ switch (type()) {
case INTEGER:
case AMOUNT:
case BALANCE:
return *this;
- case DATETIME:
- throw new value_error("Cannot find the cost of a date/time");
case BALANCE_PAIR:
- assert(((balance_pair_t *) data)->cost);
- if (((balance_pair_t *) data)->cost)
- return *(((balance_pair_t *) data)->cost);
+ assert(as_balance_pair().cost);
+ if (as_balance_pair().cost)
+ return *(as_balance_pair().cost);
else
- return ((balance_pair_t *) data)->quantity;
+ return as_balance_pair().quantity();
default:
- assert(0);
break;
}
- assert(0);
+
+ throw_(value_error, "Cannot find the cost of " << label());
return value_t();
}
-value_t& value_t::add(const amount_t& amount, const amount_t * cost)
+value_t& value_t::add(const amount_t& amount, const optional<amount_t>& tcost)
{
- switch (type) {
- case BOOLEAN:
- throw new value_error("Cannot add an amount to a boolean");
- case DATETIME:
- throw new value_error("Cannot add an amount to a date/time");
+ switch (type()) {
case INTEGER:
case AMOUNT:
- if (cost) {
- cast(BALANCE_PAIR);
- return add(amount, cost);
+ if (tcost) {
+ in_place_cast(BALANCE_PAIR);
+ return add(amount, tcost);
}
- else if ((type == AMOUNT &&
- ((amount_t *) data)->commodity() != amount.commodity()) ||
- (type != AMOUNT && amount.commodity())) {
- cast(BALANCE);
- return add(amount, cost);
+ else if ((is_amount() &&
+ as_amount().commodity() != amount.commodity()) ||
+ (! is_amount() && amount.commodity())) {
+ in_place_cast(BALANCE);
+ return add(amount, tcost);
}
- else if (type != AMOUNT) {
- cast(AMOUNT);
+ else if (! is_amount()) {
+ in_place_cast(AMOUNT);
}
- *((amount_t *) data) += amount;
+ *this += amount;
break;
case BALANCE:
- if (cost) {
- cast(BALANCE_PAIR);
- return add(amount, cost);
+ if (tcost) {
+ in_place_cast(BALANCE_PAIR);
+ return add(amount, tcost);
}
- *((balance_t *) data) += amount;
+ *this += amount;
break;
case BALANCE_PAIR:
- ((balance_pair_t *) data)->add(amount, cost);
+ as_balance_pair_lval().add(amount, tcost);
break;
default:
- assert(0);
break;
}
+ throw_(value_error, "Cannot add an amount to " << label());
return *this;
}
-value_context::value_context(const value_t& _bal,
- const std::string& desc) throw()
- : bal(new value_t(_bal)), error_context(desc) {}
-
-value_context::~value_context() throw()
-{
- delete bal;
-}
-
-void value_context::describe(std::ostream& out) const throw()
+void value_t::print(std::ostream& out, const int first_width,
+ const int latter_width) const
{
- if (! desc.empty())
- out << desc << std::endl;
-
- ledger::balance_t * ptr = NULL;
-
- out << std::right;
- out.width(20);
-
- switch (bal->type) {
- case ledger::value_t::BOOLEAN:
- out << (*((bool *) bal->data) ? "true" : "false");
- break;
- case ledger::value_t::INTEGER:
- out << *((long *) bal->data);
+ switch (type()) {
+ case VOID:
+ out << "NULL";
break;
- case ledger::value_t::DATETIME:
- out << *((datetime_t *) bal->data);
- break;
- case ledger::value_t::AMOUNT:
- out << *((ledger::amount_t *) bal->data);
- break;
- case ledger::value_t::BALANCE:
- ptr = (ledger::balance_t *) bal->data;
- // fall through...
- case ledger::value_t::BALANCE_PAIR:
- if (! ptr)
- ptr = &((ledger::balance_pair_t *) bal->data)->quantity;
-
- ptr->write(out, 20);
- break;
- default:
- assert(0);
+ case BOOLEAN:
+ case DATETIME:
+ case INTEGER:
+ case AMOUNT:
+ case STRING:
+ case POINTER:
+ // jww (2007-05-14): I need a version of this print just for XPath
+ // expression, since amounts and strings need to be output with
+ // special syntax.
+ out << *this;
+ break;
+
+ case SEQUENCE: {
+ out << '(';
+ bool first = true;
+ foreach (const value_t& value, as_sequence()) {
+ if (first)
+ first = false;
+ else
+ out << ", ";
+
+ value.print(out, first_width, latter_width);
+ }
+ out << ')';
break;
}
- out << std::endl;
-}
-
-} // namespace ledger
-
-#ifdef USE_BOOST_PYTHON
-
-#include <boost/python.hpp>
-
-using namespace boost::python;
-using namespace ledger;
-
-long balance_len(balance_t& bal);
-amount_t balance_getitem(balance_t& bal, int i);
-long balance_pair_len(balance_pair_t& bal_pair);
-amount_t balance_pair_getitem(balance_pair_t& bal_pair, int i);
-
-long value_len(value_t& value)
-{
- switch (value.type) {
- case value_t::BOOLEAN:
- case value_t::INTEGER:
- case value_t::DATETIME:
- case value_t::AMOUNT:
- return 1;
-
- case value_t::BALANCE:
- return balance_len(*((balance_t *) value.data));
-
- case value_t::BALANCE_PAIR:
- return balance_pair_len(*((balance_pair_t *) value.data));
- default:
- assert(0);
+ case BALANCE:
+ as_balance().print(out, first_width, latter_width);
+ break;
+ case BALANCE_PAIR:
+ as_balance_pair().print(out, first_width, latter_width);
break;
- }
- assert(0);
- return 0;
-}
-
-amount_t value_getitem(value_t& value, int i)
-{
- std::size_t len = value_len(value);
-
- if (abs(i) >= len) {
- PyErr_SetString(PyExc_IndexError, "Index out of range");
- throw_error_already_set();
- }
-
- switch (value.type) {
- case value_t::BOOLEAN:
- throw new value_error("Cannot cast a boolean to an amount");
-
- case value_t::INTEGER:
- return long(value);
-
- case value_t::DATETIME:
- throw new value_error("Cannot cast a date/time to an amount");
-
- case value_t::AMOUNT:
- return *((amount_t *) value.data);
-
- case value_t::BALANCE:
- return balance_getitem(*((balance_t *) value.data), i);
-
- case value_t::BALANCE_PAIR:
- return balance_pair_getitem(*((balance_pair_t *) value.data), i);
-
default:
- assert(0);
+ assert(false);
break;
}
- assert(0);
- return 0L;
-}
-
-double py_to_float(value_t& value)
-{
- return double(value);
}
-void export_value()
-{
- scope in_value = class_< value_t > ("Value")
- .def(init<value_t>())
- .def(init<balance_pair_t>())
- .def(init<balance_t>())
- .def(init<amount_t>())
- .def(init<std::string>())
- .def(init<double>())
- .def(init<long>())
- .def(init<datetime_t>())
-
- .def(self + self)
- .def(self + other<balance_pair_t>())
- .def(self + other<balance_t>())
- .def(self + other<amount_t>())
- .def(self + long())
- .def(self + double())
-
- .def(other<balance_pair_t>() + self)
- .def(other<balance_t>() + self)
- .def(other<amount_t>() + self)
- .def(long() + self)
- .def(double() + self)
-
- .def(self - self)
- .def(self - other<balance_pair_t>())
- .def(self - other<balance_t>())
- .def(self - other<amount_t>())
- .def(self - long())
- .def(self - double())
-
- .def(other<balance_pair_t>() - self)
- .def(other<balance_t>() - self)
- .def(other<amount_t>() - self)
- .def(long() - self)
- .def(double() - self)
-
- .def(self * self)
- .def(self * other<balance_pair_t>())
- .def(self * other<balance_t>())
- .def(self * other<amount_t>())
- .def(self * long())
- .def(self * double())
-
- .def(other<balance_pair_t>() * self)
- .def(other<balance_t>() * self)
- .def(other<amount_t>() * self)
- .def(long() * self)
- .def(double() * self)
-
- .def(self / self)
- .def(self / other<balance_pair_t>())
- .def(self / other<balance_t>())
- .def(self / other<amount_t>())
- .def(self / long())
- .def(self / double())
-
- .def(other<balance_pair_t>() / self)
- .def(other<balance_t>() / self)
- .def(other<amount_t>() / self)
- .def(long() / self)
- .def(double() / self)
-
- .def(- self)
-
- .def(self += self)
- .def(self += other<balance_pair_t>())
- .def(self += other<balance_t>())
- .def(self += other<amount_t>())
- .def(self += long())
- .def(self += double())
-
- .def(self -= self)
- .def(self -= other<balance_pair_t>())
- .def(self -= other<balance_t>())
- .def(self -= other<amount_t>())
- .def(self -= long())
- .def(self -= double())
-
- .def(self *= self)
- .def(self *= other<balance_pair_t>())
- .def(self *= other<balance_t>())
- .def(self *= other<amount_t>())
- .def(self *= long())
- .def(self *= double())
-
- .def(self /= self)
- .def(self /= other<balance_pair_t>())
- .def(self /= other<balance_t>())
- .def(self /= other<amount_t>())
- .def(self /= long())
- .def(self /= double())
-
- .def(self < self)
- .def(self < other<balance_pair_t>())
- .def(self < other<balance_t>())
- .def(self < other<amount_t>())
- .def(self < long())
- .def(self < other<datetime_t>())
- .def(self < double())
-
- .def(other<balance_pair_t>() < self)
- .def(other<balance_t>() < self)
- .def(other<amount_t>() < self)
- .def(long() < self)
- .def(other<datetime_t>() < self)
- .def(double() < self)
-
- .def(self <= self)
- .def(self <= other<balance_pair_t>())
- .def(self <= other<balance_t>())
- .def(self <= other<amount_t>())
- .def(self <= long())
- .def(self <= other<datetime_t>())
- .def(self <= double())
-
- .def(other<balance_pair_t>() <= self)
- .def(other<balance_t>() <= self)
- .def(other<amount_t>() <= self)
- .def(long() <= self)
- .def(other<datetime_t>() <= self)
- .def(double() <= self)
-
- .def(self > self)
- .def(self > other<balance_pair_t>())
- .def(self > other<balance_t>())
- .def(self > other<amount_t>())
- .def(self > long())
- .def(self > other<datetime_t>())
- .def(self > double())
-
- .def(other<balance_pair_t>() > self)
- .def(other<balance_t>() > self)
- .def(other<amount_t>() > self)
- .def(long() > self)
- .def(other<datetime_t>() > self)
- .def(double() > self)
-
- .def(self >= self)
- .def(self >= other<balance_pair_t>())
- .def(self >= other<balance_t>())
- .def(self >= other<amount_t>())
- .def(self >= long())
- .def(self >= other<datetime_t>())
- .def(self >= double())
-
- .def(other<balance_pair_t>() >= self)
- .def(other<balance_t>() >= self)
- .def(other<amount_t>() >= self)
- .def(long() >= self)
- .def(other<datetime_t>() >= self)
- .def(double() >= self)
-
- .def(self == self)
- .def(self == other<balance_pair_t>())
- .def(self == other<balance_t>())
- .def(self == other<amount_t>())
- .def(self == long())
- .def(self == other<datetime_t>())
- .def(self == double())
-
- .def(other<balance_pair_t>() == self)
- .def(other<balance_t>() == self)
- .def(other<amount_t>() == self)
- .def(long() == self)
- .def(other<datetime_t>() == self)
- .def(double() == self)
-
- .def(self != self)
- .def(self != other<balance_pair_t>())
- .def(self != other<balance_t>())
- .def(self != other<amount_t>())
- .def(self != long())
- .def(self != other<datetime_t>())
- .def(self != double())
-
- .def(other<balance_pair_t>() != self)
- .def(other<balance_t>() != self)
- .def(other<amount_t>() != self)
- .def(long() != self)
- .def(other<datetime_t>() != self)
- .def(double() != self)
-
- .def(! self)
-
- .def(self_ns::int_(self))
- .def(self_ns::float_(self))
- .def(self_ns::str(self))
- .def(abs(self))
-
- .def_readonly("type", &value_t::type)
-
- .def("__len__", value_len)
- .def("__getitem__", value_getitem)
-
- .def("cast", &value_t::cast)
- .def("cost", &value_t::cost)
- .def("price", &value_t::price)
- .def("date", &value_t::date)
- .def("strip_annotations", &value_t::strip_annotations)
- .def("add", &value_t::add, return_internal_reference<>())
- .def("value", &value_t::value)
- .def("round", &value_t::round)
- .def("negate", &value_t::negate)
- .def("negated", &value_t::negated)
- ;
-
- enum_< value_t::type_t > ("ValueType")
- .value("BOOLEAN", value_t::BOOLEAN)
- .value("INTEGER", value_t::INTEGER)
- .value("DATETIME", value_t::DATETIME)
- .value("AMOUNT", value_t::AMOUNT)
- .value("BALANCE", value_t::BALANCE)
- .value("BALANCE_PAIR", value_t::BALANCE_PAIR)
- ;
-}
-
-#endif // USE_BOOST_PYTHON
+} // namespace ledger
diff --git a/value.h b/value.h
index fe01786b..5618eabb 100644
--- a/value.h
+++ b/value.h
@@ -1,443 +1,832 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file value.h
+ * @author John Wiegley
+ * @date Thu Jun 14 21:54:00 2007
+ *
+ * @brief Abstract dynamic type representing various numeric types.
+ *
+ * A value_t object can be one of many types, and changes its type
+ * dynamically based on how it is used. For example, if you assign
+ * the number 10 to a value object, it's internal type will be
+ * INTEGER.
+ */
#ifndef _VALUE_H
#define _VALUE_H
-#include "amount.h"
-#include "balance.h"
-#include "error.h"
-
-#include <exception>
+#include "balpair.h" // pulls in balance.h and amount.h
namespace ledger {
-// The following type is a polymorphous value type used solely for
-// performance reasons. The alternative is to compute value
-// expressions (valexpr.cc) in terms of the largest data type,
-// balance_t. This was found to be prohibitively expensive, especially
-// when large logic chains were involved, since many temporary
-// allocations would occur for every operator. With value_t, and the
-// fact that logic chains only need boolean values to continue, no
-// memory allocations need to take place at all.
-
+/**
+ * @class value_t
+ *
+ * @brief Dynamic type representing various numeric types.
+ *
+ * The following type is a polymorphous value type used solely for
+ * performance reasons. The alternative is to compute value
+ * expressions (valexpr.cc) in terms of the largest data type,
+ * balance_t. This was found to be prohibitively expensive, especially
+ * when large logic chains were involved, since many temporary
+ * allocations would occur for every operator. With value_t, and the
+ * fact that logic chains only need boolean values to continue, no
+ * memory allocations need to take place at all.
+ */
class value_t
+ : public ordered_field_operators<value_t,
+ equality_comparable<value_t, balance_pair_t,
+ equality_comparable<value_t, balance_t,
+ additive<value_t, balance_pair_t,
+ additive<value_t, balance_t,
+ multiplicative<value_t, balance_pair_t,
+ multiplicative<value_t, balance_t,
+ ordered_field_operators<value_t, amount_t,
+ ordered_field_operators<value_t, double,
+ ordered_field_operators<value_t, unsigned long,
+ ordered_field_operators<value_t, long> > > > > > > > > > >
{
- public:
- char data[sizeof(balance_pair_t)];
-
+public:
+ /**
+ * The sequence_t member type abstracts the type used to represent a
+ * resizable "array" of value_t objects.
+ */
+ typedef std::vector<value_t> sequence_t;
+
+ typedef sequence_t::iterator iterator;
+ typedef sequence_t::const_iterator const_iterator;
+ typedef sequence_t::difference_type difference_type;
+
+ /**
+ * type_t gives the type of the data contained or referenced by a
+ * value_t object. Use the type() method to get a value of type
+ * type_t.
+ */
enum type_t {
- BOOLEAN,
- INTEGER,
- DATETIME,
- AMOUNT,
- BALANCE,
- BALANCE_PAIR
- } type;
+ VOID, // a null value (i.e., uninitialized)
+ BOOLEAN, // a boolean
+ DATETIME, // a date and time (Boost posix_time)
+ INTEGER, // a signed integer value
+ AMOUNT, // a ledger::amount_t
+ BALANCE, // a ledger::balance_t
+ BALANCE_PAIR, // a ledger::balance_pair_t
+ STRING, // a string object
+ SEQUENCE, // a vector of value_t objects
+ POINTER // an opaque pointer of any type
+ };
+
+private:
+ class storage_t
+ {
+ friend class value_t;
+
+ /**
+ * The `data' member holds the actual bytes relating to whatever
+ * has been stuffed into this storage object. There is a set of
+ * asserts in value.cc to guarantee that the sizeof expression
+ * used here is indeed at least as big as the largest object that
+ * will ever be copied into `data'.
+ *
+ * The `type' member holds the value_t::type_t value representing
+ * the type of the object stored.
+ */
+ char data[sizeof(amount_t)];
+ type_t type;
+
+ /**
+ * `refc' holds the current reference count for each storage_t
+ * object.
+ */
+ mutable int refc;
+
+ /**
+ * Constructor. Since all storage object are assigned to after
+ * construction, the only constructors allowed are explicit, and
+ * copy (see below). The default starting type is VOID, which
+ * should rarely ever be seen in practice, since the first thing
+ * that value_t typically does is to assign a valid value.
+ */
+ explicit storage_t() : type(VOID), refc(0) {
+ TRACE_CTOR(value_t::storage_t, "");
+ }
- value_t() {
- *((long *) data) = 0;
- type = INTEGER;
- }
+ public: // so `checked_delete' can access it
+ /**
+ * Destructor. Must only be called when the reference count has
+ * reached zero. The `destroy' method is used to do the actual
+ * cleanup of the data, since it's quite possible for `destroy' to
+ * be called while the object is still active -- to clear the
+ * stored data for subsequent reuse of the storage_t object.
+ */
+ ~storage_t() {
+ TRACE_DTOR(value_t::storage_t);
+ DEBUG("value.storage.refcount", "Destroying " << this);
+ assert(refc == 0);
+ destroy();
+ }
- value_t(const value_t& value) : type(INTEGER) {
- *this = value;
- }
- value_t(const bool value) {
- *((bool *) data) = value;
- type = BOOLEAN;
- }
- value_t(const long value) {
- *((long *) data) = value;
- type = INTEGER;
- }
- value_t(const datetime_t value) {
- *((datetime_t *) data) = value;
- type = DATETIME;
+ void destroy();
+
+ private:
+ /**
+ * Assignment and copy operators. These are called when making a
+ * new copy of a storage object in order to modify the copy.
+ */
+ explicit storage_t(const storage_t& rhs)
+ : type(rhs.type), refc(0) {
+ TRACE_CTOR(value_t::storage_t, "");
+ std::memcpy(data, rhs.data, sizeof(data));
+ }
+ storage_t& operator=(const storage_t& rhs) {
+ type = rhs.type;
+ std::memcpy(data, rhs.data, sizeof(data));
+ return *this;
+ }
+
+ /**
+ * Reference counting methods. The intrusive_ptr_* methods are
+ * used by boost::intrusive_ptr to manage the calls to acquire and
+ * release.
+ */
+ void acquire() const {
+ DEBUG("value.storage.refcount",
+ "Acquiring " << this << ", refc now " << refc + 1);
+ assert(refc >= 0);
+ refc++;
+ }
+ void release() const {
+ DEBUG("value.storage.refcount",
+ "Releasing " << this << ", refc now " << refc - 1);
+ assert(refc > 0);
+ if (--refc == 0)
+ checked_delete(this);
+ }
+
+ friend inline void intrusive_ptr_add_ref(value_t::storage_t * storage) {
+ storage->acquire();
+ }
+ friend inline void intrusive_ptr_release(value_t::storage_t * storage) {
+ storage->release();
+ }
+ };
+
+ /**
+ * The actual data for each value_t is kept in the `storage' member.
+ * Data is modified using a copy-on-write policy.
+ */
+ intrusive_ptr<storage_t> storage;
+
+ /**
+ * _dup() makes a private copy of the current value so that it can
+ * subsequently be modified.
+ *
+ * _clear() removes our pointer to the current value and initializes
+ * a new value for things to be stored in.
+ *
+ * _reset() makes the current object appear as if it had been
+ * default initialized.
+ */
+ void _dup();
+ void _clear() {
+ if (! storage || storage->refc > 1)
+ storage = new storage_t;
+ else
+ storage->destroy();
+ }
+ void _reset() {
+ if (storage)
+ storage = intrusive_ptr<storage_t>();
+ }
+
+ /**
+ * Because boolean "true" and "false" are so common, a pair of
+ * static references are kept to prevent the creation of throwaway
+ * storage_t objects just to represent these two common values.
+ */
+ static intrusive_ptr<storage_t> true_value;
+ static intrusive_ptr<storage_t> false_value;
+
+public:
+ // jww (2007-05-03): Make these private, and make ledger::initialize
+ // a member function of session_t.
+ static void initialize();
+ static void shutdown();
+
+public:
+ /**
+ * Constructors. value_t objects may be constructed from almost any
+ * value type that they can contain, including variations on those
+ * types (such as long, unsigned long, etc). The ordering of the
+ * methods here reflects the ordering of the constants in type_t
+ * above.
+ *
+ * One constructor of special note is that taking a string or
+ * character pointer as an argument. Because value_t("$100") is
+ * interpreted as a commoditized amount, the form value_t("$100",
+ * true) is required to represent the literal string "$100", and not
+ * the amount "one hundred dollars".
+ */
+ value_t() {
+ TRACE_CTOR(value_t, "");
+ }
+ value_t(const bool val) {
+ TRACE_CTOR(value_t, "const bool");
+ set_boolean(val);
+ }
+ value_t(const long val) {
+ TRACE_CTOR(value_t, "const long");
+ set_long(val);
+ }
+ value_t(const moment_t val) {
+ TRACE_CTOR(value_t, "const moment_t");
+ set_datetime(val);
+ }
+ value_t(const double val) {
+ TRACE_CTOR(value_t, "const double");
+ set_amount(val);
+ }
+ value_t(const unsigned long val) {
+ TRACE_CTOR(value_t, "const unsigned long");
+ set_amount(val);
+ }
+ explicit value_t(const string& val, bool literal = false) {
+ TRACE_CTOR(value_t, "const string&, bool");
+ if (literal)
+ set_string(val);
+ else
+ set_amount(amount_t(val));
+ }
+ explicit value_t(const char * val, bool literal = false) {
+ TRACE_CTOR(value_t, "const char *");
+ if (literal)
+ set_string(val);
+ else
+ set_amount(amount_t(val));
+ }
+ value_t(const amount_t& val) {
+ TRACE_CTOR(value_t, "const amount_t&");
+ set_amount(val);
+ }
+ value_t(const balance_t& val) {
+ TRACE_CTOR(value_t, "const balance_t&");
+ set_balance(val);
+ }
+ value_t(const balance_pair_t& val) {
+ TRACE_CTOR(value_t, "const balance_pair_t&");
+ set_balance_pair(val);
+ }
+ value_t(const sequence_t& val) {
+ TRACE_CTOR(value_t, "const sequence_t&");
+ set_sequence(val);
}
- value_t(const unsigned long value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+ template <typename T>
+ explicit value_t(T * item) {
+ TRACE_CTOR(value_t, "T *");
+ set_pointer(item);
}
- value_t(const double value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+
+ /**
+ * Destructor. This does not do anything, because the intrusive_ptr
+ * that refers to our storage object will decrease its reference
+ * count itself upon destruction.
+ */
+ ~value_t() {
+ TRACE_DTOR(value_t);
}
- value_t(const std::string& value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+
+ /**
+ * Assignment and copy operators. Values are cheaply copied by
+ * simply creating another reference to the other value's storage
+ * object. A true copy is only ever made prior to modification.
+ */
+ value_t(const value_t& val) {
+ TRACE_CTOR(value_t, "copy");
+ *this = val;
}
- value_t(const char * value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+ value_t& operator=(const value_t& val) {
+ if (! (this == &val || storage == val.storage))
+ storage = val.storage;
+ return *this;
}
- value_t(const amount_t& value) {
- new((amount_t *)data) amount_t(value);
- type = AMOUNT;
+
+ /**
+ * Comparison operators. Values can be compared to other values
+ */
+ bool operator==(const value_t& val) const;
+ bool operator<(const value_t& val) const;
+
+ template <typename T>
+ bool operator==(const T& amt) const {
+ return *this == value_t(amt);
}
- value_t(const balance_t& value) : type(INTEGER) {
- *this = value;
+ template <typename T>
+ bool operator<(const T& amt) const {
+ return *this < value_t(amt);
+ }
+
+ /**
+ * Binary arithmetic operators.
+ *
+ * add(amount_t, optional<amount_t>) allows for the possibility of
+ * adding both an amount and its cost in a single operation.
+ * Otherwise, there is no way to separately represent the "cost"
+ * part of an amount addition statement.
+ */
+ value_t& operator+=(const value_t& val);
+ value_t& operator-=(const value_t& val);
+ value_t& operator*=(const value_t& val);
+ value_t& operator/=(const value_t& val);
+
+ value_t& add(const amount_t& amount,
+ const optional<amount_t>& cost = none);
+
+ /**
+ * Unary arithmetic operators.
+ */
+ value_t negate() const {
+ value_t temp = *this;
+ temp.in_place_negate();
+ return temp;
}
- value_t(const balance_pair_t& value) : type(INTEGER) {
- *this = value;
+ void in_place_negate();
+
+ value_t operator-() const {
+ return negate();
}
- ~value_t() {
- destroy();
+ value_t abs() const;
+ value_t round() const;
+ value_t unround() const;
+
+ value_t reduce() const {
+ value_t temp(*this);
+ temp.in_place_reduce();
+ return temp;
}
+ void in_place_reduce();
- void destroy();
- void simplify();
+ value_t value(const optional<moment_t>& moment = none) const;
- value_t& operator=(const value_t& value);
- value_t& operator=(const bool value) {
- if ((bool *) data != &value) {
- destroy();
- *((bool *) data) = value;
- type = BOOLEAN;
+ /**
+ * Truth tests.
+ */
+ operator bool() const;
+
+ bool is_realzero() const;
+ bool is_null() const {
+ if (! storage) {
+ return true;
+ } else {
+ assert(! is_type(VOID));
+ return false;
}
- return *this;
}
- value_t& operator=(const long value) {
- if ((long *) data != &value) {
- destroy();
- *((long *) data) = value;
- type = INTEGER;
- }
- return *this;
+
+ type_t type() const {
+ type_t result = storage ? storage->type : VOID;
+ assert(result >= VOID && result <= POINTER);
+ return result;
}
- value_t& operator=(const datetime_t value) {
- if ((datetime_t *) data != &value) {
- destroy();
- *((datetime_t *) data) = value;
- type = DATETIME;
+
+ bool is_type(type_t _type) const {
+ return type() == _type;
+ }
+private:
+ void set_type(type_t new_type) {
+ assert(new_type >= VOID && new_type <= POINTER);
+ if (new_type == VOID) {
+ _reset();
+ assert(is_null());
+ } else {
+ _clear();
+ storage->type = new_type;
+ assert(is_type(new_type));
}
- return *this;
}
- value_t& operator=(const unsigned long value) {
- return *this = amount_t(value);
+
+public:
+ /**
+ * Data manipulation methods. A value object may be truth tested
+ * for the existence of every type it can contain:
+ *
+ * is_boolean()
+ * is_long()
+ * is_datetime()
+ * is_amount()
+ * is_balance()
+ * is_balance_pair()
+ * is_string()
+ * is_sequence()
+ * is_pointer()
+ *
+ * There are corresponding as_*() methods that represent a value as
+ * a reference to its underlying type. For example, as_integer()
+ * returns a reference to a "const long".
+ *
+ * There are also as_*_lval() methods, which represent the
+ * underlying data as a reference to a non-const type. The
+ * difference here is that an _lval() call causes the underlying
+ * data to be fully copied before the resulting reference is
+ * returned.
+ *
+ * Lastly, there are corresponding set_*(data) methods for directly
+ * assigning data of a particular type, rather than using the
+ * regular assignment operator (whose implementation simply calls
+ * the various set_ methods).
+ */
+ bool is_boolean() const {
+ return is_type(BOOLEAN);
+ }
+ bool& as_boolean_lval() {
+ assert(is_boolean());
+ _dup();
+ return *(bool *) storage->data;
+ }
+ const bool& as_boolean() const {
+ assert(is_boolean());
+ return *(bool *) storage->data;
+ }
+ void set_boolean(const bool val) {
+ set_type(BOOLEAN);
+ storage = val ? true_value : false_value;
+ }
+
+ bool is_long() const {
+ return is_type(INTEGER);
+ }
+ long& as_long_lval() {
+ assert(is_long());
+ _dup();
+ return *(long *) storage->data;
+ }
+ const long& as_long() const {
+ assert(is_long());
+ return *(long *) storage->data;
+ }
+ void set_long(const long val) {
+ set_type(INTEGER);
+ *(long *) storage->data = val;
+ }
+
+ bool is_datetime() const {
+ return is_type(DATETIME);
}
- value_t& operator=(const double value) {
- return *this = amount_t(value);
+ moment_t& as_datetime_lval() {
+ assert(is_datetime());
+ _dup();
+ return *(moment_t *) storage->data;
}
- value_t& operator=(const std::string& value) {
- return *this = amount_t(value);
+ const moment_t& as_datetime() const {
+ assert(is_datetime());
+ return *(moment_t *) storage->data;
}
- value_t& operator=(const char * value) {
- return *this = amount_t(value);
+ void set_datetime(const moment_t& val) {
+ set_type(DATETIME);
+ new((moment_t *) storage->data) moment_t(val);
}
- value_t& operator=(const amount_t& value) {
- if (type == AMOUNT &&
- (amount_t *) data == &value)
- return *this;
-
- if (value.realzero()) {
- return *this = 0L;
- } else {
- destroy();
- new((amount_t *)data) amount_t(value);
- type = AMOUNT;
- }
- return *this;
+
+ bool is_amount() const {
+ return is_type(AMOUNT);
}
- value_t& operator=(const balance_t& value) {
- if (type == BALANCE &&
- (balance_t *) data == &value)
- return *this;
-
- if (value.realzero()) {
- return *this = 0L;
- }
- else if (value.amounts.size() == 1) {
- return *this = (*value.amounts.begin()).second;
- }
- else {
- destroy();
- new((balance_t *)data) balance_t(value);
- type = BALANCE;
- return *this;
- }
+ amount_t& as_amount_lval() {
+ assert(is_amount());
+ _dup();
+ return *(amount_t *) storage->data;
}
- value_t& operator=(const balance_pair_t& value) {
- if (type == BALANCE_PAIR &&
- (balance_pair_t *) data == &value)
- return *this;
-
- if (value.realzero()) {
- return *this = 0L;
- }
- else if (! value.cost) {
- return *this = value.quantity;
- }
- else {
- destroy();
- new((balance_pair_t *)data) balance_pair_t(value);
- type = BALANCE_PAIR;
- return *this;
- }
+ const amount_t& as_amount() const {
+ assert(is_amount());
+ return *(amount_t *) storage->data;
+ }
+ void set_amount(const amount_t& val) {
+ set_type(AMOUNT);
+ new((amount_t *) storage->data) amount_t(val);
}
- value_t& operator+=(const value_t& value);
- value_t& operator-=(const value_t& value);
- value_t& operator*=(const value_t& value);
- value_t& operator/=(const value_t& value);
+ bool is_balance() const {
+ return is_type(BALANCE);
+ }
+ balance_t& as_balance_lval() {
+ assert(is_balance());
+ _dup();
+ return **(balance_t **) storage->data;
+ }
+ const balance_t& as_balance() const {
+ assert(is_balance());
+ return **(balance_t **) storage->data;
+ }
+ void set_balance(const balance_t& val) {
+ set_type(BALANCE);
+ *(balance_t **) storage->data = new balance_t(val);
+ }
- template <typename T>
- value_t& operator+=(const T& value) {
- return *this += value_t(value);
+ bool is_balance_pair() const {
+ return is_type(BALANCE_PAIR);
}
- template <typename T>
- value_t& operator-=(const T& value) {
- return *this -= value_t(value);
+ balance_pair_t& as_balance_pair_lval() {
+ assert(is_balance_pair());
+ _dup();
+ return **(balance_pair_t **) storage->data;
}
- template <typename T>
- value_t& operator*=(const T& value) {
- return *this *= value_t(value);
+ const balance_pair_t& as_balance_pair() const {
+ assert(is_balance_pair());
+ return **(balance_pair_t **) storage->data;
}
- template <typename T>
- value_t& operator/=(const T& value) {
- return *this /= value_t(value);
+ void set_balance_pair(const balance_pair_t& val) {
+ set_type(BALANCE_PAIR);
+ *(balance_pair_t **) storage->data = new balance_pair_t(val);
}
- value_t operator+(const value_t& value) {
- value_t temp(*this);
- temp += value;
- return temp;
+ bool is_string() const {
+ return is_type(STRING);
}
- value_t operator-(const value_t& value) {
- value_t temp(*this);
- temp -= value;
- return temp;
+ string& as_string_lval() {
+ assert(is_string());
+ _dup();
+ return *(string *) storage->data;
}
- value_t operator*(const value_t& value) {
- value_t temp(*this);
- temp *= value;
- return temp;
+ const string& as_string() const {
+ assert(is_string());
+ return *(string *) storage->data;
}
- value_t operator/(const value_t& value) {
- value_t temp(*this);
- temp /= value;
- return temp;
+ void set_string(const string& val = "") {
+ set_type(STRING);
+ new((string *) storage->data) string(val);
}
- template <typename T>
- value_t operator+(const T& value) {
- return *this + value_t(value);
+ bool is_sequence() const {
+ return is_type(SEQUENCE);
}
- template <typename T>
- value_t operator-(const T& value) {
- return *this - value_t(value);
+ sequence_t& as_sequence_lval() {
+ assert(is_sequence());
+ _dup();
+ return **(sequence_t **) storage->data;
}
- template <typename T>
- value_t operator*(const T& value) {
- return *this * value_t(value);
+ const sequence_t& as_sequence() const {
+ assert(is_sequence());
+ return **(sequence_t **) storage->data;
}
- template <typename T>
- value_t operator/(const T& value) {
- return *this / value_t(value);
+ void set_sequence(const sequence_t& val) {
+ set_type(SEQUENCE);
+ *(sequence_t **) storage->data = new sequence_t(val);
}
- bool operator<(const value_t& value);
- bool operator<=(const value_t& value);
- bool operator>(const value_t& value);
- bool operator>=(const value_t& value);
- bool operator==(const value_t& value);
- bool operator!=(const value_t& value) {
- return ! (*this == value);
+ bool is_pointer() const {
+ return is_type(POINTER);
}
-
- template <typename T>
- bool operator<(const T& value) {
- return *this < value_t(value);
+ boost::any& as_any_pointer_lval() {
+ assert(is_pointer());
+ _dup();
+ return *(boost::any *) storage->data;
}
template <typename T>
- bool operator<=(const T& value) {
- return *this <= value_t(value);
+ T *& as_pointer_lval() {
+ assert(is_pointer());
+ _dup();
+ return any_cast<T *>(*(boost::any *) storage->data);
}
template <typename T>
- bool operator>(const T& value) {
- return *this > value_t(value);
+ T& as_ref_lval() {
+ assert(is_pointer());
+ _dup();
+ return *any_cast<T *>(*(boost::any *) storage->data);
}
- template <typename T>
- bool operator>=(const T& value) {
- return *this >= value_t(value);
+ boost::any as_any_pointer() const {
+ assert(is_pointer());
+ return *(boost::any *) storage->data;
}
template <typename T>
- bool operator==(const T& value) {
- return *this == value_t(value);
+ T * as_pointer() const {
+ assert(is_pointer());
+ return any_cast<T *>(*(boost::any *) storage->data);
}
template <typename T>
- bool operator!=(const T& value) {
- return ! (*this == value);
+ T& as_ref() const {
+ assert(is_pointer());
+ return *any_cast<T *>(*(boost::any *) storage->data);
+ }
+ void set_any_pointer(const boost::any& val) {
+ set_type(POINTER);
+ new((boost::any *) storage->data) boost::any(val);
}
-
template <typename T>
- operator T() const;
+ void set_pointer(T * val) {
+ set_type(POINTER);
+ new((boost::any *) storage->data) boost::any(val);
+ }
+
+ /**
+ * Data conversion methods. These methods convert a value object to
+ * its underlying type, where possible. If not possible, an
+ * exception is thrown.
+ */
+ bool to_boolean() const;
+ long to_long() const;
+ moment_t to_datetime() const;
+ amount_t to_amount() const;
+ balance_t to_balance() const;
+ balance_pair_t to_balance_pair() const;
+ string to_string() const;
+ sequence_t to_sequence() const;
+
+ /**
+ * Dynamic typing conversion methods.
+ *
+ * `cast(type_t)' returns a new value whose type has been cast to
+ * the given type, but whose value is based on the original value.
+ * For example, the uncommoditized AMOUNT "100.00" could be cast to
+ * an INTEGER value. If a cast would lose information or is not
+ * meaningful, an exception is thrown.
+ *
+ * `simplify()' is an automatic cast to the simplest type that can
+ * still represent the original value.
+ *
+ * There are also "in-place" versions of these two methods:
+ * in_place_cast
+ * in_place_simplify
+ */
+ value_t cast(type_t cast_type) const {
+ value_t temp(*this);
+ temp.in_place_cast(cast_type);
+ return temp;
+ }
+ void in_place_cast(type_t cast_type);
- void negate();
- value_t negated() const {
+ value_t simplify() const {
value_t temp = *this;
- temp.negate();
+ temp.in_place_simplify();
return temp;
}
- value_t operator-() const {
- return negated();
+ void in_place_simplify();
+
+ /**
+ * Annotated commodity methods.
+ */
+ value_t annotated_price() const;
+ value_t annotated_date() const;
+ value_t annotated_tag() const;
+
+ value_t strip_annotations(const bool keep_price = amount_t::keep_price,
+ const bool keep_date = amount_t::keep_date,
+ const bool keep_tag = amount_t::keep_tag) const;
+
+ /**
+ * Collection-style access methods
+ */
+ value_t& operator[](const int index) {
+ assert(! is_null());
+ if (is_sequence())
+ return as_sequence_lval()[index];
+ else if (index == 0)
+ return *this;
+
+ assert(false);
+ static value_t null;
+ return null;
}
+ const value_t& operator[](const int index) const {
+ assert(! is_null());
+ if (is_sequence())
+ return as_sequence()[index];
+ else if (index == 0)
+ return *this;
+
+ assert(false);
+ static value_t null;
+ return null;
+ }
+
+ void push_back(const value_t& val) {
+ if (! val.is_null()) {
+ if (is_null()) {
+ *this = val;
+ } else {
+ if (! is_sequence())
+ in_place_cast(SEQUENCE);
+
+ value_t::sequence_t& seq(as_sequence_lval());
+ if (! val.is_sequence()) {
+ if (! val.is_null())
+ seq.push_back(val);
+ } else {
+ const value_t::sequence_t& val_seq(val.as_sequence());
+ std::copy(val_seq.begin(), val_seq.end(), back_inserter(seq));
+ }
+ }
+ }
+ }
+
+ void pop_back() {
+ assert(! is_null());
- bool realzero() const {
- switch (type) {
+ if (! is_sequence()) {
+ _reset();
+ } else {
+ as_sequence_lval().pop_back();
+
+ std::size_t new_size = as_sequence().size();
+ if (new_size == 0)
+ _reset();
+ else if (new_size == 1)
+ *this = as_sequence().front();
+ }
+ }
+
+ const std::size_t size() const {
+ if (is_null())
+ return 0;
+ else if (is_sequence())
+ return as_sequence().size();
+ else
+ return 1;
+ }
+
+ /**
+ * Informational methods.
+ */
+ string label(optional<type_t> the_type = none) const {
+ switch (the_type ? *the_type : type()) {
+ case VOID:
+ return "an uninitialized value";
case BOOLEAN:
- return ! *((bool *) data);
+ return "a boolean";
case INTEGER:
- return *((long *) data) == 0;
+ return "an integer";
case DATETIME:
- return ! *((datetime_t *) data);
+ return "a date/time";
case AMOUNT:
- return ((amount_t *) data)->realzero();
+ return "an amount";
case BALANCE:
- return ((balance_t *) data)->realzero();
+ return "a balance";
case BALANCE_PAIR:
- return ((balance_pair_t *) data)->realzero();
-
+ return "a balance pair";
+ case STRING:
+ return "a string";
+ case SEQUENCE:
+ return "a sequence";
+ case POINTER:
+ return "a pointer";
default:
- assert(0);
+ assert(false);
break;
}
- assert(0);
- return 0;
+ assert(false);
+ return "<invalid>";
}
- void abs();
- void cast(type_t cast_type);
value_t cost() const;
- value_t price() const;
- value_t date() const;
- value_t strip_annotations(const bool keep_price = amount_t::keep_price,
- const bool keep_date = amount_t::keep_date,
- const bool keep_tag = amount_t::keep_tag) const;
+ /**
+ * Printing methods.
+ */
+ void print(std::ostream& out, const int first_width,
+ const int latter_width = -1) const;
- value_t& add(const amount_t& amount, const amount_t * cost = NULL);
- value_t value(const datetime_t& moment) const;
- void reduce();
-
- value_t reduced() const {
- value_t temp(*this);
- temp.reduce();
- return temp;
- }
-
- void round();
- value_t unround() const;
+ /**
+ * Debugging methods.
+ */
};
-#define DEF_VALUE_AUX_OP(OP) \
- inline value_t operator OP(const balance_pair_t& value, \
- const value_t& obj) { \
- return value_t(value) OP obj; \
- } \
- inline value_t operator OP(const balance_t& value, \
- const value_t& obj) { \
- return value_t(value) OP obj; \
- } \
- inline value_t operator OP(const amount_t& value, \
- const value_t& obj) { \
- return value_t(value) OP obj; \
- } \
- template <typename T> \
- inline value_t operator OP(T value, const value_t& obj) { \
- return value_t(value) OP obj; \
- }
-
-DEF_VALUE_AUX_OP(+)
-DEF_VALUE_AUX_OP(-)
-DEF_VALUE_AUX_OP(*)
-DEF_VALUE_AUX_OP(/)
-
-DEF_VALUE_AUX_OP(<)
-DEF_VALUE_AUX_OP(<=)
-DEF_VALUE_AUX_OP(>)
-DEF_VALUE_AUX_OP(>=)
-DEF_VALUE_AUX_OP(==)
-DEF_VALUE_AUX_OP(!=)
-
-template <typename T>
-value_t::operator T() const
-{
- switch (type) {
- case BOOLEAN:
- return *((bool *) data);
- case INTEGER:
- return *((long *) data);
- case DATETIME:
- return *((datetime_t *) data);
- case AMOUNT:
- return *((amount_t *) data);
- case BALANCE:
- return *((balance_t *) data);
- case BALANCE_PAIR:
- return *((balance_pair_t *) data);
-
- default:
- assert(0);
- break;
- }
- assert(0);
- return 0;
-}
-
-template <> value_t::operator long() const;
-template <> value_t::operator datetime_t() const;
-template <> value_t::operator double() const;
+#define NULL_VALUE (value_t())
-inline value_t abs(const value_t& value) {
- value_t temp(value);
- temp.abs();
- return temp;
+inline value_t string_value(const string& str) {
+ return value_t(str, true);
}
-inline std::ostream& operator<<(std::ostream& out, const value_t& value) {
- switch (value.type) {
- case value_t::BOOLEAN:
- out << (*((bool *) value.data) ? "true" : "false");
- break;
- case value_t::INTEGER:
- out << *((long *) value.data);
- break;
- case value_t::DATETIME:
- out << *((datetime_t *) value.data);
- break;
- case value_t::AMOUNT:
- out << *((amount_t *) value.data);
- break;
- case value_t::BALANCE:
- out << *((balance_t *) value.data);
- break;
- case value_t::BALANCE_PAIR:
- out << *((balance_pair_t *) value.data);
- break;
-
- default:
- assert(0);
- break;
- }
+inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
+ val.print(out, 12);
return out;
}
-class value_context : public error_context
-{
- value_t * bal;
- public:
- value_context(const value_t& _bal,
- const std::string& desc = "") throw();
- virtual ~value_context() throw();
-
- virtual void describe(std::ostream& out) const throw();
-};
-
-class value_error : public error {
- public:
- value_error(const std::string& reason, error_context * ctxt = NULL) throw()
- : error(reason, ctxt) {}
- virtual ~value_error() throw() {}
-};
+DECLARE_EXCEPTION(error, value_error);
} // namespace ledger
diff --git a/version b/version
new file mode 100755
index 00000000..b481c6f4
--- /dev/null
+++ b/version
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+cat configure.in | \
+ grep "^AC_INIT" | \
+ sed 's/AC_INIT(\[ledger\],\[\([^]]*\)\],.*/\1/'