Rearranged the code a bit, breaking walk.cc into several different files:
compare compare_items<T> handler item_handler<T> iterators used to iterators sets of journal objects filters derived from item_handler, they morph the result set output derived from item_handler, these do the printing Also, created a new 'help' files which contains just Ledger's help text.
This commit is contained in:
parent
3e4b016940
commit
bbdab79302
33 changed files with 1933 additions and 1536 deletions
13
Makefile.am
13
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 \
|
||||
|
|
|
|||
21
account.cc
21
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
|
||||
|
|
|
|||
48
account.h
48
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);
|
||||
|
|
|
|||
85
compare.cc
Normal file
85
compare.cc
Normal file
|
|
@ -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
|
||||
77
compare.h
Normal file
77
compare.h
Normal file
|
|
@ -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
|
||||
2
csv.h
2
csv.h
|
|
@ -32,7 +32,7 @@
|
|||
#ifndef _CSV_H
|
||||
#define _CSV_H
|
||||
|
||||
#include "journal.h"
|
||||
#include "handler.h"
|
||||
#include "format.h"
|
||||
|
||||
namespace ledger {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
1
emacs.cc
1
emacs.cc
|
|
@ -30,6 +30,7 @@
|
|||
*/
|
||||
|
||||
#include "emacs.h"
|
||||
#include "account.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
2
emacs.h
2
emacs.h
|
|
@ -32,7 +32,7 @@
|
|||
#ifndef _EMACS_H
|
||||
#define _EMACS_H
|
||||
|
||||
#include "journal.h"
|
||||
#include "handler.h"
|
||||
#include "format.h"
|
||||
|
||||
namespace ledger {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
pass_down_xacts::pass_down_xacts(xact_handler_ptr handler,
|
||||
xacts_iterator& iter)
|
||||
: item_handler<xact_t>(handler)
|
||||
{
|
||||
assert(left);
|
||||
assert(right);
|
||||
TRACE_CTOR(pass_down_xacts, "xact_handler_ptr, xacts_iterator");
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
pass_down_accounts::pass_down_accounts(acct_handler_ptr handler,
|
||||
accounts_iterator& iter)
|
||||
: item_handler<account_t>(handler)
|
||||
{
|
||||
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()()
|
||||
{
|
||||
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
|
||||
|
|
@ -1,136 +1,47 @@
|
|||
#ifndef _WALK_H
|
||||
#define _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 "journal.h"
|
||||
#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
|
||||
13
format.cc
13
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())
|
||||
|
|
|
|||
1
format.h
1
format.h
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
#include "journal.h"
|
||||
#include "expr.h"
|
||||
#include "walk.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
19
gnucash.cc
19
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 {
|
||||
|
||||
|
|
|
|||
72
handler.h
Normal file
72
handler.h
Normal file
|
|
@ -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
|
||||
205
help.cc
Normal file
205
help.cc
Normal file
|
|
@ -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
|
||||
49
help.h
Normal file
49
help.h
Normal file
|
|
@ -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
|
||||
200
iterators.cc
Normal file
200
iterators.cc
Normal file
|
|
@ -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
|
||||
226
iterators.h
Normal file
226
iterators.h
Normal file
|
|
@ -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
|
||||
663
main.cc
663
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);
|
||||
|
||||
session_t& session(report.session);
|
||||
|
||||
// Handle the command-line arguments
|
||||
|
||||
strings_list args;
|
||||
process_arguments(argc - 1, argv + 1, false, report, args);
|
||||
|
||||
if (args.empty()) {
|
||||
#if 0
|
||||
help(std::cerr);
|
||||
#endif
|
||||
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;
|
||||
|
||||
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
|
||||
|
||||
// Process the environment settings
|
||||
|
||||
TRACE_START(environment, 1, "Processed environment variables");
|
||||
process_environment(const_cast<const char **>(envp), "LEDGER_", report);
|
||||
TRACE_FINISH(environment, 1);
|
||||
|
||||
optional<path> home;
|
||||
if (const char * home_var = std::getenv("HOME"))
|
||||
home = home_var;
|
||||
|
||||
if (! session.init_file)
|
||||
session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
|
||||
if (! session.price_db)
|
||||
session.price_db = home ? *home / ".pricedb" : "./.pricedb";
|
||||
|
||||
if (! session.cache_file)
|
||||
session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
|
||||
|
||||
if (session.data_file == *session.cache_file)
|
||||
session.use_cache = false;
|
||||
|
||||
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_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.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
|
||||
#endif
|
||||
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");
|
||||
find_scope<report_t>(args).xacts_report
|
||||
(xact_handler_ptr(new Formatter(*ostream, *format)));
|
||||
return true;
|
||||
}
|
||||
else if (status == 0) { // child
|
||||
// Duplicate pipe's reading end into stdin
|
||||
status = dup2(pfd[0], STDIN_FILENO);
|
||||
};
|
||||
|
||||
template <class Formatter = format_accounts>
|
||||
class accounts_report
|
||||
{
|
||||
string format_name;
|
||||
|
||||
public:
|
||||
accounts_report(const string& _format_name)
|
||||
: format_name(_format_name) {}
|
||||
|
||||
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).accounts_report
|
||||
(acct_handler_ptr(new Formatter(*ostream, *format)));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int read_and_report(ledger::report_t& report,
|
||||
int argc, char * argv[], char * envp[])
|
||||
{
|
||||
using namespace ledger;
|
||||
|
||||
session_t& session(report.session);
|
||||
|
||||
// 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;
|
||||
|
||||
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
|
||||
|
||||
// Process the environment settings
|
||||
|
||||
TRACE_START(environment, 1, "Processed environment variables");
|
||||
process_environment(const_cast<const char **>(envp), "LEDGER_", report);
|
||||
TRACE_FINISH(environment, 1);
|
||||
|
||||
optional<path> home;
|
||||
if (const char * home_var = std::getenv("HOME"))
|
||||
home = home_var;
|
||||
|
||||
if (! session.init_file)
|
||||
session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
|
||||
if (! session.price_db)
|
||||
session.price_db = home ? *home / ".pricedb" : "./.pricedb";
|
||||
|
||||
if (! session.cache_file)
|
||||
session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
|
||||
|
||||
if (session.data_file == *session.cache_file)
|
||||
session.use_cache = false;
|
||||
|
||||
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_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.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
|
||||
#endif
|
||||
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)
|
||||
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]);
|
||||
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");
|
||||
|
||||
// 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
|
||||
// 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]);
|
||||
|
||||
// Read the command word and create a command object based on it
|
||||
|
||||
string verb = *arg++;
|
||||
|
||||
if (verb == "parse") {
|
||||
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 << 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 << std::endl;
|
||||
*out << "--- Parsed tree ---" << std::endl;
|
||||
expr.dump(*out);
|
||||
|
||||
expr.compile(report);
|
||||
|
||||
*out << std::endl;
|
||||
*out << "--- Compiled 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 == "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;
|
||||
|
||||
date_t date = interval.first();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
*out << std::right;
|
||||
out->width(2);
|
||||
|
||||
*out << i << ": " << format_date(date) << std::endl;
|
||||
|
||||
date = interval.increment(date);
|
||||
if (is_valid(interval.end) && date >= interval.end)
|
||||
break;
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse the initialization file, which can only be textual; then
|
||||
// parse the journal data.
|
||||
// Read the command word and see if it's any of the debugging commands
|
||||
// that Ledger supports.
|
||||
|
||||
session.read_init();
|
||||
string verb = *arg++;
|
||||
|
||||
INFO_START(journal, "Read journal file");
|
||||
if (verb == "parse") {
|
||||
expr_t expr(*arg);
|
||||
|
||||
journal_t& journal(*session.create_journal());
|
||||
*out << "Value expression as input: " << *arg << 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?");
|
||||
|
||||
INFO_FINISH(journal);
|
||||
|
||||
INFO("Found " << count << " entries");
|
||||
|
||||
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);
|
||||
|
||||
// Are we handling the expr commands? Do so now.
|
||||
|
||||
if (verb == "expr") {
|
||||
expr_t expr(*arg);
|
||||
|
||||
IF_INFO() {
|
||||
*out << "Value expression tree:" << std::endl;
|
||||
expr.dump(*out);
|
||||
*out << std::endl;
|
||||
*out << "Value expression parsed was:" << std::endl;
|
||||
*out << "Value expression as parsed: ";
|
||||
expr.print(*out, report);
|
||||
*out << std::endl << std::endl;
|
||||
*out << "Result of calculation: ";
|
||||
*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 << std::endl;
|
||||
*out << "--- Parsed tree ---" << std::endl;
|
||||
expr.dump(*out);
|
||||
|
||||
expr.compile(report);
|
||||
|
||||
*out << std::endl;
|
||||
*out << "--- Compiled 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 == "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;
|
||||
|
||||
date_t date = interval.first();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
*out << std::right;
|
||||
out->width(2);
|
||||
|
||||
*out << i << ": " << format_date(date) << std::endl;
|
||||
|
||||
date = interval.increment(date);
|
||||
if (is_valid(interval.end) && date >= interval.end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out << expr.calc(report).strip_annotations() << std::endl;
|
||||
// Parse the initialization file, which can only be textual; then
|
||||
// parse the journal data.
|
||||
|
||||
session.read_init();
|
||||
|
||||
INFO_START(journal, "Read journal file");
|
||||
|
||||
journal_t& journal(*session.create_journal());
|
||||
|
||||
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?");
|
||||
|
||||
INFO_FINISH(journal);
|
||||
|
||||
INFO("Found " << count << " entries");
|
||||
|
||||
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);
|
||||
|
||||
// Create a command object based on the command verb that was seen
|
||||
// above.
|
||||
|
||||
function_t 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 == "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 {
|
||||
char buf[128];
|
||||
std::strcpy(buf, "cmd_");
|
||||
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 + "'");
|
||||
}
|
||||
|
||||
// Create an argument scope containing the report command's
|
||||
// arguments, and then invoke the command.
|
||||
|
||||
call_scope_t command_args(report);
|
||||
|
||||
command_args.push_back(value_t(out));
|
||||
|
||||
for (strings_list::iterator i = arg; i != args.end(); i++)
|
||||
command_args.push_back(string_value(*i));
|
||||
|
||||
INFO_START(command, "Did user command '" << verb << "'");
|
||||
|
||||
command(command_args);
|
||||
|
||||
INFO_FINISH(command);
|
||||
|
||||
#if 0
|
||||
// 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");
|
||||
|
||||
ofstream stream(*session.cache_file);
|
||||
journal.write(stream);
|
||||
|
||||
TRACE_FINISH(binary_cache, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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");
|
||||
}
|
||||
#endif
|
||||
else if (DO_VERIFY() && report.output_file) {
|
||||
checked_delete(out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read the command word and create a command object based on it
|
||||
|
||||
function_t command;
|
||||
|
||||
if (verb == "register" || verb == "reg" || verb == "r")
|
||||
command = register_command;
|
||||
#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();
|
||||
#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 + "'");
|
||||
}
|
||||
|
||||
// Create an argument scope containing the report command's
|
||||
// arguments, and then invoke the command.
|
||||
|
||||
call_scope_t command_args(report);
|
||||
|
||||
command_args.push_back(value_t(&report));
|
||||
command_args.push_back(value_t(out));
|
||||
|
||||
for (strings_list::iterator i = arg; i != args.end(); i++)
|
||||
command_args.push_back(string_value(*i));
|
||||
|
||||
INFO_START(command, "Did user command '" << verb << "'");
|
||||
|
||||
command(command_args);
|
||||
|
||||
INFO_FINISH(command);
|
||||
|
||||
// Clean up memory, if it matters
|
||||
|
||||
if (DO_VERIFY() && report.output_file)
|
||||
checked_delete(out);
|
||||
|
||||
#if 0
|
||||
// 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");
|
||||
|
||||
ofstream stream(*session.cache_file);
|
||||
journal.write(stream);
|
||||
|
||||
TRACE_FINISH(binary_cache, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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");
|
||||
}
|
||||
#endif
|
||||
else if (DO_VERIFY() && report.output_file) {
|
||||
checked_delete(out);
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
|||
169
option.cc
169
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
|
||||
|
|
|
|||
282
output.cc
Normal file
282
output.cc
Normal file
|
|
@ -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
|
||||
143
output.h
Normal file
143
output.h
Normal file
|
|
@ -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
|
||||
|
|
@ -30,7 +30,6 @@
|
|||
*/
|
||||
|
||||
#include "reconcile.h"
|
||||
#include "walk.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@
|
|||
#define _RECONCILE_H
|
||||
|
||||
#include "value.h"
|
||||
#include "walk.h"
|
||||
#include "iterators.h"
|
||||
#include "filters.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
295
report.cc
295
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
|
||||
|
|
|
|||
116
report.h
116
report.h
|
|
@ -33,8 +33,7 @@
|
|||
#define _REPORT_H
|
||||
|
||||
#include "session.h"
|
||||
#include "format.h"
|
||||
#include "walk.h"
|
||||
#include "handler.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
@ -195,13 +194,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
|
||||
|
|
|
|||
47
session.cc
47
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;
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
#define _TEXTUAL_H
|
||||
|
||||
#include "journal.h"
|
||||
#include "format.h"
|
||||
#include "walk.h"
|
||||
#include "handler.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
28
token.cc
28
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];
|
||||
|
|
|
|||
8
xact.h
8
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 {
|
||||
|
|
|
|||
1
xml.h
1
xml.h
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "journal.h"
|
||||
#include "report.h"
|
||||
#include "output.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue