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:
John Wiegley 2008-08-03 21:38:53 -04:00
parent 3e4b016940
commit bbdab79302
33 changed files with 1933 additions and 1536 deletions

View file

@ -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 \

View file

@ -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

View file

@ -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
View 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
View 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
View file

@ -32,7 +32,7 @@
#ifndef _CSV_H
#define _CSV_H
#include "journal.h"
#include "handler.h"
#include "format.h"
namespace ledger {

View file

@ -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());
}

View file

@ -30,6 +30,7 @@
*/
#include "emacs.h"
#include "account.h"
namespace ledger {

View file

@ -32,7 +32,7 @@
#ifndef _EMACS_H
#define _EMACS_H
#include "journal.h"
#include "handler.h"
#include "format.h"
namespace ledger {

View file

@ -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

View file

@ -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

View file

@ -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())

View file

@ -34,7 +34,6 @@
#include "journal.h"
#include "expr.h"
#include "walk.h"
namespace ledger {

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View file

@ -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
View file

@ -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
View 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
View 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

View file

@ -30,7 +30,6 @@
*/
#include "reconcile.h"
#include "walk.h"
namespace ledger {

View file

@ -33,7 +33,8 @@
#define _RECONCILE_H
#include "value.h"
#include "walk.h"
#include "iterators.h"
#include "filters.h"
namespace ledger {

295
report.cc
View file

@ -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
View file

@ -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

View file

@ -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;

View file

@ -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())

View file

@ -33,8 +33,7 @@
#define _TEXTUAL_H
#include "journal.h"
#include "format.h"
#include "walk.h"
#include "handler.h"
namespace ledger {

View file

@ -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
View file

@ -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
View file

@ -34,6 +34,7 @@
#include "journal.h"
#include "report.h"
#include "output.h"
namespace ledger {