summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am13
-rw-r--r--account.cc21
-rw-r--r--account.h48
-rw-r--r--compare.cc85
-rw-r--r--compare.h77
-rw-r--r--csv.h2
-rw-r--r--derive.cc4
-rw-r--r--emacs.cc1
-rw-r--r--emacs.h2
-rw-r--r--filters.cc (renamed from walk.cc)289
-rw-r--r--filters.h (renamed from walk.h)383
-rw-r--r--format.cc13
-rw-r--r--format.h1
-rw-r--r--gnucash.cc19
-rw-r--r--handler.h72
-rw-r--r--help.cc205
-rw-r--r--help.h49
-rw-r--r--iterators.cc200
-rw-r--r--iterators.h226
-rw-r--r--main.cc543
-rw-r--r--option.cc169
-rw-r--r--output.cc282
-rw-r--r--output.h143
-rw-r--r--reconcile.cc1
-rw-r--r--reconcile.h3
-rw-r--r--report.cc295
-rw-r--r--report.h116
-rw-r--r--session.cc47
-rw-r--r--textual.cc2
-rw-r--r--textual.h3
-rw-r--r--token.cc28
-rw-r--r--xact.h8
-rw-r--r--xml.h1
33 files changed, 1874 insertions, 1477 deletions
diff --git a/Makefile.am b/Makefile.am
index 6fe575be..72ff6f38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,6 +70,8 @@ libledger_la_SOURCES = \
entry.cc \
xact.cc \
account.cc \
+ iterators.cc \
+ compare.cc \
\
textual.cc \
cache.cc \
@@ -80,7 +82,9 @@ libledger_la_SOURCES = \
\
session.cc \
report.cc \
- walk.cc \
+ filters.cc \
+ output.cc \
+ help.cc \
\
derive.cc \
reconcile.cc \
@@ -130,6 +134,8 @@ pkginclude_HEADERS = \
entry.h \
xact.h \
account.h \
+ iterators.h \
+ compare.h \
\
textual.h \
cache.h \
@@ -142,7 +148,10 @@ pkginclude_HEADERS = \
\
session.h \
report.h \
- walk.h \
+ handler.h \
+ filters.h \
+ output.h \
+ help.h \
\
derive.h \
reconcile.h \
diff --git a/account.cc b/account.cc
index 241ac097..9fee7145 100644
--- a/account.cc
+++ b/account.cc
@@ -143,4 +143,25 @@ bool account_t::valid() const
return true;
}
+void account_t::calculate_sums()
+{
+ xdata_t& xd(xdata());
+
+ foreach (accounts_map::value_type& pair, accounts) {
+ (*pair.second).calculate_sums();
+
+ xd.total += (*pair.second).xdata().total;
+ xd.total_count += ((*pair.second).xdata().total_count +
+ (*pair.second).xdata().count);
+ }
+
+ value_t result;
+#if 0
+ compute_amount(result, details_t(account));
+#endif
+ if (! result.is_realzero())
+ xd.total += result;
+ xd.total_count += xd.count;
+}
+
} // namespace ledger
diff --git a/account.h b/account.h
index 9102a172..92bd8736 100644
--- a/account.h
+++ b/account.h
@@ -98,6 +98,54 @@ class account_t : public scope_t
bool valid() const;
friend class journal_t;
+
+ struct xdata_t : public supports_flags<>
+ {
+#define ACCOUNT_EXT_TO_DISPLAY 0x01
+#define ACCOUNT_EXT_DISPLAYED 0x02
+#define ACCOUNT_EXT_SORT_CALC 0x04
+#define ACCOUNT_EXT_HAS_NON_VIRTUALS 0x08
+#define ACCOUNT_EXT_HAS_UNB_VIRTUALS 0x10
+
+ value_t value;
+ value_t total;
+ value_t sort_value;
+ unsigned int count; // xacts counted toward amount
+ unsigned int total_count; // xacts counted toward total
+ unsigned int virtuals;
+ unsigned short dflags;
+
+ xdata_t()
+ : supports_flags<>(), count(0), total_count(0),
+ virtuals(0), dflags(0)
+ {
+ TRACE_CTOR(xdata_t, "");
+ }
+
+ ~xdata_t() throw() {
+ TRACE_DTOR(xdata_t);
+ }
+ };
+
+ // This variable holds optional "extended data" which is usually produced
+ // only during reporting, and only for the transaction set being reported.
+ // It's a memory-saving measure to delay allocation until the last possible
+ // moment.
+ mutable optional<xdata_t> xdata_;
+
+ bool has_xdata() const {
+ return xdata_;
+ }
+ void clear_xdata() {
+ xdata_ = none;
+ }
+ xdata_t& xdata() {
+ if (! xdata_)
+ xdata_ = xdata_t();
+ return *xdata_;
+ }
+
+ void calculate_sums();
};
std::ostream& operator<<(std::ostream& out, const account_t& account);
diff --git a/compare.cc b/compare.cc
new file mode 100644
index 00000000..1cbe7082
--- /dev/null
+++ b/compare.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2003-2008, 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 "compare.h"
+
+namespace ledger {
+
+template <>
+bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right)
+{
+ assert(left);
+ assert(right);
+
+ xact_t::xdata_t& lxdata(left->xdata());
+ if (! lxdata.has_flags(XACT_EXT_SORT_CALC)) {
+ lxdata.sort_value = sort_order.calc(*left);
+ lxdata.sort_value.reduce();
+ lxdata.add_flags(XACT_EXT_SORT_CALC);
+ }
+
+ xact_t::xdata_t& rxdata(right->xdata());
+ if (! rxdata.has_flags(XACT_EXT_SORT_CALC)) {
+ rxdata.sort_value = sort_order.calc(*right);
+ rxdata.sort_value.reduce();
+ rxdata.add_flags(XACT_EXT_SORT_CALC);
+ }
+
+ DEBUG("ledger.walk.compare_items_xact",
+ "lxdata.sort_value = " << lxdata.sort_value);
+ DEBUG("ledger.walk.compare_items_xact",
+ "rxdata.sort_value = " << rxdata.sort_value);
+
+ return lxdata.sort_value < rxdata.sort_value;
+}
+
+template <>
+bool compare_items<account_t>::operator()(account_t * left, account_t * right)
+{
+ assert(left);
+ assert(right);
+
+ account_t::xdata_t& lxdata(left->xdata());
+ if (! lxdata.has_flags(ACCOUNT_EXT_SORT_CALC)) {
+ lxdata.sort_value = sort_order.calc(*left);
+ lxdata.add_flags(ACCOUNT_EXT_SORT_CALC);
+ }
+
+ account_t::xdata_t& rxdata(right->xdata());
+ if (! rxdata.has_flags(ACCOUNT_EXT_SORT_CALC)) {
+ rxdata.sort_value = sort_order.calc(*right);
+ rxdata.add_flags(ACCOUNT_EXT_SORT_CALC);
+ }
+
+ return lxdata.sort_value < rxdata.sort_value;
+}
+
+} // namespace ledger
diff --git a/compare.h b/compare.h
new file mode 100644
index 00000000..d86771ef
--- /dev/null
+++ b/compare.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2003-2008, 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 _COMPARE_H
+#define _COMPARE_H
+
+#include "expr.h"
+#include "xact.h"
+#include "account.h"
+
+namespace ledger {
+
+template <typename T>
+class compare_items
+{
+ expr_t sort_order;
+
+ compare_items();
+
+public:
+ compare_items(const compare_items& other) : sort_order(other.sort_order) {
+ TRACE_CTOR(compare_items, "copy");
+ }
+ compare_items(const expr_t& _sort_order) : sort_order(_sort_order) {
+ TRACE_CTOR(compare_items, "const value_expr&");
+ }
+ ~compare_items() throw() {
+ TRACE_DTOR(compare_items);
+ }
+ bool operator()(T * left, T * right);
+};
+
+template <typename T>
+bool compare_items<T>::operator()(T * left, T * right)
+{
+ assert(left);
+ assert(right);
+ return sort_order.calc(*left) < sort_order.calc(*right);
+}
+
+template <>
+bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right);
+template <>
+bool compare_items<account_t>::operator()(account_t * left,
+ account_t * right);
+
+} // namespace ledger
+
+#endif // _COMPARE_H
diff --git a/csv.h b/csv.h
index 2a45390f..bef58ad2 100644
--- a/csv.h
+++ b/csv.h
@@ -32,7 +32,7 @@
#ifndef _CSV_H
#define _CSV_H
-#include "journal.h"
+#include "handler.h"
#include "format.h"
namespace ledger {
diff --git a/derive.cc b/derive.cc
index f3a2f3e7..dd9dc4ca 100644
--- a/derive.cc
+++ b/derive.cc
@@ -31,7 +31,7 @@
#include "derive.h"
#include "session.h"
-#include "walk.h"
+#include "iterators.h"
namespace ledger {
@@ -96,7 +96,7 @@ entry_t * derive_new_entry(report_t& report,
report.sum_all_accounts();
- value_t total = account_xdata(*acct).total;
+ value_t total = acct->xdata().total;
if (total.is_type(value_t::AMOUNT))
xact->amount.set_commodity(total.as_amount().commodity());
}
diff --git a/emacs.cc b/emacs.cc
index 48589597..cf787e75 100644
--- a/emacs.cc
+++ b/emacs.cc
@@ -30,6 +30,7 @@
*/
#include "emacs.h"
+#include "account.h"
namespace ledger {
diff --git a/emacs.h b/emacs.h
index 6786ada1..59b937f8 100644
--- a/emacs.h
+++ b/emacs.h
@@ -32,7 +32,7 @@
#ifndef _EMACS_H
#define _EMACS_H
-#include "journal.h"
+#include "handler.h"
#include "format.h"
namespace ledger {
diff --git a/walk.cc b/filters.cc
index 067e057d..e5455423 100644
--- a/walk.cc
+++ b/filters.cc
@@ -1,89 +1,51 @@
-#include "walk.h"
+/*
+ * Copyright (c) 2003-2008, 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 "filters.h"
+#include "iterators.h"
+#include "compare.h"
#include "session.h"
#include "format.h"
#include "textual.h"
-#include <algorithm>
-
namespace ledger {
-template <>
-bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right)
-{
- assert(left);
- assert(right);
-
- xact_t::xdata_t& lxdata(left->xdata());
- if (! lxdata.has_flags(XACT_EXT_SORT_CALC)) {
- lxdata.sort_value = sort_order.calc(*left);
- lxdata.sort_value.reduce();
- lxdata.add_flags(XACT_EXT_SORT_CALC);
- }
-
- xact_t::xdata_t& rxdata(right->xdata());
- if (! rxdata.has_flags(XACT_EXT_SORT_CALC)) {
- rxdata.sort_value = sort_order.calc(*right);
- rxdata.sort_value.reduce();
- rxdata.add_flags(XACT_EXT_SORT_CALC);
- }
-
- DEBUG("ledger.walk.compare_items_xact",
- "lxdata.sort_value = " << lxdata.sort_value);
- DEBUG("ledger.walk.compare_items_xact",
- "rxdata.sort_value = " << rxdata.sort_value);
-
- return lxdata.sort_value < rxdata.sort_value;
-}
-
-void entries_iterator::reset(session_t& session)
-{
- journals_i = session.journals.begin();
- journals_end = session.journals.end();
-
- journals_uninitialized = false;
-
- if (journals_i != journals_end) {
- entries_i = (*journals_i).entries.begin();
- entries_end = (*journals_i).entries.end();
-
- entries_uninitialized = false;
- } else {
- entries_uninitialized = true;
- }
-}
-
-entry_t * entries_iterator::operator()()
-{
- if (entries_i == entries_end) {
- journals_i++;
- if (journals_i == journals_end)
- return NULL;
-
- entries_i = (*journals_i).entries.begin();
- entries_end = (*journals_i).entries.end();
- }
- return *entries_i++;
-}
-
-void session_xacts_iterator::reset(session_t& session)
+pass_down_xacts::pass_down_xacts(xact_handler_ptr handler,
+ xacts_iterator& iter)
+ : item_handler<xact_t>(handler)
{
- entries.reset(session);
- entry_t * entry = entries();
- if (entry != NULL)
- xacts.reset(*entry);
-}
+ TRACE_CTOR(pass_down_xacts, "xact_handler_ptr, xacts_iterator");
-xact_t * session_xacts_iterator::operator()()
-{
- xact_t * xact = xacts();
- if (xact == NULL) {
- entry_t * entry = entries();
- if (entry != NULL) {
- xacts.reset(*entry);
- xact = xacts();
- }
- }
- return xact;
+ for (xact_t * xact = iter(); xact; xact = iter())
+ item_handler<xact_t>::operator()(*xact);
}
void truncate_entries::flush()
@@ -136,9 +98,8 @@ void truncate_entries::flush()
void set_account_value::operator()(xact_t& xact)
{
account_t * acct = xact.reported_account();
- assert(acct);
- account_xdata_t& xdata = account_xdata(*acct);
+ account_t::xdata_t& xdata(acct->xdata());
xact.add_to_value(xdata.value);
xdata.count++;
@@ -233,10 +194,10 @@ void handle_value(const value_t& value,
// the xact as such. This allows subtotal reports to show
// "(Account)" for accounts that contain only virtual xacts.
- if (account && account_has_xdata(*account))
- if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_NON_VIRTUALS)) {
+ if (account && account->has_xdata())
+ if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) {
xact.add_flags(XACT_VIRTUAL);
- if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_UNB_VIRTUALS))
+ if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS))
xact.add_flags(XACT_BALANCE);
}
@@ -459,9 +420,9 @@ void subtotal_xacts::operator()(xact_t& xact)
// that contain only virtual xacts.
if (! xact.has_flags(XACT_VIRTUAL))
- account_xdata(*xact.reported_account()).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
+ xact.reported_account()->xdata().add_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS);
else if (! xact.has_flags(XACT_BALANCE))
- account_xdata(*xact.reported_account()).dflags |= ACCOUNT_HAS_UNB_VIRTUALS;
+ xact.reported_account()->xdata().add_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS);
}
void interval_xacts::report_subtotal(const date_t& date)
@@ -781,158 +742,14 @@ void forecast_xacts::flush()
item_handler<xact_t>::flush();
}
-template <>
-bool compare_items<account_t>::operator()(account_t * left, account_t * right)
-{
- assert(left);
- assert(right);
-
- account_xdata_t& lxdata(account_xdata(*left));
- if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) {
- lxdata.sort_value = sort_order.calc(*left);
- lxdata.dflags |= ACCOUNT_SORT_CALC;
- }
-
- account_xdata_t& rxdata(account_xdata(*right));
- if (! (rxdata.dflags & ACCOUNT_SORT_CALC)) {
- rxdata.sort_value = sort_order.calc(*right);
- rxdata.dflags |= ACCOUNT_SORT_CALC;
- }
-
- return lxdata.sort_value < rxdata.sort_value;
-}
-
-account_xdata_t& account_xdata(const account_t& account)
-{
- if (! account.data)
- account.data = new account_xdata_t();
-
- return *static_cast<account_xdata_t *>(account.data);
-}
-
-void sum_accounts(account_t& account)
-{
- account_xdata_t& xdata(account_xdata(account));
-
- foreach (accounts_map::value_type& pair, account.accounts) {
- sum_accounts(*pair.second);
-
- xdata.total += account_xdata_(*pair.second).total;
- xdata.total_count += (account_xdata_(*pair.second).total_count +
- account_xdata_(*pair.second).count);
- }
-
- value_t result;
-#if 0
- compute_amount(result, details_t(account));
-#endif
- if (! result.is_realzero())
- xdata.total += result;
- xdata.total_count += xdata.count;
-}
-
-account_t * accounts_iterator::operator()()
-{
- while (! accounts_i.empty() &&
- accounts_i.back() == accounts_end.back()) {
- accounts_i.pop_back();
- accounts_end.pop_back();
- }
- if (accounts_i.empty())
- return NULL;
-
- account_t * account = (*(accounts_i.back()++)).second;
- assert(account);
-
- // If this account has children, queue them up to be iterated next.
- if (! account->accounts.empty())
- push_back(*account);
-
- return account;
-}
-
-void sorted_accounts_iterator::sort_accounts(account_t& account,
- accounts_deque_t& deque)
-{
- foreach (accounts_map::value_type& pair, account.accounts)
- deque.push_back(pair.second);
-
- std::stable_sort(deque.begin(), deque.end(),
- compare_items<account_t>(sort_cmp));
-}
-
-account_t * sorted_accounts_iterator::operator()()
-{
- while (! sorted_accounts_i.empty() &&
- sorted_accounts_i.back() == sorted_accounts_end.back()) {
- sorted_accounts_i.pop_back();
- sorted_accounts_end.pop_back();
- assert(! accounts_list.empty());
- accounts_list.pop_back();
- }
- if (sorted_accounts_i.empty())
- return NULL;
-
- account_t * account = *sorted_accounts_i.back()++;
- assert(account);
-
- // If this account has children, queue them up to be iterated next.
- if (! account->accounts.empty())
- push_back(*account);
-
- account_xdata(*account).dflags &= ~ACCOUNT_SORT_CALC;
- return account;
-}
-
-void walk_commodities(commodity_pool_t::commodities_by_ident& commodities,
- item_handler<xact_t>& handler)
-{
- std::list<xact_t> xact_temps;
- std::list<entry_t> entry_temps;
- std::list<account_t> acct_temps;
-
- for (commodity_pool_t::commodities_by_ident::iterator
- i = commodities.begin();
- i != commodities.end();
- i++) {
- if ((*i)->has_flags(COMMODITY_STYLE_NOMARKET))
- continue;
-
- entry_temps.push_back(entry_t());
- acct_temps.push_back(account_t(NULL, (*i)->symbol()));
-
- if ((*i)->history())
- foreach (const commodity_t::history_map::value_type& pair,
- (*i)->history()->prices) {
- entry_temps.back()._date = pair.first.date();
-
- xact_temps.push_back(xact_t(&acct_temps.back()));
- xact_t& temp = xact_temps.back();
- temp.entry = &entry_temps.back();
- temp.amount = pair.second;
- temp.add_flags(XACT_TEMP);
- entry_temps.back().add_xact(&temp);
-
- handler(xact_temps.back());
- }
- }
-
- handler.flush();
-
- clear_entries_xacts(entry_temps);
-}
-
-void journals_iterator::reset(session_t& session)
-{
- journals_i = session.journals.begin();
- journals_end = session.journals.end();
-}
-
-journal_t * journals_iterator::operator()()
+pass_down_accounts::pass_down_accounts(acct_handler_ptr handler,
+ accounts_iterator& iter)
+ : item_handler<account_t>(handler)
{
- if (journals_i == journals_end)
- return NULL;
- return &(*journals_i++);
+ TRACE_CTOR(pass_down_accounts,
+ "acct_handler_ptr, accounts_iterator");
+ for (account_t * account = iter(); account; account = iter())
+ item_handler<account_t>::operator()(*account);
}
} // namespace ledger
diff --git a/walk.h b/filters.h
index 1d449b2c..7f67cbd8 100644
--- a/walk.h
+++ b/filters.h
@@ -1,136 +1,47 @@
-#ifndef _WALK_H
-#define _WALK_H
-
-#include "journal.h"
+/*
+ * Copyright (c) 2003-2008, 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 _FILTERS_H
+#define _FILTERS_H
+
+#include "handler.h"
+#include "predicate.h"
#include "entry.h"
-#include "account.h"
namespace ledger {
-template <typename T>
-struct item_handler : public noncopyable
-{
- shared_ptr<item_handler> handler;
-
-public:
- item_handler() {
- TRACE_CTOR(item_handler, "");
- }
- item_handler(shared_ptr<item_handler> _handler) : handler(_handler) {
- TRACE_CTOR(item_handler, "shared_ptr<item_handler>");
- }
- virtual ~item_handler() {
- TRACE_DTOR(item_handler);
- }
-
- virtual void flush() {
- if (handler.get())
- handler->flush();
- }
- virtual void operator()(T& item) {
- if (handler.get())
- (*handler.get())(item);
- }
-};
-
-typedef shared_ptr<item_handler<xact_t> > xact_handler_ptr;
-
-//////////////////////////////////////////////////////////////////////
-
-class entries_iterator : public noncopyable
-{
- ptr_list<journal_t>::iterator journals_i;
- ptr_list<journal_t>::iterator journals_end;
-
- bool journals_uninitialized;
-
- entries_list::iterator entries_i;
- entries_list::iterator entries_end;
-
- bool entries_uninitialized;
-
-public:
- entries_iterator()
- : journals_uninitialized(true), entries_uninitialized(true) {
- TRACE_CTOR(entries_iterator, "");
- }
- entries_iterator(session_t& session)
- : journals_uninitialized(true), entries_uninitialized(true) {
- TRACE_CTOR(entries_iterator, "session_t&");
- reset(session);
- }
- ~entries_iterator() throw() {
- TRACE_DTOR(entries_iterator);
- }
-
- void reset(session_t& session);
-
- entry_t * operator()();
-};
-
-class xacts_iterator : public noncopyable
-{
-public:
- virtual xact_t * operator()() = 0;
-};
-
-class entry_xacts_iterator : public xacts_iterator
-{
- xacts_list::iterator xacts_i;
- xacts_list::iterator xacts_end;
-
- bool xacts_uninitialized;
-
-public:
- entry_xacts_iterator() : xacts_uninitialized(true) {
- TRACE_CTOR(entry_xacts_iterator, "");
- }
- entry_xacts_iterator(entry_t& entry)
- : xacts_uninitialized(true) {
- TRACE_CTOR(entry_xacts_iterator, "entry_t&");
- reset(entry);
- }
- virtual ~entry_xacts_iterator() throw() {
- TRACE_DTOR(entry_xacts_iterator);
- }
-
- void reset(entry_t& entry) {
- xacts_i = entry.xacts.begin();
- xacts_end = entry.xacts.end();
-
- xacts_uninitialized = false;
- }
-
- virtual xact_t * operator()() {
- if (xacts_i == xacts_end || xacts_uninitialized)
- return NULL;
- return *xacts_i++;
- }
-};
-
-class session_xacts_iterator : public xacts_iterator
-{
- entries_iterator entries;
- entry_xacts_iterator xacts;
-
-public:
- session_xacts_iterator() {
- TRACE_CTOR(session_xacts_iterator, "");
- }
- session_xacts_iterator(session_t& session) {
- TRACE_CTOR(session_xacts_iterator, "session_t&");
- reset(session);
- }
- virtual ~session_xacts_iterator() throw() {
- TRACE_DTOR(session_xacts_iterator);
- }
-
- void reset(session_t& session);
-
- virtual xact_t * operator()();
-};
-
//////////////////////////////////////////////////////////////////////
+//
+// Transaction filters
+//
class ignore_xacts : public item_handler<xact_t>
{
@@ -146,19 +57,14 @@ public:
}
};
+class xacts_iterator;
+
class pass_down_xacts : public item_handler<xact_t>
{
pass_down_xacts();
public:
- pass_down_xacts(xact_handler_ptr handler, xacts_iterator& iter)
- : item_handler<xact_t>(handler)
- {
- TRACE_CTOR(pass_down_xacts, "xact_handler_ptr, xacts_iterator");
-
- for (xact_t * xact = iter(); xact; xact = iter())
- item_handler<xact_t>::operator()(*xact);
- }
+ pass_down_xacts(xact_handler_ptr handler, xacts_iterator& iter);
virtual ~pass_down_xacts() {
TRACE_DTOR(pass_down_xacts);
@@ -265,7 +171,7 @@ public:
class sort_entries : public item_handler<xact_t>
{
sort_xacts sorter;
- entry_t * last_entry;
+ entry_t * last_entry;
sort_entries();
@@ -432,7 +338,7 @@ public:
class related_xacts : public item_handler<xact_t>
{
xacts_list xacts;
- bool also_matching;
+ bool also_matching;
related_xacts();
@@ -626,8 +532,8 @@ class by_payee_xacts : public item_handler<xact_t>
class set_comm_as_payee : public item_handler<xact_t>
{
- std::list<entry_t> entry_temps;
- std::list<xact_t> xact_temps;
+ std::list<entry_t> entry_temps;
+ std::list<xact_t> xact_temps;
set_comm_as_payee();
@@ -646,8 +552,8 @@ public:
class set_code_as_payee : public item_handler<xact_t>
{
- std::list<entry_t> entry_temps;
- std::list<xact_t> xact_temps;
+ std::list<entry_t> entry_temps;
+ std::list<xact_t> xact_temps;
set_code_as_payee();
@@ -694,9 +600,9 @@ protected:
typedef std::pair<interval_t, xact_t *> pending_xacts_pair;
typedef std::list<pending_xacts_pair> pending_xacts_list;
- pending_xacts_list pending_xacts;
- std::list<entry_t> entry_temps;
- std::list<xact_t> xact_temps;
+ pending_xacts_list pending_xacts;
+ std::list<entry_t> entry_temps;
+ std::list<xact_t> xact_temps;
public:
generate_xacts(xact_handler_ptr handler)
@@ -714,12 +620,12 @@ public:
virtual void add_xact(const interval_t& period, xact_t& xact);
};
+class budget_xacts : public generate_xacts
+{
#define BUDGET_NO_BUDGET 0x00
#define BUDGET_BUDGETED 0x01
#define BUDGET_UNBUDGETED 0x02
-class budget_xacts : public generate_xacts
-{
unsigned short flags;
budget_xacts();
@@ -764,204 +670,33 @@ class forecast_xacts : public generate_xacts
virtual void flush();
};
-
//////////////////////////////////////////////////////////////////////
//
-// Account walking functions
+// Account filters
//
-#define ACCOUNT_TO_DISPLAY 0x0001
-#define ACCOUNT_DISPLAYED 0x0002
-#define ACCOUNT_SORT_CALC 0x0004
-#define ACCOUNT_HAS_NON_VIRTUALS 0x0008
-#define ACCOUNT_HAS_UNB_VIRTUALS 0x0010
-
-struct account_xdata_t : public noncopyable
-{
- value_t value;
- value_t total;
- value_t sort_value;
- unsigned int count; // xacts counted toward amount
- unsigned int total_count; // xacts counted toward total
- unsigned int virtuals;
- unsigned short dflags;
-
- account_xdata_t() : count(0), total_count(0), virtuals(0), dflags(0) {
- TRACE_CTOR(account_xdata_t, "");
- }
- ~account_xdata_t() throw() {
- TRACE_DTOR(account_xdata_t);
- }
-};
-
-inline bool account_has_xdata(const account_t& account) {
- return account.data != NULL;
-}
-
-inline account_xdata_t& account_xdata_(const account_t& account) {
- return *static_cast<account_xdata_t *>(account.data);
-}
-
-account_xdata_t& account_xdata(const account_t& account);
-
-void sum_accounts(account_t& account);
-
-//////////////////////////////////////////////////////////////////////
-
-class accounts_iterator : public noncopyable
-{
- std::list<accounts_map::const_iterator> accounts_i;
- std::list<accounts_map::const_iterator> accounts_end;
-
-public:
- accounts_iterator() {
- TRACE_CTOR(accounts_iterator, "");
- }
- accounts_iterator(account_t& account) {
- TRACE_CTOR(accounts_iterator, "account_t&");
- push_back(account);
- }
- virtual ~accounts_iterator() throw() {
- TRACE_DTOR(accounts_iterator);
- }
-
- void push_back(account_t& account) {
- accounts_i.push_back(account.accounts.begin());
- accounts_end.push_back(account.accounts.end());
- }
-
- virtual account_t * operator()();
-};
-
-class sorted_accounts_iterator : public noncopyable
-{
- expr_t sort_cmp;
-
- typedef std::deque<account_t *> accounts_deque_t;
-
- std::list<accounts_deque_t> accounts_list;
- std::list<accounts_deque_t::const_iterator> sorted_accounts_i;
- std::list<accounts_deque_t::const_iterator> sorted_accounts_end;
-
-public:
- sorted_accounts_iterator(const string& sort_order) {
- TRACE_CTOR(sorted_accounts_iterator, "const string&");
- sort_cmp = expr_t(sort_order);
- }
- sorted_accounts_iterator(account_t& account, const string& sort_order) {
- TRACE_CTOR(sorted_accounts_iterator, "account_t&, const string&");
- sort_cmp = expr_t(sort_order);
- push_back(account);
- }
- virtual ~sorted_accounts_iterator() throw() {
- TRACE_DTOR(sorted_accounts_iterator);
- }
-
- void sort_accounts(account_t& account, accounts_deque_t& deque);
-
- void push_back(account_t& account) {
- accounts_list.push_back(accounts_deque_t());
- sort_accounts(account, accounts_list.back());
-
- sorted_accounts_i.push_back(accounts_list.back().begin());
- sorted_accounts_end.push_back(accounts_list.back().end());
- }
-
- virtual account_t * operator()();
-};
-
-//////////////////////////////////////////////////////////////////////
-
-typedef shared_ptr<item_handler<account_t> > acct_handler_ptr;
-
class clear_account_xdata : public item_handler<account_t>
{
public:
virtual void operator()(account_t& acct) {
- if (acct.data) {
- checked_delete(static_cast<account_xdata_t *>(acct.data));
- acct.data = NULL;
- }
+ acct.clear_xdata();
}
};
-template <typename Iterator>
+class accounts_iterator;
+
class pass_down_accounts : public item_handler<account_t>
{
pass_down_accounts();
public:
- pass_down_accounts(acct_handler_ptr handler, Iterator& iter)
- : item_handler<account_t>(handler) {
- TRACE_CTOR(pass_down_accounts,
- "acct_handler_ptr, accounts_iterator");
- for (account_t * account = iter(); account; account = iter())
- item_handler<account_t>::operator()(*account);
- }
+ pass_down_accounts(acct_handler_ptr handler, accounts_iterator& iter);
virtual ~pass_down_accounts() {
TRACE_DTOR(pass_down_accounts);
}
};
-//////////////////////////////////////////////////////////////////////
-
-class journals_iterator : public noncopyable
-{
- ptr_list<journal_t>::iterator journals_i;
- ptr_list<journal_t>::iterator journals_end;
-
-public:
- journals_iterator() {
- TRACE_CTOR(journals_iterator, "");
- }
- journals_iterator(session_t& session) {
- TRACE_CTOR(journals_iterator, "session_t&");
- reset(session);
- }
- virtual ~journals_iterator() throw() {
- TRACE_DTOR(journals_iterator);
- }
-
- void reset(session_t& session);
-
- virtual journal_t * operator()();
-};
-
-template <typename T>
-class compare_items
-{
- expr_t sort_order;
-
- compare_items();
-
-public:
- compare_items(const compare_items& other) : sort_order(other.sort_order) {
- TRACE_CTOR(compare_items, "copy");
- }
- compare_items(const expr_t& _sort_order) : sort_order(_sort_order) {
- TRACE_CTOR(compare_items, "const value_expr&");
- }
- ~compare_items() throw() {
- TRACE_DTOR(compare_items);
- }
- bool operator()(T * left, T * right);
-};
-
-template <typename T>
-bool compare_items<T>::operator()(T * left, T * right)
-{
- assert(left);
- assert(right);
- return sort_order.calc(*left) < sort_order.calc(*right);
-}
-
-template <>
-bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right);
-template <>
-bool compare_items<account_t>::operator()(account_t * left,
- account_t * right);
-
} // namespace ledger
-#endif // _WALK_H
+#endif // _FILTERS_H
diff --git a/format.cc b/format.cc
index 48cd7f7a..035f9268 100644
--- a/format.cc
+++ b/format.cc
@@ -30,10 +30,7 @@
*/
#include "format.h"
-#include "error.h"
-#include "util.h"
-
-#include <cstdlib>
+#include "account.h"
namespace ledger {
@@ -70,15 +67,15 @@ void format_t::element_t::dump(std::ostream& out) const
}
namespace {
- string partial_account_name(const account_t& account)
+ string partial_account_name(account_t& account)
{
string name;
- for (const account_t * acct = &account;
+ for (account_t * acct = &account;
acct && acct->parent;
acct = acct->parent) {
- if (account_has_xdata(*acct) &&
- account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED)
+ if (acct->has_xdata() &&
+ acct->xdata().has_flags(ACCOUNT_EXT_DISPLAYED))
break;
if (name.empty())
diff --git a/format.h b/format.h
index 6c96cedb..31f2bb1d 100644
--- a/format.h
+++ b/format.h
@@ -34,7 +34,6 @@
#include "journal.h"
#include "expr.h"
-#include "walk.h"
namespace ledger {
diff --git a/gnucash.cc b/gnucash.cc
index 176bfc41..41990cb0 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -30,24 +30,7 @@
*/
#include "gnucash.h"
-#include "journal.h"
-#include "format.h"
-#include "error.h"
-#include "acconf.h"
-
-#include <iostream>
-#include <sstream>
-#include <cstring>
-
-extern "C" {
-#if defined(HAVE_EXPAT)
-#include <expat.h> // expat XML parser
-#elif defined(HAVE_XMLPARSE)
-#include <xmlparse.h> // expat XML parser
-#else
-#error "No XML parser library defined."
-#endif
-}
+#include "account.h"
namespace ledger {
diff --git a/handler.h b/handler.h
new file mode 100644
index 00000000..6ebd6a5d
--- /dev/null
+++ b/handler.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2003-2008, 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 _HANDLER_H
+#define _HANDLER_H
+
+#include "utils.h"
+#include "xact.h"
+#include "account.h"
+
+namespace ledger {
+
+template <typename T>
+struct item_handler : public noncopyable
+{
+ shared_ptr<item_handler> handler;
+
+public:
+ item_handler() {
+ TRACE_CTOR(item_handler, "");
+ }
+ item_handler(shared_ptr<item_handler> _handler) : handler(_handler) {
+ TRACE_CTOR(item_handler, "shared_ptr<item_handler>");
+ }
+ virtual ~item_handler() {
+ TRACE_DTOR(item_handler);
+ }
+
+ virtual void flush() {
+ if (handler.get())
+ handler->flush();
+ }
+ virtual void operator()(T& item) {
+ if (handler.get())
+ (*handler.get())(item);
+ }
+};
+
+typedef shared_ptr<item_handler<xact_t> > xact_handler_ptr;
+typedef shared_ptr<item_handler<account_t> > acct_handler_ptr;
+
+} // namespace ledger
+
+#endif // _HANDLER_H
diff --git a/help.cc b/help.cc
new file mode 100644
index 00000000..84d5a178
--- /dev/null
+++ b/help.cc
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2003-2008, 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 "help.h"
+
+namespace ledger {
+
+void help(std::ostream& out)
+{
+ out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
+Use -H to see all the help text on one page, or:\n\
+ --help-calc calculation options\n\
+ --help-disp display options\n\
+ --help-comm commodity options\n\n\
+Basic options:\n\
+ -h, --help display this help text\n\
+ -v, --version show version information\n\
+ -f, --file FILE read ledger data from FILE\n\
+ -o, --output FILE write output to FILE\n\
+ -i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
+ --cache FILE use FILE as a binary cache when --file is not used\n\
+ --no-cache don't use a cache, even if it would be appropriate\n\
+ -a, --account NAME use NAME for the default account (useful with QIF)\n\n\
+Commands:\n\
+ balance [REGEXP]... show balance totals for matching accounts\n\
+ register [REGEXP]... show register of matching transactions\n\
+ print [REGEXP]... print all matching entries\n\
+ xml [REGEXP]... print matching entries in XML format\n\
+ equity [REGEXP]... output equity entries for matching accounts\n\
+ prices [REGEXP]... display price history for matching commodities\n\
+ entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
+}
+
+void calc_help(std::ostream& out)
+{
+ out << "Options to control how a report is calculated:\n\
+ -c, --current show only current and past entries (not future)\n\
+ -b, --begin DATE set report begin date\n\
+ -e, --end DATE set report end date\n\
+ -p, --period STR report using the given period\n\
+ --period-sort EXPR sort each report period's entries by EXPR\n\
+ -C, --cleared consider only cleared transactions\n\
+ -U, --uncleared consider only uncleared transactions\n\
+ -R, --real consider only real (non-virtual) transactions\n\
+ -L, --actual consider only actual (non-automated) transactions\n\
+ -r, --related calculate report using related transactions\n\
+ --budget generate budget entries based on periodic entries\n\
+ --add-budget show all transactions plus the budget\n\
+ --unbudgeted show only unbudgeted transactions\n\
+ --forecast EXPR generate forecast entries while EXPR is true\n\
+ -l, --limit EXPR calculate only transactions matching EXPR\n\
+ -t, --amount EXPR use EXPR to calculate the displayed amount\n\
+ -T, --total EXPR use EXPR to calculate the displayed total\n";
+}
+
+void disp_help(std::ostream& out)
+{
+ out << "Output to control how report results are displayed:\n\
+ -n, --collapse register: collapse entries; balance: no grand total\n\
+ -s, --subtotal balance: show sub-accounts; other: show subtotals\n\
+ -P, --by-payee show summarized totals by payee\n\
+ -x, --comm-as-payee set commodity name as the payee, for reporting\n\
+ -E, --empty balance: show accounts with zero balance\n\
+ -W, --weekly show weekly sub-totals\n\
+ -M, --monthly show monthly sub-totals\n\
+ -Y, --yearly show yearly sub-totals\n\
+ --dow show a days-of-the-week report\n\
+ -S, --sort EXPR sort report according to the value expression EXPR\n\
+ -w, --wide for the default register report, use 132 columns\n\
+ --head COUNT show only the first COUNT entries (negative inverts)\n\
+ --tail COUNT show only the last COUNT entries (negative inverts)\n\
+ --pager PAGER send all output through the given PAGER program\n\
+ -A, --average report average transaction amount\n\
+ -D, --deviation report deviation from the average\n\
+ -%, --percentage report balance totals as a percentile of the parent\n\
+ --totals in the \"xml\" report, include running total\n\
+ -j, --amount-data print only raw amount data (useful for scripting)\n\
+ -J, --total-data print only raw total data\n\
+ -d, --display EXPR display only transactions matching EXPR\n\
+ -y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
+ -F, --format STR use STR as the format; for each report type, use:\n\
+ --balance-format --register-format --print-format\n\
+ --plot-amount-format --plot-total-format --equity-format\n\
+ --prices-format --wide-register-format\n";
+}
+
+void comm_help(std::ostream& out)
+{
+ out << "Options to control how commodity values are determined:\n\
+ --price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
+ -Z, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
+ -Q, --download download price information when needed\n\
+ -O, --quantity report commodity totals (this is the default)\n\
+ -B, --basis report cost basis of commodities\n\
+ -V, --market report last known market value\n\
+ -g, --performance report gain/loss for each displayed transaction\n\
+ -G, --gain report net gain/loss\n";
+}
+
+void full_help(std::ostream& out)
+{
+ out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
+Basic options:\n\
+ -H, --full-help display this help text\n\
+ -h, --help display summarized help text\n\
+ -v, --version show version information\n\
+ -f, --file FILE read ledger data from FILE\n\
+ -o, --output FILE write output to FILE\n\
+ -i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
+ --cache FILE use FILE as a binary cache when --file is not used\n\
+ --no-cache don't use a cache, even if it would be appropriate\n\
+ -a, --account NAME use NAME for the default account (useful with QIF)\n\n\
+Report filtering:\n\
+ -c, --current show only current and past entries (not future)\n\
+ -b, --begin DATE set report begin date\n\
+ -e, --end DATE set report end date\n\
+ -p, --period STR report using the given period\n\
+ --period-sort EXPR sort each report period's entries by EXPR\n\
+ -C, --cleared consider only cleared transactions\n\
+ -U, --uncleared consider only uncleared transactions\n\
+ -R, --real consider only real (non-virtual) transactions\n\
+ -L, --actual consider only actual (non-automated) transactions\n\
+ -r, --related calculate report using related transactions\n\
+ --budget generate budget entries based on periodic entries\n\
+ --add-budget show all transactions plus the budget\n\
+ --unbudgeted show only unbudgeted transactions\n\
+ --forecast EXPR generate forecast entries while EXPR is true\n\
+ -l, --limit EXPR calculate only transactions matching EXPR\n\
+ -t, --amount EXPR use EXPR to calculate the displayed amount\n\
+ -T, --total EXPR use EXPR to calculate the displayed total\n\n\
+Output customization:\n\
+ -n, --collapse register: collapse entries; balance: no grand total\n\
+ -s, --subtotal balance: show sub-accounts; other: show subtotals\n\
+ -P, --by-payee show summarized totals by payee\n\
+ -x, --comm-as-payee set commodity name as the payee, for reporting\n\
+ -E, --empty balance: show accounts with zero balance\n\
+ -W, --weekly show weekly sub-totals\n\
+ -M, --monthly show monthly sub-totals\n\
+ -Y, --yearly show yearly sub-totals\n\
+ --dow show a days-of-the-week report\n\
+ -S, --sort EXPR sort report according to the value expression EXPR\n\
+ -w, --wide for the default register report, use 132 columns\n\
+ --head COUNT show only the first COUNT entries (negative inverts)\n\
+ --tail COUNT show only the last COUNT entries (negative inverts)\n\
+ --pager PAGER send all output through the given PAGER program\n\
+ -A, --average report average transaction amount\n\
+ -D, --deviation report deviation from the average\n\
+ -%, --percentage report balance totals as a percentile of the parent\n\
+ --totals in the \"xml\" report, include running total\n\
+ -j, --amount-data print only raw amount data (useful for scripting)\n\
+ -J, --total-data print only raw total data\n\
+ -d, --display EXPR display only transactions matching EXPR\n\
+ -y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
+ -F, --format STR use STR as the format; for each report type, use:\n\
+ --balance-format --register-format --print-format\n\
+ --plot-amount-format --plot-total-format --equity-format\n\
+ --prices-format --wide-register-format\n\n\
+Commodity reporting:\n\
+ --price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
+ -L, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
+ -Q, --download download price information when needed\n\
+ -O, --quantity report commodity totals (this is the default)\n\
+ -B, --basis report cost basis of commodities\n\
+ -V, --market report last known market value\n\
+ -g, --performance report gain/loss for each displayed transaction\n\
+ -G, --gain report net gain/loss\n\n\
+Commands:\n\
+ balance [REGEXP]... show balance totals for matching accounts\n\
+ register [REGEXP]... show register of matching transactions\n\
+ print [REGEXP]... print all matching entries\n\
+ xml [REGEXP]... print matching entries in XML format\n\
+ equity [REGEXP]... output equity entries for matching accounts\n\
+ prices [REGEXP]... display price history for matching commodities\n\
+ entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
+}
+
+} // namespace ledger
diff --git a/help.h b/help.h
new file mode 100644
index 00000000..14067be4
--- /dev/null
+++ b/help.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2003-2008, 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 _HELP_H
+#define _HELP_H
+
+#include "utils.h"
+
+namespace ledger {
+
+void help(std::ostream& out);
+
+void calc_help(std::ostream& out);
+void disp_help(std::ostream& out);
+void comm_help(std::ostream& out);
+
+void full_help(std::ostream& out);
+
+} // namespace ledger
+
+#endif // _HELP_H
diff --git a/iterators.cc b/iterators.cc
new file mode 100644
index 00000000..34beba7e
--- /dev/null
+++ b/iterators.cc
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2003-2008, 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 "iterators.h"
+#include "session.h"
+#include "compare.h"
+
+namespace ledger {
+
+void entries_iterator::reset(session_t& session)
+{
+ journals_i = session.journals.begin();
+ journals_end = session.journals.end();
+
+ journals_uninitialized = false;
+
+ if (journals_i != journals_end) {
+ entries_i = (*journals_i).entries.begin();
+ entries_end = (*journals_i).entries.end();
+
+ entries_uninitialized = false;
+ } else {
+ entries_uninitialized = true;
+ }
+}
+
+entry_t * entries_iterator::operator()()
+{
+ if (entries_i == entries_end) {
+ journals_i++;
+ if (journals_i == journals_end)
+ return NULL;
+
+ entries_i = (*journals_i).entries.begin();
+ entries_end = (*journals_i).entries.end();
+ }
+ return *entries_i++;
+}
+
+void session_xacts_iterator::reset(session_t& session)
+{
+ entries.reset(session);
+ entry_t * entry = entries();
+ if (entry != NULL)
+ xacts.reset(*entry);
+}
+
+xact_t * session_xacts_iterator::operator()()
+{
+ xact_t * xact = xacts();
+ if (xact == NULL) {
+ entry_t * entry = entries();
+ if (entry != NULL) {
+ xacts.reset(*entry);
+ xact = xacts();
+ }
+ }
+ return xact;
+}
+
+account_t * basic_accounts_iterator::operator()()
+{
+ while (! accounts_i.empty() &&
+ accounts_i.back() == accounts_end.back()) {
+ accounts_i.pop_back();
+ accounts_end.pop_back();
+ }
+ if (accounts_i.empty())
+ return NULL;
+
+ account_t * account = (*(accounts_i.back()++)).second;
+ assert(account);
+
+ // If this account has children, queue them up to be iterated next.
+ if (! account->accounts.empty())
+ push_back(*account);
+
+ return account;
+}
+
+void sorted_accounts_iterator::sort_accounts(account_t& account,
+ accounts_deque_t& deque)
+{
+ foreach (accounts_map::value_type& pair, account.accounts)
+ deque.push_back(pair.second);
+
+ std::stable_sort(deque.begin(), deque.end(),
+ compare_items<account_t>(sort_cmp));
+}
+
+account_t * sorted_accounts_iterator::operator()()
+{
+ while (! sorted_accounts_i.empty() &&
+ sorted_accounts_i.back() == sorted_accounts_end.back()) {
+ sorted_accounts_i.pop_back();
+ sorted_accounts_end.pop_back();
+ assert(! accounts_list.empty());
+ accounts_list.pop_back();
+ }
+ if (sorted_accounts_i.empty())
+ return NULL;
+
+ account_t * account = *sorted_accounts_i.back()++;
+ assert(account);
+
+ // If this account has children, queue them up to be iterated next.
+ if (! account->accounts.empty())
+ push_back(*account);
+
+ account->xdata().drop_flags(ACCOUNT_EXT_SORT_CALC);
+ return account;
+}
+
+void journals_iterator::reset(session_t& session)
+{
+ journals_i = session.journals.begin();
+ journals_end = session.journals.end();
+}
+
+journal_t * journals_iterator::operator()()
+{
+ if (journals_i == journals_end)
+ return NULL;
+ return &(*journals_i++);
+}
+
+#if 0
+// jww (2008-08-03): This needs to be changed into a commodities->xacts
+// iterator.
+
+// jww (2008-08-03): We then could also use a payees->xacts iterator
+
+void walk_commodities(commodity_pool_t::commodities_by_ident& commodities,
+ item_handler<xact_t>& handler)
+{
+ std::list<xact_t> xact_temps;
+ std::list<entry_t> entry_temps;
+ std::list<account_t> acct_temps;
+
+ for (commodity_pool_t::commodities_by_ident::iterator
+ i = commodities.begin();
+ i != commodities.end();
+ i++) {
+ if ((*i)->has_flags(COMMODITY_STYLE_NOMARKET))
+ continue;
+
+ entry_temps.push_back(entry_t());
+ acct_temps.push_back(account_t(NULL, (*i)->symbol()));
+
+ if ((*i)->history())
+ foreach (const commodity_t::history_map::value_type& pair,
+ (*i)->history()->prices) {
+ entry_temps.back()._date = pair.first.date();
+
+ xact_temps.push_back(xact_t(&acct_temps.back()));
+ xact_t& temp = xact_temps.back();
+ temp.entry = &entry_temps.back();
+ temp.amount = pair.second;
+ temp.add_flags(XACT_TEMP);
+ entry_temps.back().add_xact(&temp);
+
+ handler(xact_temps.back());
+ }
+ }
+
+ handler.flush();
+
+ clear_entries_xacts(entry_temps);
+}
+#endif
+
+} // namespace ledger
diff --git a/iterators.h b/iterators.h
new file mode 100644
index 00000000..c52a6827
--- /dev/null
+++ b/iterators.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2003-2008, 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 _ITERATORS_H
+#define _ITERATORS_H
+
+#include "journal.h"
+#include "entry.h"
+#include "account.h"
+
+namespace ledger {
+
+class xacts_iterator : public noncopyable
+{
+public:
+ virtual xact_t * operator()() = 0;
+};
+
+class entry_xacts_iterator : public xacts_iterator
+{
+ xacts_list::iterator xacts_i;
+ xacts_list::iterator xacts_end;
+
+ bool xacts_uninitialized;
+
+public:
+ entry_xacts_iterator() : xacts_uninitialized(true) {
+ TRACE_CTOR(entry_xacts_iterator, "");
+ }
+ entry_xacts_iterator(entry_t& entry)
+ : xacts_uninitialized(true) {
+ TRACE_CTOR(entry_xacts_iterator, "entry_t&");
+ reset(entry);
+ }
+ virtual ~entry_xacts_iterator() throw() {
+ TRACE_DTOR(entry_xacts_iterator);
+ }
+
+ void reset(entry_t& entry) {
+ xacts_i = entry.xacts.begin();
+ xacts_end = entry.xacts.end();
+
+ xacts_uninitialized = false;
+ }
+
+ virtual xact_t * operator()() {
+ if (xacts_i == xacts_end || xacts_uninitialized)
+ return NULL;
+ return *xacts_i++;
+ }
+};
+
+class entries_iterator : public noncopyable
+{
+ ptr_list<journal_t>::iterator journals_i;
+ ptr_list<journal_t>::iterator journals_end;
+
+ bool journals_uninitialized;
+
+ entries_list::iterator entries_i;
+ entries_list::iterator entries_end;
+
+ bool entries_uninitialized;
+
+public:
+ entries_iterator()
+ : journals_uninitialized(true), entries_uninitialized(true) {
+ TRACE_CTOR(entries_iterator, "");
+ }
+ entries_iterator(session_t& session)
+ : journals_uninitialized(true), entries_uninitialized(true) {
+ TRACE_CTOR(entries_iterator, "session_t&");
+ reset(session);
+ }
+ ~entries_iterator() throw() {
+ TRACE_DTOR(entries_iterator);
+ }
+
+ void reset(session_t& session);
+
+ entry_t * operator()();
+};
+
+class session_xacts_iterator : public xacts_iterator
+{
+ entries_iterator entries;
+ entry_xacts_iterator xacts;
+
+public:
+ session_xacts_iterator() {
+ TRACE_CTOR(session_xacts_iterator, "");
+ }
+ session_xacts_iterator(session_t& session) {
+ TRACE_CTOR(session_xacts_iterator, "session_t&");
+ reset(session);
+ }
+ virtual ~session_xacts_iterator() throw() {
+ TRACE_DTOR(session_xacts_iterator);
+ }
+
+ void reset(session_t& session);
+
+ virtual xact_t * operator()();
+};
+
+class accounts_iterator : public noncopyable
+{
+public:
+ virtual account_t * operator()() = 0;
+};
+
+class basic_accounts_iterator : public accounts_iterator
+{
+ std::list<accounts_map::const_iterator> accounts_i;
+ std::list<accounts_map::const_iterator> accounts_end;
+
+public:
+ basic_accounts_iterator() {
+ TRACE_CTOR(basic_accounts_iterator, "");
+ }
+ basic_accounts_iterator(account_t& account) {
+ TRACE_CTOR(basic_accounts_iterator, "account_t&");
+ push_back(account);
+ }
+ virtual ~basic_accounts_iterator() throw() {
+ TRACE_DTOR(basic_accounts_iterator);
+ }
+
+ void push_back(account_t& account) {
+ accounts_i.push_back(account.accounts.begin());
+ accounts_end.push_back(account.accounts.end());
+ }
+
+ virtual account_t * operator()();
+};
+
+class sorted_accounts_iterator : public accounts_iterator
+{
+ expr_t sort_cmp;
+
+ typedef std::deque<account_t *> accounts_deque_t;
+
+ std::list<accounts_deque_t> accounts_list;
+ std::list<accounts_deque_t::const_iterator> sorted_accounts_i;
+ std::list<accounts_deque_t::const_iterator> sorted_accounts_end;
+
+public:
+ sorted_accounts_iterator(const string& sort_order) {
+ TRACE_CTOR(sorted_accounts_iterator, "const string&");
+ sort_cmp = expr_t(sort_order);
+ }
+ sorted_accounts_iterator(account_t& account, const string& sort_order) {
+ TRACE_CTOR(sorted_accounts_iterator, "account_t&, const string&");
+ sort_cmp = expr_t(sort_order);
+ push_back(account);
+ }
+ virtual ~sorted_accounts_iterator() throw() {
+ TRACE_DTOR(sorted_accounts_iterator);
+ }
+
+ void sort_accounts(account_t& account, accounts_deque_t& deque);
+
+ void push_back(account_t& account) {
+ accounts_list.push_back(accounts_deque_t());
+ sort_accounts(account, accounts_list.back());
+
+ sorted_accounts_i.push_back(accounts_list.back().begin());
+ sorted_accounts_end.push_back(accounts_list.back().end());
+ }
+
+ virtual account_t * operator()();
+};
+
+class journals_iterator : public noncopyable
+{
+ ptr_list<journal_t>::iterator journals_i;
+ ptr_list<journal_t>::iterator journals_end;
+
+public:
+ journals_iterator() {
+ TRACE_CTOR(journals_iterator, "");
+ }
+ journals_iterator(session_t& session) {
+ TRACE_CTOR(journals_iterator, "session_t&");
+ reset(session);
+ }
+ virtual ~journals_iterator() throw() {
+ TRACE_DTOR(journals_iterator);
+ }
+
+ void reset(session_t& session);
+
+ virtual journal_t * operator()();
+};
+
+} // namespace ledger
+
+#endif // _ITERATORS_H
diff --git a/main.cc b/main.cc
index 1a6f0ea2..e3a3544a 100644
--- a/main.cc
+++ b/main.cc
@@ -29,15 +29,21 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "utils.h"
+#include "session.h"
+#include "report.h"
#include "option.h"
+#include "output.h"
+#include "help.h"
+
+#include "textual.h"
+#include "qif.h"
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+#include "xml.h"
#include "gnucash.h"
#endif
-#include "qif.h"
+#ifdef HAVE_LIBOFX
#include "ofx.h"
-
-#include <ledger.h>
+#endif
#ifdef HAVE_UNIX_PIPES
#include <sys/types.h>
@@ -46,352 +52,351 @@
#endif
namespace ledger {
- value_t register_command(call_scope_t& args)
+ template <class Formatter = format_xacts>
+ class xacts_report
{
- ptr_t<report_t> report(args, 0);
- ptr_t<std::ostream> ostream(args, 1);
+ string format_name;
- report->xacts_report
- (xact_handler_ptr(new format_xacts
- (*ostream, report->session.register_format)));
- return true;
- }
-}
+ public:
+ xacts_report(const string& _format_name)
+ : format_name(_format_name) {}
-static int read_and_report(ledger::report_t& report, int argc, char * argv[],
- char * envp[])
-{
- using namespace ledger;
+ value_t operator()(call_scope_t& args)
+ {
+ ptr_t<std::ostream> ostream(args, 0);
+ var_t<string> format(args, format_name);
+
+ find_scope<report_t>(args).xacts_report
+ (xact_handler_ptr(new Formatter(*ostream, *format)));
+ return true;
+ }
+ };
- session_t& session(report.session);
+ template <class Formatter = format_accounts>
+ class accounts_report
+ {
+ string format_name;
- // Handle the command-line arguments
+ public:
+ accounts_report(const string& _format_name)
+ : format_name(_format_name) {}
- strings_list args;
- process_arguments(argc - 1, argv + 1, false, report, args);
+ value_t operator()(call_scope_t& args)
+ {
+ ptr_t<std::ostream> ostream(args, 0);
+ var_t<string> format(args, format_name);
- if (args.empty()) {
-#if 0
- help(std::cerr);
-#endif
- return 1;
- }
- strings_list::iterator arg = args.begin();
+ find_scope<report_t>(args).accounts_report
+ (acct_handler_ptr(new Formatter(*ostream, *format)));
+ return true;
+ }
+ };
- if (! session.cache_file)
- session.use_cache = false;
- else
- session.use_cache = ! session.data_file.empty() && session.price_db;
+ int read_and_report(ledger::report_t& report,
+ int argc, char * argv[], char * envp[])
+ {
+ using namespace ledger;
- DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
+ session_t& session(report.session);
- // Process the environment settings
+ // Handle the command-line arguments
+
+ strings_list args;
+ process_arguments(argc - 1, argv + 1, false, report, args);
+
+ if (args.empty()) {
+ ledger::help(std::cout);
+ return 1;
+ }
+ strings_list::iterator arg = args.begin();
+
+ if (! session.cache_file)
+ session.use_cache = false;
+ else
+ session.use_cache = ! session.data_file.empty() && session.price_db;
- TRACE_START(environment, 1, "Processed environment variables");
- process_environment(const_cast<const char **>(envp), "LEDGER_", report);
- TRACE_FINISH(environment, 1);
+ DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
- optional<path> home;
- if (const char * home_var = std::getenv("HOME"))
- home = home_var;
+ // Process the environment settings
- if (! session.init_file)
- session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
- if (! session.price_db)
- session.price_db = home ? *home / ".pricedb" : "./.pricedb";
+ TRACE_START(environment, 1, "Processed environment variables");
+ process_environment(const_cast<const char **>(envp), "LEDGER_", report);
+ TRACE_FINISH(environment, 1);
- if (! session.cache_file)
- session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
+ optional<path> home;
+ if (const char * home_var = std::getenv("HOME"))
+ home = home_var;
- if (session.data_file == *session.cache_file)
- session.use_cache = false;
+ if (! session.init_file)
+ session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
+ if (! session.price_db)
+ session.price_db = home ? *home / ".pricedb" : "./.pricedb";
- DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
+ if (! session.cache_file)
+ session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
- INFO("Initialization file is " << session.init_file->string());
- INFO("Price database is " << session.price_db->string());
- INFO("Binary cache is " << session.cache_file->string());
- INFO("Journal file is " << session.data_file.string());
+ if (session.data_file == *session.cache_file)
+ session.use_cache = false;
- if (! session.use_cache)
- INFO("Binary cache mechanism will not be used");
+ DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
- // Configure the output stream
+ INFO("Initialization file is " << session.init_file->string());
+ INFO("Price database is " << session.price_db->string());
+ INFO("Binary cache is " << session.cache_file->string());
+ INFO("Journal file is " << session.data_file.string());
+
+ if (! session.use_cache)
+ INFO("Binary cache mechanism will not be used");
+
+ // Configure the output stream
#ifdef HAVE_UNIX_PIPES
- int status, pfd[2]; // Pipe file descriptors
+ int status, pfd[2]; // Pipe file descriptors
#endif
- std::ostream * out = &std::cout;
+ std::ostream * out = &std::cout;
- if (report.output_file) {
- out = new ofstream(*report.output_file);
- }
-#ifdef HAVE_UNIX_PIPES
- else if (report.pager) {
- status = pipe(pfd);
- if (status == -1)
- throw_(std::logic_error, "Failed to create pipe");
-
- status = fork();
- if (status < 0) {
- throw_(std::logic_error, "Failed to fork child process");
+ if (report.output_file) {
+ out = new ofstream(*report.output_file);
}
- else if (status == 0) { // child
- // Duplicate pipe's reading end into stdin
- status = dup2(pfd[0], STDIN_FILENO);
+#ifdef HAVE_UNIX_PIPES
+ else if (report.pager) {
+ status = pipe(pfd);
if (status == -1)
- perror("dup2");
+ throw_(std::logic_error, "Failed to create pipe");
- // Close unuseful file descriptors: the pipe's writing and
- // reading ends (the latter is not needed anymore, after the
- // duplication).
- close(pfd[1]);
- close(pfd[0]);
-
- // Find command name: its the substring starting right of the
- // rightmost '/' character in the pager pathname. See manpage
- // for strrchr.
- execlp(report.pager->native_file_string().c_str(),
- basename(*report.pager).c_str(), NULL);
- perror("execl");
- exit(1);
- }
- else { // parent
- close(pfd[0]);
- out = new boost::fdostream(pfd[1]);
+ status = fork();
+ if (status < 0) {
+ throw_(std::logic_error, "Failed to fork child process");
+ }
+ else if (status == 0) { // child
+ // Duplicate pipe's reading end into stdin
+ status = dup2(pfd[0], STDIN_FILENO);
+ if (status == -1)
+ perror("dup2");
+
+ // Close unuseful file descriptors: the pipe's writing and
+ // reading ends (the latter is not needed anymore, after the
+ // duplication).
+ close(pfd[1]);
+ close(pfd[0]);
+
+ // Find command name: its the substring starting right of the
+ // rightmost '/' character in the pager pathname. See manpage
+ // for strrchr.
+ execlp(report.pager->native_file_string().c_str(),
+ basename(*report.pager).c_str(), NULL);
+ perror("execl");
+ exit(1);
+ }
+ else { // parent
+ close(pfd[0]);
+ out = new boost::fdostream(pfd[1]);
+ }
}
- }
#endif
- // Read the command word and create a command object based on it
+ // Read the command word and see if it's any of the debugging commands
+ // that Ledger supports.
- string verb = *arg++;
+ string verb = *arg++;
- if (verb == "parse") {
- expr_t expr(*arg);
+ if (verb == "parse") {
+ expr_t expr(*arg);
- *out << "Value expression as input: " << *arg << std::endl;
+ *out << "Value expression as input: " << *arg << std::endl;
- *out << "Value expression as parsed: ";
- expr.print(*out, report);
- *out << std::endl;
-
- *out << std::endl;
- *out << "--- Parsed tree ---" << std::endl;
- expr.dump(*out);
-
- *out << std::endl;
- *out << "--- Calculated value ---" << std::endl;
- expr.calc(report).print(*out);
- *out << std::endl;
-
- return 0;
- }
- else if (verb == "compile") {
- expr_t expr(*arg);
-
- *out << "Value expression as input: " << *arg << std::endl;
- *out << "Value expression as parsed: ";
- expr.print(*out, report);
- *out << std::endl;
+ *out << "Value expression as parsed: ";
+ expr.print(*out, report);
+ *out << std::endl;
- *out << std::endl;
- *out << "--- Parsed tree ---" << std::endl;
- expr.dump(*out);
+ *out << std::endl;
+ *out << "--- Parsed tree ---" << std::endl;
+ expr.dump(*out);
- expr.compile(report);
+ *out << std::endl;
+ *out << "--- Calculated value ---" << std::endl;
+ expr.calc(report).print(*out);
+ *out << std::endl;
- *out << std::endl;
- *out << "--- Compiled tree ---" << std::endl;
- expr.dump(*out);
+ return 0;
+ }
+ else if (verb == "compile") {
+ expr_t expr(*arg);
- *out << std::endl;
- *out << "--- Calculated value ---" << std::endl;
- expr.calc(report).print(*out);
- *out << std::endl;
+ *out << "Value expression as input: " << *arg << std::endl;
+ *out << "Value expression as parsed: ";
+ expr.print(*out, report);
+ *out << std::endl;
- return 0;
- }
- else if (verb == "eval") {
- expr_t expr(*arg);
- *out << expr.calc(report).strip_annotations() << std::endl;
- return 0;
- }
- else if (verb == "format") {
- format_t fmt(*arg);
- fmt.dump(*out);
- return 0;
- }
- else if (verb == "period") {
- interval_t interval(*arg);
-
- if (! is_valid(interval.begin)) {
- *out << "Time period has no beginning." << std::endl;
- } else {
- *out << "begin: " << format_date(interval.begin) << std::endl;
- *out << " end: " << format_date(interval.end) << std::endl;
*out << std::endl;
+ *out << "--- Parsed tree ---" << std::endl;
+ expr.dump(*out);
- date_t date = interval.first();
+ expr.compile(report);
- for (int i = 0; i < 20; i++) {
- *out << std::right;
- out->width(2);
+ *out << std::endl;
+ *out << "--- Compiled tree ---" << std::endl;
+ expr.dump(*out);
- *out << i << ": " << format_date(date) << std::endl;
+ *out << std::endl;
+ *out << "--- Calculated value ---" << std::endl;
+ expr.calc(report).print(*out);
+ *out << std::endl;
- date = interval.increment(date);
- if (is_valid(interval.end) && date >= interval.end)
- break;
- }
+ return 0;
}
- return 0;
- }
+ else if (verb == "eval") {
+ expr_t expr(*arg);
+ *out << expr.calc(report).strip_annotations() << std::endl;
+ return 0;
+ }
+ else if (verb == "format") {
+ format_t fmt(*arg);
+ fmt.dump(*out);
+ return 0;
+ }
+ else if (verb == "period") {
+ interval_t interval(*arg);
- // Parse the initialization file, which can only be textual; then
- // parse the journal data.
+ if (! is_valid(interval.begin)) {
+ *out << "Time period has no beginning." << std::endl;
+ } else {
+ *out << "begin: " << format_date(interval.begin) << std::endl;
+ *out << " end: " << format_date(interval.end) << std::endl;
+ *out << std::endl;
- session.read_init();
+ date_t date = interval.first();
- INFO_START(journal, "Read journal file");
+ for (int i = 0; i < 20; i++) {
+ *out << std::right;
+ out->width(2);
- journal_t& journal(*session.create_journal());
+ *out << i << ": " << format_date(date) << std::endl;
- std::size_t count = session.read_data(journal, report.account);
- if (count == 0)
- throw_(parse_error, "Failed to locate any journal entries; "
- "did you specify a valid file with -f?");
+ date = interval.increment(date);
+ if (is_valid(interval.end) && date >= interval.end)
+ break;
+ }
+ }
+ return 0;
+ }
- INFO_FINISH(journal);
+ // Parse the initialization file, which can only be textual; then
+ // parse the journal data.
- INFO("Found " << count << " entries");
+ session.read_init();
- TRACE_FINISH(entry_text, 1);
- TRACE_FINISH(entry_date, 1);
- TRACE_FINISH(entry_details, 1);
- TRACE_FINISH(entry_xacts, 1);
- TRACE_FINISH(entries, 1);
- TRACE_FINISH(parsing_total, 1);
+ INFO_START(journal, "Read journal file");
- // Are we handling the expr commands? Do so now.
+ journal_t& journal(*session.create_journal());
- if (verb == "expr") {
- expr_t expr(*arg);
+ std::size_t count = session.read_data(journal, report.account);
+ if (count == 0)
+ throw_(parse_error, "Failed to locate any journal entries; "
+ "did you specify a valid file with -f?");
- IF_INFO() {
- *out << "Value expression tree:" << std::endl;
- expr.dump(*out);
- *out << std::endl;
- *out << "Value expression parsed was:" << std::endl;
- expr.print(*out, report);
- *out << std::endl << std::endl;
- *out << "Result of calculation: ";
- }
+ INFO_FINISH(journal);
- *out << expr.calc(report).strip_annotations() << std::endl;
+ INFO("Found " << count << " entries");
- return 0;
- }
+ TRACE_FINISH(entry_text, 1);
+ TRACE_FINISH(entry_date, 1);
+ TRACE_FINISH(entry_details, 1);
+ TRACE_FINISH(entry_xacts, 1);
+ TRACE_FINISH(entries, 1);
+ TRACE_FINISH(parsing_total, 1);
- // Read the command word and create a command object based on it
+ // Create a command object based on the command verb that was seen
+ // above.
- function_t command;
+ function_t command;
- if (verb == "register" || verb == "reg" || verb == "r")
- command = register_command;
+ if (verb == "register" || verb == "reg" || verb == "r")
+ command = xacts_report<>("register_format");
+ else if (verb == "print" || verb == "p")
+ command = xacts_report<>("print_format");
+ else if (verb == "balance" || verb == "bal" || verb == "b")
+ command = accounts_report<>("balance_format");
+ else if (verb == "equity")
+ command = accounts_report<format_equity>("print_format");
#if 0
- else if (verb == "balance" || verb == "bal" || verb == "b")
- command = balance_command();
- else if (verb == "print" || verb == "p")
- command = print_command();
- else if (verb == "equity")
- command = equity_command();
- else if (verb == "entry")
- command = entry_command();
- else if (verb == "dump")
- command = dump_command();
- else if (verb == "output")
- command = output_command();
- else if (verb == "prices")
- command = prices_command();
- else if (verb == "pricesdb")
- command = pricesdb_command();
- else if (verb == "csv")
- command = csv_command();
- else if (verb == "emacs" || verb == "lisp")
- command = emacs_command();
- else if (verb == "xml")
- command = xml_command();
+ else if (verb == "entry")
+ command = entry_command();
+ else if (verb == "dump")
+ command = dump_command();
+ else if (verb == "output")
+ command = output_command();
+ else if (verb == "prices")
+ command = prices_command();
+ else if (verb == "pricesdb")
+ command = pricesdb_command();
+ else if (verb == "csv")
+ command = csv_command();
+ else if (verb == "emacs" || verb == "lisp")
+ command = emacs_command();
+ else if (verb == "xml")
+ command = xml_command();
#endif
- else if (verb == "expr")
- ;
- else if (verb == "xpath")
- ;
- else {
- char buf[128];
- std::strcpy(buf, "command_");
- std::strcat(buf, verb.c_str());
-
- if (expr_t::ptr_op_t def = report.lookup(buf))
- command = def->as_function();
-
- if (! command)
- throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
- }
+ else {
+ char buf[128];
+ std::strcpy(buf, "cmd_");
+ std::strcat(buf, verb.c_str());
- // Create an argument scope containing the report command's
- // arguments, and then invoke the command.
+ if (expr_t::ptr_op_t def = report.lookup(buf))
+ command = def->as_function();
- call_scope_t command_args(report);
+ if (! command)
+ throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
+ }
- command_args.push_back(value_t(&report));
- command_args.push_back(value_t(out));
+ // Create an argument scope containing the report command's
+ // arguments, and then invoke the command.
- for (strings_list::iterator i = arg; i != args.end(); i++)
- command_args.push_back(string_value(*i));
+ call_scope_t command_args(report);
- INFO_START(command, "Did user command '" << verb << "'");
+ command_args.push_back(value_t(out));
- command(command_args);
+ for (strings_list::iterator i = arg; i != args.end(); i++)
+ command_args.push_back(string_value(*i));
- INFO_FINISH(command);
+ INFO_START(command, "Did user command '" << verb << "'");
- // Clean up memory, if it matters
+ command(command_args);
- if (DO_VERIFY() && report.output_file)
- checked_delete(out);
+ INFO_FINISH(command);
#if 0
- // Write out the binary cache, if need be
+ // Write out the binary cache, if need be
- if (session.use_cache && session.cache_dirty && session.cache_file) {
- TRACE_START(binary_cache, 1, "Wrote binary journal file");
+ if (session.use_cache && session.cache_dirty && session.cache_file) {
+ TRACE_START(binary_cache, 1, "Wrote binary journal file");
- ofstream stream(*session.cache_file);
- journal.write(stream);
+ ofstream stream(*session.cache_file);
+ journal.write(stream);
- TRACE_FINISH(binary_cache, 1);
- }
+ TRACE_FINISH(binary_cache, 1);
+ }
#endif
- // If the user specified a pager, wait for it to exit now
+ // If the user specified a pager, wait for it to exit now
#ifdef HAVE_UNIX_PIPES
- if (! report.output_file && report.pager) {
- checked_delete(out);
- close(pfd[1]);
-
- // Wait for child to finish
- wait(&status);
- if (status & 0xffff != 0)
- throw_(std::logic_error, "Something went wrong in the pager");
- }
+ if (! report.output_file && report.pager) {
+ checked_delete(out);
+ close(pfd[1]);
+
+ // Wait for child to finish
+ wait(&status);
+ if (status & 0xffff != 0)
+ throw_(std::logic_error, "Something went wrong in the pager");
+ }
#endif
- else if (DO_VERIFY() && report.output_file) {
- checked_delete(out);
- }
+ else if (DO_VERIFY() && report.output_file) {
+ checked_delete(out);
+ }
- return 0;
+ return 0;
+ }
}
int main(int argc, char * argv[], char * envp[])
@@ -469,7 +474,7 @@ int main(int argc, char * argv[], char * envp[])
if (DO_VERIFY())
ledger::set_session_context();
else
- session.release(); // don't free anything!
+ session.release(); // don't free anything! just let it leak
}
catch (const std::exception& err) {
std::cout.flush();
diff --git a/option.cc b/option.cc
index e2a742da..e217d1d0 100644
--- a/option.cc
+++ b/option.cc
@@ -212,175 +212,6 @@ void process_arguments(int, char ** argv, const bool anywhere,
} // namespace ledger
#if 0
-void option_full_help(std::ostream& out)
-{
- out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
-Basic options:\n\
- -H, --full-help display this help text\n\
- -h, --help display summarized help text\n\
- -v, --version show version information\n\
- -f, --file FILE read ledger data from FILE\n\
- -o, --output FILE write output to FILE\n\
- -i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
- --cache FILE use FILE as a binary cache when --file is not used\n\
- --no-cache don't use a cache, even if it would be appropriate\n\
- -a, --account NAME use NAME for the default account (useful with QIF)\n\n\
-Report filtering:\n\
- -c, --current show only current and past entries (not future)\n\
- -b, --begin DATE set report begin date\n\
- -e, --end DATE set report end date\n\
- -p, --period STR report using the given period\n\
- --period-sort EXPR sort each report period's entries by EXPR\n\
- -C, --cleared consider only cleared transactions\n\
- -U, --uncleared consider only uncleared transactions\n\
- -R, --real consider only real (non-virtual) transactions\n\
- -L, --actual consider only actual (non-automated) transactions\n\
- -r, --related calculate report using related transactions\n\
- --budget generate budget entries based on periodic entries\n\
- --add-budget show all transactions plus the budget\n\
- --unbudgeted show only unbudgeted transactions\n\
- --forecast EXPR generate forecast entries while EXPR is true\n\
- -l, --limit EXPR calculate only transactions matching EXPR\n\
- -t, --amount EXPR use EXPR to calculate the displayed amount\n\
- -T, --total EXPR use EXPR to calculate the displayed total\n\n\
-Output customization:\n\
- -n, --collapse register: collapse entries; balance: no grand total\n\
- -s, --subtotal balance: show sub-accounts; other: show subtotals\n\
- -P, --by-payee show summarized totals by payee\n\
- -x, --comm-as-payee set commodity name as the payee, for reporting\n\
- -E, --empty balance: show accounts with zero balance\n\
- -W, --weekly show weekly sub-totals\n\
- -M, --monthly show monthly sub-totals\n\
- -Y, --yearly show yearly sub-totals\n\
- --dow show a days-of-the-week report\n\
- -S, --sort EXPR sort report according to the value expression EXPR\n\
- -w, --wide for the default register report, use 132 columns\n\
- --head COUNT show only the first COUNT entries (negative inverts)\n\
- --tail COUNT show only the last COUNT entries (negative inverts)\n\
- --pager PAGER send all output through the given PAGER program\n\
- -A, --average report average transaction amount\n\
- -D, --deviation report deviation from the average\n\
- -%, --percentage report balance totals as a percentile of the parent\n\
- --totals in the \"xml\" report, include running total\n\
- -j, --amount-data print only raw amount data (useful for scripting)\n\
- -J, --total-data print only raw total data\n\
- -d, --display EXPR display only transactions matching EXPR\n\
- -y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
- -F, --format STR use STR as the format; for each report type, use:\n\
- --balance-format --register-format --print-format\n\
- --plot-amount-format --plot-total-format --equity-format\n\
- --prices-format --wide-register-format\n\n\
-Commodity reporting:\n\
- --price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
- -L, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
- -Q, --download download price information when needed\n\
- -O, --quantity report commodity totals (this is the default)\n\
- -B, --basis report cost basis of commodities\n\
- -V, --market report last known market value\n\
- -g, --performance report gain/loss for each displayed transaction\n\
- -G, --gain report net gain/loss\n\n\
-Commands:\n\
- balance [REGEXP]... show balance totals for matching accounts\n\
- register [REGEXP]... show register of matching transactions\n\
- print [REGEXP]... print all matching entries\n\
- xml [REGEXP]... print matching entries in XML format\n\
- equity [REGEXP]... output equity entries for matching accounts\n\
- prices [REGEXP]... display price history for matching commodities\n\
- entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
-}
-
-void option_help(std::ostream& out)
-{
- out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
-Use -H to see all the help text on one page, or:\n\
- --help-calc calculation options\n\
- --help-disp display options\n\
- --help-comm commodity options\n\n\
-Basic options:\n\
- -h, --help display this help text\n\
- -v, --version show version information\n\
- -f, --file FILE read ledger data from FILE\n\
- -o, --output FILE write output to FILE\n\
- -i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
- --cache FILE use FILE as a binary cache when --file is not used\n\
- --no-cache don't use a cache, even if it would be appropriate\n\
- -a, --account NAME use NAME for the default account (useful with QIF)\n\n\
-Commands:\n\
- balance [REGEXP]... show balance totals for matching accounts\n\
- register [REGEXP]... show register of matching transactions\n\
- print [REGEXP]... print all matching entries\n\
- xml [REGEXP]... print matching entries in XML format\n\
- equity [REGEXP]... output equity entries for matching accounts\n\
- prices [REGEXP]... display price history for matching commodities\n\
- entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
-}
-
-void option_calc_help(std::ostream& out)
-{
- out << "Options to control how a report is calculated:\n\
- -c, --current show only current and past entries (not future)\n\
- -b, --begin DATE set report begin date\n\
- -e, --end DATE set report end date\n\
- -p, --period STR report using the given period\n\
- --period-sort EXPR sort each report period's entries by EXPR\n\
- -C, --cleared consider only cleared transactions\n\
- -U, --uncleared consider only uncleared transactions\n\
- -R, --real consider only real (non-virtual) transactions\n\
- -L, --actual consider only actual (non-automated) transactions\n\
- -r, --related calculate report using related transactions\n\
- --budget generate budget entries based on periodic entries\n\
- --add-budget show all transactions plus the budget\n\
- --unbudgeted show only unbudgeted transactions\n\
- --forecast EXPR generate forecast entries while EXPR is true\n\
- -l, --limit EXPR calculate only transactions matching EXPR\n\
- -t, --amount EXPR use EXPR to calculate the displayed amount\n\
- -T, --total EXPR use EXPR to calculate the displayed total\n";
-}
-
-void option_disp_help(std::ostream& out)
-{
- out << "Output to control how report results are displayed:\n\
- -n, --collapse register: collapse entries; balance: no grand total\n\
- -s, --subtotal balance: show sub-accounts; other: show subtotals\n\
- -P, --by-payee show summarized totals by payee\n\
- -x, --comm-as-payee set commodity name as the payee, for reporting\n\
- -E, --empty balance: show accounts with zero balance\n\
- -W, --weekly show weekly sub-totals\n\
- -M, --monthly show monthly sub-totals\n\
- -Y, --yearly show yearly sub-totals\n\
- --dow show a days-of-the-week report\n\
- -S, --sort EXPR sort report according to the value expression EXPR\n\
- -w, --wide for the default register report, use 132 columns\n\
- --head COUNT show only the first COUNT entries (negative inverts)\n\
- --tail COUNT show only the last COUNT entries (negative inverts)\n\
- --pager PAGER send all output through the given PAGER program\n\
- -A, --average report average transaction amount\n\
- -D, --deviation report deviation from the average\n\
- -%, --percentage report balance totals as a percentile of the parent\n\
- --totals in the \"xml\" report, include running total\n\
- -j, --amount-data print only raw amount data (useful for scripting)\n\
- -J, --total-data print only raw total data\n\
- -d, --display EXPR display only transactions matching EXPR\n\
- -y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
- -F, --format STR use STR as the format; for each report type, use:\n\
- --balance-format --register-format --print-format\n\
- --plot-amount-format --plot-total-format --equity-format\n\
- --prices-format --wide-register-format\n";
-}
-
-void option_comm_help(std::ostream& out)
-{
- out << "Options to control how commodity values are determined:\n\
- --price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
- -Z, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
- -Q, --download download price information when needed\n\
- -O, --quantity report commodity totals (this is the default)\n\
- -B, --basis report cost basis of commodities\n\
- -V, --market report last known market value\n\
- -g, --performance report gain/loss for each displayed transaction\n\
- -G, --gain report net gain/loss\n";
-}
-
//////////////////////////////////////////////////////////////////////
//
// Basic options
diff --git a/output.cc b/output.cc
new file mode 100644
index 00000000..a470c3ec
--- /dev/null
+++ b/output.cc
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2003-2008, 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 "output.h"
+
+namespace ledger {
+
+format_xacts::format_xacts(std::ostream& _output_stream,
+ const string& format)
+ : output_stream(_output_stream), last_entry(NULL), last_xact(NULL)
+{
+ TRACE_CTOR(format_xacts, "std::ostream&, const string&");
+
+ const char * f = format.c_str();
+ if (const char * p = std::strstr(f, "%/")) {
+ first_line_format.parse(string(f, 0, p - f));
+ next_lines_format.parse(string(p + 2));
+ } else {
+ first_line_format.parse(format);
+ next_lines_format.parse(format);
+ }
+}
+
+void format_xacts::operator()(xact_t& xact)
+{
+ if (! xact.has_xdata() ||
+ ! xact.xdata().has_flags(XACT_EXT_DISPLAYED)) {
+ if (last_entry != xact.entry) {
+ first_line_format.format(output_stream, xact);
+ last_entry = xact.entry;
+ }
+ else if (last_xact && last_xact->date() != xact.date()) {
+ first_line_format.format(output_stream, xact);
+ }
+ else {
+ next_lines_format.format(output_stream, xact);
+ }
+
+ xact.xdata().add_flags(XACT_EXT_DISPLAYED);
+ last_xact = &xact;
+ }
+}
+
+void format_entries::format_last_entry()
+{
+ bool first = true;
+
+ foreach (xact_t * xact, last_entry->xacts) {
+ if (xact->has_xdata() &&
+ xact->xdata().has_flags(XACT_EXT_TO_DISPLAY)) {
+ if (first) {
+ first_line_format.format(output_stream, *xact);
+ first = false;
+ } else {
+ next_lines_format.format(output_stream, *xact);
+ }
+ xact->xdata().add_flags(XACT_EXT_DISPLAYED);
+ }
+ }
+}
+
+void format_entries::operator()(xact_t& xact)
+{
+ xact.xdata().add_flags(XACT_EXT_TO_DISPLAY);
+
+ if (last_entry && xact.entry != last_entry)
+ format_last_entry();
+
+ last_entry = xact.entry;
+}
+
+void print_entry(std::ostream& out, const entry_base_t& entry_base,
+ const string& prefix)
+{
+ string print_format;
+
+ if (dynamic_cast<const entry_t *>(&entry_base)) {
+ print_format = (prefix + "%D %X%C%P\n" +
+ prefix + " %-34A %12o\n%/" +
+ prefix + " %-34A %12o\n");
+ }
+ else if (const auto_entry_t * entry =
+ dynamic_cast<const auto_entry_t *>(&entry_base)) {
+ out << "= " << entry->predicate.predicate.text() << '\n';
+ print_format = prefix + " %-34A %12o\n";
+ }
+ else if (const period_entry_t * entry =
+ dynamic_cast<const period_entry_t *>(&entry_base)) {
+ out << "~ " << entry->period_string << '\n';
+ print_format = prefix + " %-34A %12o\n";
+ }
+ else {
+ assert(false);
+ }
+
+#if 0
+ format_entries formatter(out, print_format);
+ walk_xacts(const_cast<xacts_list&>(entry_base.xacts), formatter);
+ formatter.flush();
+
+ clear_xact_xdata cleaner;
+ walk_xacts(const_cast<xacts_list&>(entry_base.xacts), cleaner);
+#endif
+}
+
+void format_accounts::operator()(account_t& account)
+{
+ if (display_account(account)) {
+ if (! account.parent) {
+ account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY);
+ } else {
+ format.format(output_stream, account);
+ account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
+ }
+ }
+}
+
+bool format_accounts::disp_subaccounts_p(account_t& account,
+ account_t *& to_show)
+{
+ bool display = false;
+ unsigned int counted = 0;
+ bool matches = disp_pred(account);
+ bool computed = false;
+ value_t acct_total;
+ value_t result;
+
+ to_show = NULL;
+
+ foreach (accounts_map::value_type pair, account.accounts) {
+ if (! disp_pred(*pair.second))
+ continue;
+
+#if 0
+ compute_total(result, *pair.second);
+#endif
+ if (! computed) {
+#if 0
+ compute_total(acct_total, account);
+#endif
+ computed = true;
+ }
+
+ if ((result != acct_total) || counted > 0) {
+ display = matches;
+ break;
+ }
+ to_show = pair.second;
+ counted++;
+ }
+
+ return display;
+}
+
+bool format_accounts::display_account(account_t& account)
+{
+ // Never display an account that has already been displayed.
+ if (account.has_xdata() &&
+ account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED))
+ return false;
+
+ // At this point, one of two possibilities exists: the account is a
+ // leaf which matches the predicate restrictions; or it is a parent
+ // and two or more children must be subtotaled; or it is a parent
+ // and its child has been hidden by the predicate. So first,
+ // determine if it is a parent that must be displayed regardless of
+ // the predicate.
+
+ account_t * account_to_show = NULL;
+ if (disp_subaccounts_p(account, account_to_show))
+ return true;
+
+ return ! account_to_show && disp_pred(account);
+}
+
+format_equity::format_equity(std::ostream& _output_stream,
+ const string& _format,
+ const string& display_predicate)
+ : format_accounts(_output_stream, "", display_predicate)
+{
+ const char * f = _format.c_str();
+
+ if (const char * p = std::strstr(f, "%/")) {
+ first_line_format.parse(string(f, 0, p - f));
+ next_lines_format.parse(string(p + 2));
+ } else {
+ first_line_format.parse(_format);
+ next_lines_format.parse(_format);
+ }
+
+ entry_t header_entry;
+ header_entry.payee = "Opening Balances";
+ header_entry._date = current_date;
+ first_line_format.format(output_stream, header_entry);
+}
+
+void format_equity::flush()
+{
+ account_t summary(NULL, "Equity:Opening Balances");
+
+ account_t::xdata_t& xdata(summary.xdata());
+
+ xdata.value = total.negate();
+
+ if (total.type() >= value_t::BALANCE) {
+ const balance_t * bal;
+ if (total.is_type(value_t::BALANCE))
+ bal = &(total.as_balance());
+ else if (total.is_type(value_t::BALANCE_PAIR))
+ bal = &(total.as_balance_pair().quantity());
+ else
+ assert(false);
+
+ foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
+ xdata.value = pair.second;
+ xdata.value.negate();
+ next_lines_format.format(output_stream, summary);
+ }
+ } else {
+ next_lines_format.format(output_stream, summary);
+ }
+ output_stream.flush();
+}
+
+void format_equity::operator()(account_t& account)
+{
+ if (display_account(account)) {
+ if (account.has_xdata()) {
+ value_t val = account.xdata().value;
+
+ if (val.type() >= value_t::BALANCE) {
+ const balance_t * bal;
+ if (val.is_type(value_t::BALANCE))
+ bal = &(val.as_balance());
+ else if (val.is_type(value_t::BALANCE_PAIR))
+ bal = &(val.as_balance_pair().quantity());
+ else
+ assert(false);
+
+ foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
+ account.xdata().value = pair.second;
+ next_lines_format.format(output_stream, account);
+ }
+ account.xdata().value = val;
+ } else {
+ next_lines_format.format(output_stream, account);
+ }
+ total += val;
+ }
+ account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
+ }
+}
+
+} // namespace ledger
diff --git a/output.h b/output.h
new file mode 100644
index 00000000..4954942f
--- /dev/null
+++ b/output.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2003-2008, 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 _OUTPUT_H
+#define _OUTPUT_H
+
+#include "session.h"
+#include "handler.h"
+#include "format.h"
+
+namespace ledger {
+
+class format_xacts : public item_handler<xact_t>
+{
+protected:
+ std::ostream& output_stream;
+ format_t first_line_format;
+ format_t next_lines_format;
+ entry_t * last_entry;
+ xact_t * last_xact;
+
+public:
+ format_xacts(std::ostream& _output_stream,
+ const string& format);
+ virtual ~format_xacts() {
+ TRACE_DTOR(format_xacts);
+ }
+
+ virtual void flush() {
+ output_stream.flush();
+ }
+ virtual void operator()(xact_t& xact);
+};
+
+class format_entries : public format_xacts
+{
+ public:
+ format_entries(std::ostream& output_stream, const string& format)
+ : format_xacts(output_stream, format) {
+ TRACE_CTOR(format_entries, "std::ostream&, const string&");
+ }
+ virtual ~format_entries() {
+ TRACE_DTOR(format_entries);
+ }
+
+ virtual void format_last_entry();
+
+ virtual void flush() {
+ if (last_entry) {
+ format_last_entry();
+ last_entry = NULL;
+ }
+ format_xacts::flush();
+ }
+ virtual void operator()(xact_t& xact);
+
+private:
+ void print_entry(std::ostream& out,
+ const entry_base_t& entry,
+ const string& prefix = "");
+};
+
+class format_accounts : public item_handler<account_t>
+{
+protected:
+ std::ostream& output_stream;
+
+ item_predicate<account_t> disp_pred;
+
+ bool disp_subaccounts_p(account_t& account, account_t *& to_show);
+ bool display_account(account_t& account);
+
+public:
+ format_t format;
+
+ format_accounts(std::ostream& _output_stream,
+ const string& _format,
+ const string& display_predicate = "")
+ : output_stream(_output_stream), disp_pred(display_predicate),
+ format(_format) {
+ TRACE_CTOR(format_accounts, "std::ostream&, const string&, const string&");
+ }
+ virtual ~format_accounts() {
+ TRACE_DTOR(format_accounts);
+ }
+
+ virtual void flush() {
+ output_stream.flush();
+ }
+
+ virtual void operator()(account_t& account);
+};
+
+class format_equity : public format_accounts
+{
+ format_t first_line_format;
+ format_t next_lines_format;
+
+ mutable value_t total;
+
+ public:
+ format_equity(std::ostream& _output_stream,
+ const string& _format,
+ const string& display_predicate = "");
+ virtual ~format_equity() {
+ TRACE_DTOR(format_equity);
+ }
+
+ virtual void flush();
+ virtual void operator()(account_t& account);
+};
+
+} // namespace ledger
+
+#endif // _OUTPUT_H
diff --git a/reconcile.cc b/reconcile.cc
index 273517f1..aca1732e 100644
--- a/reconcile.cc
+++ b/reconcile.cc
@@ -30,7 +30,6 @@
*/
#include "reconcile.h"
-#include "walk.h"
namespace ledger {
diff --git a/reconcile.h b/reconcile.h
index 97ae6c7b..2e133087 100644
--- a/reconcile.h
+++ b/reconcile.h
@@ -33,7 +33,8 @@
#define _RECONCILE_H
#include "value.h"
-#include "walk.h"
+#include "iterators.h"
+#include "filters.h"
namespace ledger {
diff --git a/report.cc b/report.cc
index d6aea4fa..f250a1c4 100644
--- a/report.cc
+++ b/report.cc
@@ -372,7 +372,7 @@ report_t::chain_xact_handlers(xact_handler_ptr base_handler,
forecast_xacts * forecast_handler
= new forecast_xacts(handler, forecast_limit);
forecast_handler->add_period_entries(journal->period_entries);
- handler.reset(forecast_handler;
+ handler.reset(forecast_handler);
// See above, under budget_xacts.
if (! predicate.empty())
@@ -415,7 +415,7 @@ void report_t::sum_all_accounts()
(chain_xact_handlers(xact_handler_ptr(new set_account_value), false),
walker);
// no flush() needed with set_account_value
- sum_accounts(*session.master);
+ session.master->calculate_sums();
}
void report_t::accounts_report(acct_handler_ptr handler,
@@ -425,11 +425,11 @@ void report_t::accounts_report(acct_handler_ptr handler,
sum_all_accounts();
if (sort_string.empty()) {
- accounts_iterator walker(*session.master);
- pass_down_accounts<accounts_iterator>(handler, walker);
+ basic_accounts_iterator walker(*session.master);
+ pass_down_accounts(handler, walker);
} else {
sorted_accounts_iterator walker(*session.master, sort_string);
- pass_down_accounts<sorted_accounts_iterator>(handler, walker);
+ pass_down_accounts(handler, walker);
}
handler->flush();
@@ -463,43 +463,6 @@ void report_t::entry_report(const entry_t& entry, const string& format)
{
}
-#if 0
-value_t report_t::abbrev(call_scope_t& args)
-{
- if (args.size() < 2)
- throw_(usage_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])");
-
- const var_t<string> str(args, 0);
- const var_t<long> wid(args, 1);
- const var_t<long> style(args, 2);
- const var_t<long> abbrev_len(args, 3);
-
- return value_t(abbreviate(*str, *wid,
- (style ?
- static_cast<format_t::elision_style_t>(*style) :
- session.elision_style),
- true,
- abbrev_len ? *abbrev_len : session.abbrev_len),
- true);
-}
-
-value_t report_t::ftime(call_scope_t& args)
-{
- if (args.size() < 1)
- throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])");
-
- date_t date = args[0].as_date();
-
- string date_format;
- if (args.size() == 2)
- date_format = args[1].as_string();
- else
- date_format = moment_t::output_format;
-
- return string_value(date.as_string(date_format));
-}
-#endif
-
expr_t::ptr_op_t report_t::lookup(const string& name)
{
const char * p = name.c_str();
@@ -552,252 +515,4 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return session.lookup(name);
}
-// jww (2008-08-01): Find a home for this code
-
-format_xacts::format_xacts(std::ostream& _output_stream,
- const string& format)
- : output_stream(_output_stream), last_entry(NULL), last_xact(NULL)
-{
- TRACE_CTOR(format_xacts, "std::ostream&, const string&");
-
- const char * f = format.c_str();
- if (const char * p = std::strstr(f, "%/")) {
- first_line_format.parse(string(f, 0, p - f));
- next_lines_format.parse(string(p + 2));
- } else {
- first_line_format.parse(format);
- next_lines_format.parse(format);
- }
-}
-
-void format_xacts::operator()(xact_t& xact)
-{
- if (! xact.has_xdata() ||
- ! xact.xdata().has_flags(XACT_EXT_DISPLAYED)) {
- if (last_entry != xact.entry) {
- first_line_format.format(output_stream, xact);
- last_entry = xact.entry;
- }
- else if (last_xact && last_xact->date() != xact.date()) {
- first_line_format.format(output_stream, xact);
- }
- else {
- next_lines_format.format(output_stream, xact);
- }
-
- xact.xdata().add_flags(XACT_EXT_DISPLAYED);
- last_xact = &xact;
- }
-}
-
-void format_entries::format_last_entry()
-{
- bool first = true;
-
- foreach (xact_t * xact, last_entry->xacts) {
- if (xact->has_xdata() &&
- xact->xdata().has_flags(XACT_EXT_TO_DISPLAY)) {
- if (first) {
- first_line_format.format(output_stream, *xact);
- first = false;
- } else {
- next_lines_format.format(output_stream, *xact);
- }
- xact->xdata().add_flags(XACT_EXT_DISPLAYED);
- }
- }
-}
-
-void format_entries::operator()(xact_t& xact)
-{
- xact.xdata().add_flags(XACT_EXT_TO_DISPLAY);
-
- if (last_entry && xact.entry != last_entry)
- format_last_entry();
-
- last_entry = xact.entry;
-}
-
-void print_entry(std::ostream& out, const entry_base_t& entry_base,
- const string& prefix)
-{
- string print_format;
-
- if (dynamic_cast<const entry_t *>(&entry_base)) {
- print_format = (prefix + "%D %X%C%P\n" +
- prefix + " %-34A %12o\n%/" +
- prefix + " %-34A %12o\n");
- }
- else if (const auto_entry_t * entry =
- dynamic_cast<const auto_entry_t *>(&entry_base)) {
- out << "= " << entry->predicate.predicate.text() << '\n';
- print_format = prefix + " %-34A %12o\n";
- }
- else if (const period_entry_t * entry =
- dynamic_cast<const period_entry_t *>(&entry_base)) {
- out << "~ " << entry->period_string << '\n';
- print_format = prefix + " %-34A %12o\n";
- }
- else {
- assert(false);
- }
-
-#if 0
- format_entries formatter(out, print_format);
- walk_xacts(const_cast<xacts_list&>(entry_base.xacts), formatter);
- formatter.flush();
-
- clear_xact_xdata cleaner;
- walk_xacts(const_cast<xacts_list&>(entry_base.xacts), cleaner);
-#endif
-}
-
-bool disp_subaccounts_p(account_t& account,
- item_predicate<account_t>& disp_pred,
- account_t *& to_show)
-{
- bool display = false;
- unsigned int counted = 0;
- bool matches = disp_pred(account);
- bool computed = false;
- value_t acct_total;
- value_t result;
-
- to_show = NULL;
-
- foreach (accounts_map::value_type pair, account.accounts) {
- if (! disp_pred(*pair.second))
- continue;
-
-#if 0
- compute_total(result, *pair.second);
-#endif
- if (! computed) {
-#if 0
- compute_total(acct_total, account);
-#endif
- computed = true;
- }
-
- if ((result != acct_total) || counted > 0) {
- display = matches;
- break;
- }
- to_show = pair.second;
- counted++;
- }
-
- return display;
-}
-
-bool display_account(account_t& account, item_predicate<account_t>& disp_pred)
-{
- // Never display an account that has already been displayed.
- if (account_has_xdata(account) &&
- account_xdata_(account).dflags & ACCOUNT_DISPLAYED)
- return false;
-
- // At this point, one of two possibilities exists: the account is a
- // leaf which matches the predicate restrictions; or it is a parent
- // and two or more children must be subtotaled; or it is a parent
- // and its child has been hidden by the predicate. So first,
- // determine if it is a parent that must be displayed regardless of
- // the predicate.
-
- account_t * account_to_show = NULL;
- if (disp_subaccounts_p(account, disp_pred, account_to_show))
- return true;
-
- return ! account_to_show && disp_pred(account);
-}
-
-void format_accounts::operator()(account_t& account)
-{
- if (display_account(account, disp_pred)) {
- if (! account.parent) {
- account_xdata(account).dflags |= ACCOUNT_TO_DISPLAY;
- } else {
- format.format(output_stream, account);
- account_xdata(account).dflags |= ACCOUNT_DISPLAYED;
- }
- }
-}
-
-format_equity::format_equity(std::ostream& _output_stream,
- const string& _format,
- const string& display_predicate)
- : output_stream(_output_stream), disp_pred(display_predicate)
-{
- const char * f = _format.c_str();
- if (const char * p = std::strstr(f, "%/")) {
- first_line_format.parse(string(f, 0, p - f));
- next_lines_format.parse(string(p + 2));
- } else {
- first_line_format.parse(_format);
- next_lines_format.parse(_format);
- }
-
- entry_t header_entry;
- header_entry.payee = "Opening Balances";
- header_entry._date = current_date;
- first_line_format.format(output_stream, header_entry);
-}
-
-void format_equity::flush()
-{
- account_xdata_t xdata;
- xdata.value = total;
- xdata.value.negate();
- account_t summary(NULL, "Equity:Opening Balances");
- summary.data = &xdata;
-
- if (total.type() >= value_t::BALANCE) {
- const balance_t * bal;
- if (total.is_type(value_t::BALANCE))
- bal = &(total.as_balance());
- else if (total.is_type(value_t::BALANCE_PAIR))
- bal = &(total.as_balance_pair().quantity());
- else
- assert(false);
-
- foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
- xdata.value = pair.second;
- xdata.value.negate();
- next_lines_format.format(output_stream, summary);
- }
- } else {
- next_lines_format.format(output_stream, summary);
- }
- output_stream.flush();
-}
-
-void format_equity::operator()(account_t& account)
-{
- if (display_account(account, disp_pred)) {
- if (account_has_xdata(account)) {
- value_t val = account_xdata_(account).value;
-
- if (val.type() >= value_t::BALANCE) {
- const balance_t * bal;
- if (val.is_type(value_t::BALANCE))
- bal = &(val.as_balance());
- else if (val.is_type(value_t::BALANCE_PAIR))
- bal = &(val.as_balance_pair().quantity());
- else
- assert(false);
-
- foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
- account_xdata_(account).value = pair.second;
- next_lines_format.format(output_stream, account);
- }
- account_xdata_(account).value = val;
- } else {
- next_lines_format.format(output_stream, account);
- }
- total += val;
- }
- account_xdata(account).dflags |= ACCOUNT_DISPLAYED;
- }
-}
-
} // namespace ledger
diff --git a/report.h b/report.h
index a2bbe8e6..b160c15b 100644
--- a/report.h
+++ b/report.h
@@ -33,8 +33,7 @@
#define _REPORT_H
#include "session.h"
-#include "format.h"
-#include "walk.h"
+#include "handler.h"
namespace ledger {
@@ -196,13 +195,6 @@ public:
void entry_report(const entry_t& entry, const string& format);
//
- // Utility functions for value expressions
- //
-
- value_t ftime(call_scope_t& args);
- value_t abbrev(call_scope_t& args);
-
- //
// Config options
//
@@ -257,112 +249,6 @@ public:
virtual expr_t::ptr_op_t lookup(const string& name);
};
-string abbrev(const string& str, unsigned int width,
- const bool is_account);
-
-// jww (2008-08-01): Where does this code belong?
-
-class format_xacts : public item_handler<xact_t>
-{
-protected:
- std::ostream& output_stream;
- format_t first_line_format;
- format_t next_lines_format;
- entry_t * last_entry;
- xact_t * last_xact;
-
-public:
- format_xacts(std::ostream& _output_stream,
- const string& format);
- ~format_xacts() throw() {
- TRACE_DTOR(format_xacts);
- }
-
- virtual void flush() {
- output_stream.flush();
- }
- virtual void operator()(xact_t& xact);
-};
-
-class format_entries : public format_xacts
-{
- public:
- format_entries(std::ostream& output_stream, const string& format)
- : format_xacts(output_stream, format) {
- TRACE_CTOR(format_entries, "std::ostream&, const string&");
- }
- ~format_entries() throw() {
- TRACE_DTOR(format_entries);
- }
-
- virtual void format_last_entry();
-
- virtual void flush() {
- if (last_entry) {
- format_last_entry();
- last_entry = NULL;
- }
- format_xacts::flush();
- }
- virtual void operator()(xact_t& xact);
-};
-
-void print_entry(std::ostream& out,
- const entry_base_t& entry,
- const string& prefix = "");
-
-bool disp_subaccounts_p(account_t& account,
- item_predicate<account_t>& disp_pred,
- account_t *& to_show);
-
-bool display_account(account_t& account, item_predicate<account_t>& disp_pred);
-
-class format_accounts : public item_handler<account_t>
-{
- std::ostream& output_stream;
-
- item_predicate<account_t> disp_pred;
-
- public:
- format_t format;
-
- format_accounts(std::ostream& _output_stream,
- const string& _format,
- const string& display_predicate = NULL)
- : output_stream(_output_stream), disp_pred(display_predicate),
- format(_format) {
- TRACE_CTOR(format_accounts, "std::ostream&, const string&, const string&");
- }
- ~format_accounts() throw() {
- TRACE_DTOR(format_accounts);
- }
-
- virtual void flush() {
- output_stream.flush();
- }
-
- virtual void operator()(account_t& account);
-};
-
-class format_equity : public item_handler<account_t>
-{
- std::ostream& output_stream;
- format_t first_line_format;
- format_t next_lines_format;
-
- item_predicate<account_t> disp_pred;
-
- mutable value_t total;
-
- public:
- format_equity(std::ostream& _output_stream,
- const string& _format,
- const string& display_predicate);
-
- virtual void flush();
- virtual void operator()(account_t& account);
-};
-
} // namespace ledger
#endif // _REPORT_H
diff --git a/session.cc b/session.cc
index 3bafd43c..06288444 100644
--- a/session.cc
+++ b/session.cc
@@ -31,7 +31,9 @@
#include "session.h"
#include "report.h"
-#include "walk.h"
+#include "handler.h"
+#include "iterators.h"
+#include "filters.h"
namespace ledger {
@@ -256,9 +258,9 @@ void session_t::clean_xacts(entry_t& entry)
void session_t::clean_accounts()
{
- accounts_iterator acct_walker(*master);
- pass_down_accounts<accounts_iterator>
- (acct_handler_ptr(new clear_account_xdata), acct_walker);
+ basic_accounts_iterator acct_walker(*master);
+ pass_down_accounts(acct_handler_ptr(new clear_account_xdata),
+ acct_walker);
}
#if 0
@@ -297,6 +299,43 @@ expr_t::ptr_op_t session_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
+ case 'b':
+ if (std::strcmp(p, "balance_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(balance_format));
+ break;
+
+ case 'e':
+ if (std::strcmp(p, "equity_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(equity_format));
+ break;
+
+ case 'p':
+ if (std::strcmp(p, "plot_amount_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(plot_amount_format));
+ else if (std::strcmp(p, "plot_total_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(plot_total_format));
+ else if (std::strcmp(p, "prices_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(prices_format));
+ else if (std::strcmp(p, "pricesdb_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(pricesdb_format));
+ else if (std::strcmp(p, "print_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(print_format));
+ break;
+
+ case 'r':
+ if (std::strcmp(p, "register_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(register_format));
+ break;
+
+ case 'w':
+ if (std::strcmp(p, "wide_register_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(wide_register_format));
+ else if (std::strcmp(p, "write_hdr_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(write_hdr_format));
+ else if (std::strcmp(p, "write_xact_format") == 0)
+ return expr_t::op_t::wrap_value(string_value(write_xact_format));
+ break;
+
case 'o':
if (std::strncmp(p, "opt_", 4) == 0) {
p = p + 4;
diff --git a/textual.cc b/textual.cc
index 9aa28189..e46ae22a 100644
--- a/textual.cc
+++ b/textual.cc
@@ -306,7 +306,7 @@ xact_t * parse_xact(char * line, account_t * account, entry_t * entry = NULL)
if (entry != NULL) {
// Add this amount to the related account now
- account_xdata_t& xdata(account_xdata(*xact->account));
+ account_t::xdata_t& xdata(xact->account->xdata());
if (! xact->amount.is_null()) {
if (xdata.value.is_null())
diff --git a/textual.h b/textual.h
index 47faad8e..8064d0db 100644
--- a/textual.h
+++ b/textual.h
@@ -33,8 +33,7 @@
#define _TEXTUAL_H
#include "journal.h"
-#include "format.h"
-#include "walk.h"
+#include "handler.h"
namespace ledger {
diff --git a/token.cc b/token.cc
index deafdb40..d1d70a38 100644
--- a/token.cc
+++ b/token.cc
@@ -243,36 +243,8 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
kind = COLON;
break;
- case 'c':
- case 'C':
- case 'p':
- case 'w':
- case 'W':
- case 'e':
case '/': {
- bool code_mask = c == 'c';
- bool commodity_mask = c == 'C';
- bool payee_mask = c == 'p';
- bool note_mask = c == 'e';
- bool short_account_mask = c == 'w';
-
in.get(c);
- if (c == '/') {
- c = peek_next_nonws(in);
- if (c == '/') {
- in.get(c);
- c = in.peek();
- if (c == '/') {
- in.get(c);
- c = in.peek();
- short_account_mask = true;
- } else {
- payee_mask = true;
- }
- }
- } else {
- in.get(c);
- }
// Read in the regexp
char buf[256];
diff --git a/xact.h b/xact.h
index 5a3aa32f..5d4950f9 100644
--- a/xact.h
+++ b/xact.h
@@ -184,10 +184,10 @@ public:
#endif
};
- // This variable holds a pointer to "extended data" which is usually
- // produced only during reporting, and only for the transaction set being
- // reported. It's a memory-saving measure to delay allocation until the
- // last possible moment.
+ // This variable holds optional "extended data" which is usually produced
+ // only during reporting, and only for the transaction set being reported.
+ // It's a memory-saving measure to delay allocation until the last possible
+ // moment.
mutable optional<xdata_t> xdata_;
bool has_xdata() const {
diff --git a/xml.h b/xml.h
index a9249967..0e86163c 100644
--- a/xml.h
+++ b/xml.h
@@ -34,6 +34,7 @@
#include "journal.h"
#include "report.h"
+#include "output.h"
namespace ledger {