Rearranged the code a bit, breaking walk.cc into several different files:
compare compare_items<T> handler item_handler<T> iterators used to iterators sets of journal objects filters derived from item_handler, they morph the result set output derived from item_handler, these do the printing Also, created a new 'help' files which contains just Ledger's help text.
This commit is contained in:
parent
3e4b016940
commit
bbdab79302
33 changed files with 1933 additions and 1536 deletions
13
Makefile.am
13
Makefile.am
|
|
@ -70,6 +70,8 @@ libledger_la_SOURCES = \
|
||||||
entry.cc \
|
entry.cc \
|
||||||
xact.cc \
|
xact.cc \
|
||||||
account.cc \
|
account.cc \
|
||||||
|
iterators.cc \
|
||||||
|
compare.cc \
|
||||||
\
|
\
|
||||||
textual.cc \
|
textual.cc \
|
||||||
cache.cc \
|
cache.cc \
|
||||||
|
|
@ -80,7 +82,9 @@ libledger_la_SOURCES = \
|
||||||
\
|
\
|
||||||
session.cc \
|
session.cc \
|
||||||
report.cc \
|
report.cc \
|
||||||
walk.cc \
|
filters.cc \
|
||||||
|
output.cc \
|
||||||
|
help.cc \
|
||||||
\
|
\
|
||||||
derive.cc \
|
derive.cc \
|
||||||
reconcile.cc \
|
reconcile.cc \
|
||||||
|
|
@ -130,6 +134,8 @@ pkginclude_HEADERS = \
|
||||||
entry.h \
|
entry.h \
|
||||||
xact.h \
|
xact.h \
|
||||||
account.h \
|
account.h \
|
||||||
|
iterators.h \
|
||||||
|
compare.h \
|
||||||
\
|
\
|
||||||
textual.h \
|
textual.h \
|
||||||
cache.h \
|
cache.h \
|
||||||
|
|
@ -142,7 +148,10 @@ pkginclude_HEADERS = \
|
||||||
\
|
\
|
||||||
session.h \
|
session.h \
|
||||||
report.h \
|
report.h \
|
||||||
walk.h \
|
handler.h \
|
||||||
|
filters.h \
|
||||||
|
output.h \
|
||||||
|
help.h \
|
||||||
\
|
\
|
||||||
derive.h \
|
derive.h \
|
||||||
reconcile.h \
|
reconcile.h \
|
||||||
|
|
|
||||||
21
account.cc
21
account.cc
|
|
@ -143,4 +143,25 @@ bool account_t::valid() const
|
||||||
return true;
|
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
|
} // namespace ledger
|
||||||
|
|
|
||||||
48
account.h
48
account.h
|
|
@ -98,6 +98,54 @@ class account_t : public scope_t
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
|
|
||||||
friend class journal_t;
|
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);
|
std::ostream& operator<<(std::ostream& out, const account_t& account);
|
||||||
|
|
|
||||||
85
compare.cc
Normal file
85
compare.cc
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compare.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right)
|
||||||
|
{
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
|
||||||
|
xact_t::xdata_t& lxdata(left->xdata());
|
||||||
|
if (! lxdata.has_flags(XACT_EXT_SORT_CALC)) {
|
||||||
|
lxdata.sort_value = sort_order.calc(*left);
|
||||||
|
lxdata.sort_value.reduce();
|
||||||
|
lxdata.add_flags(XACT_EXT_SORT_CALC);
|
||||||
|
}
|
||||||
|
|
||||||
|
xact_t::xdata_t& rxdata(right->xdata());
|
||||||
|
if (! rxdata.has_flags(XACT_EXT_SORT_CALC)) {
|
||||||
|
rxdata.sort_value = sort_order.calc(*right);
|
||||||
|
rxdata.sort_value.reduce();
|
||||||
|
rxdata.add_flags(XACT_EXT_SORT_CALC);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("ledger.walk.compare_items_xact",
|
||||||
|
"lxdata.sort_value = " << lxdata.sort_value);
|
||||||
|
DEBUG("ledger.walk.compare_items_xact",
|
||||||
|
"rxdata.sort_value = " << rxdata.sort_value);
|
||||||
|
|
||||||
|
return lxdata.sort_value < rxdata.sort_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool compare_items<account_t>::operator()(account_t * left, account_t * right)
|
||||||
|
{
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
|
||||||
|
account_t::xdata_t& lxdata(left->xdata());
|
||||||
|
if (! lxdata.has_flags(ACCOUNT_EXT_SORT_CALC)) {
|
||||||
|
lxdata.sort_value = sort_order.calc(*left);
|
||||||
|
lxdata.add_flags(ACCOUNT_EXT_SORT_CALC);
|
||||||
|
}
|
||||||
|
|
||||||
|
account_t::xdata_t& rxdata(right->xdata());
|
||||||
|
if (! rxdata.has_flags(ACCOUNT_EXT_SORT_CALC)) {
|
||||||
|
rxdata.sort_value = sort_order.calc(*right);
|
||||||
|
rxdata.add_flags(ACCOUNT_EXT_SORT_CALC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lxdata.sort_value < rxdata.sort_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
77
compare.h
Normal file
77
compare.h
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _COMPARE_H
|
||||||
|
#define _COMPARE_H
|
||||||
|
|
||||||
|
#include "expr.h"
|
||||||
|
#include "xact.h"
|
||||||
|
#include "account.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class compare_items
|
||||||
|
{
|
||||||
|
expr_t sort_order;
|
||||||
|
|
||||||
|
compare_items();
|
||||||
|
|
||||||
|
public:
|
||||||
|
compare_items(const compare_items& other) : sort_order(other.sort_order) {
|
||||||
|
TRACE_CTOR(compare_items, "copy");
|
||||||
|
}
|
||||||
|
compare_items(const expr_t& _sort_order) : sort_order(_sort_order) {
|
||||||
|
TRACE_CTOR(compare_items, "const value_expr&");
|
||||||
|
}
|
||||||
|
~compare_items() throw() {
|
||||||
|
TRACE_DTOR(compare_items);
|
||||||
|
}
|
||||||
|
bool operator()(T * left, T * right);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool compare_items<T>::operator()(T * left, T * right)
|
||||||
|
{
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
return sort_order.calc(*left) < sort_order.calc(*right);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right);
|
||||||
|
template <>
|
||||||
|
bool compare_items<account_t>::operator()(account_t * left,
|
||||||
|
account_t * right);
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _COMPARE_H
|
||||||
2
csv.h
2
csv.h
|
|
@ -32,7 +32,7 @@
|
||||||
#ifndef _CSV_H
|
#ifndef _CSV_H
|
||||||
#define _CSV_H
|
#define _CSV_H
|
||||||
|
|
||||||
#include "journal.h"
|
#include "handler.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
#include "derive.h"
|
#include "derive.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "walk.h"
|
#include "iterators.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ entry_t * derive_new_entry(report_t& report,
|
||||||
|
|
||||||
report.sum_all_accounts();
|
report.sum_all_accounts();
|
||||||
|
|
||||||
value_t total = account_xdata(*acct).total;
|
value_t total = acct->xdata().total;
|
||||||
if (total.is_type(value_t::AMOUNT))
|
if (total.is_type(value_t::AMOUNT))
|
||||||
xact->amount.set_commodity(total.as_amount().commodity());
|
xact->amount.set_commodity(total.as_amount().commodity());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
emacs.cc
1
emacs.cc
|
|
@ -30,6 +30,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "emacs.h"
|
#include "emacs.h"
|
||||||
|
#include "account.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
2
emacs.h
2
emacs.h
|
|
@ -32,7 +32,7 @@
|
||||||
#ifndef _EMACS_H
|
#ifndef _EMACS_H
|
||||||
#define _EMACS_H
|
#define _EMACS_H
|
||||||
|
|
||||||
#include "journal.h"
|
#include "handler.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
|
||||||
|
|
@ -1,89 +1,51 @@
|
||||||
#include "walk.h"
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "filters.h"
|
||||||
|
#include "iterators.h"
|
||||||
|
#include "compare.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "textual.h"
|
#include "textual.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
template <>
|
pass_down_xacts::pass_down_xacts(xact_handler_ptr handler,
|
||||||
bool compare_items<xact_t>::operator()(xact_t * left, xact_t * right)
|
xacts_iterator& iter)
|
||||||
|
: item_handler<xact_t>(handler)
|
||||||
{
|
{
|
||||||
assert(left);
|
TRACE_CTOR(pass_down_xacts, "xact_handler_ptr, xacts_iterator");
|
||||||
assert(right);
|
|
||||||
|
|
||||||
xact_t::xdata_t& lxdata(left->xdata());
|
for (xact_t * xact = iter(); xact; xact = iter())
|
||||||
if (! lxdata.has_flags(XACT_EXT_SORT_CALC)) {
|
item_handler<xact_t>::operator()(*xact);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void truncate_entries::flush()
|
void truncate_entries::flush()
|
||||||
|
|
@ -136,9 +98,8 @@ void truncate_entries::flush()
|
||||||
void set_account_value::operator()(xact_t& xact)
|
void set_account_value::operator()(xact_t& xact)
|
||||||
{
|
{
|
||||||
account_t * acct = xact.reported_account();
|
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);
|
xact.add_to_value(xdata.value);
|
||||||
|
|
||||||
xdata.count++;
|
xdata.count++;
|
||||||
|
|
@ -233,10 +194,10 @@ void handle_value(const value_t& value,
|
||||||
// the xact as such. This allows subtotal reports to show
|
// the xact as such. This allows subtotal reports to show
|
||||||
// "(Account)" for accounts that contain only virtual xacts.
|
// "(Account)" for accounts that contain only virtual xacts.
|
||||||
|
|
||||||
if (account && account_has_xdata(*account))
|
if (account && account->has_xdata())
|
||||||
if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_NON_VIRTUALS)) {
|
if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) {
|
||||||
xact.add_flags(XACT_VIRTUAL);
|
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);
|
xact.add_flags(XACT_BALANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,9 +420,9 @@ void subtotal_xacts::operator()(xact_t& xact)
|
||||||
// that contain only virtual xacts.
|
// that contain only virtual xacts.
|
||||||
|
|
||||||
if (! xact.has_flags(XACT_VIRTUAL))
|
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))
|
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)
|
void interval_xacts::report_subtotal(const date_t& date)
|
||||||
|
|
@ -781,158 +742,14 @@ void forecast_xacts::flush()
|
||||||
item_handler<xact_t>::flush();
|
item_handler<xact_t>::flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
pass_down_accounts::pass_down_accounts(acct_handler_ptr handler,
|
||||||
bool compare_items<account_t>::operator()(account_t * left, account_t * right)
|
accounts_iterator& iter)
|
||||||
|
: item_handler<account_t>(handler)
|
||||||
{
|
{
|
||||||
assert(left);
|
TRACE_CTOR(pass_down_accounts,
|
||||||
assert(right);
|
"acct_handler_ptr, accounts_iterator");
|
||||||
|
for (account_t * account = iter(); account; account = iter())
|
||||||
account_xdata_t& lxdata(account_xdata(*left));
|
item_handler<account_t>::operator()(*account);
|
||||||
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++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
@ -1,136 +1,47 @@
|
||||||
#ifndef _WALK_H
|
/*
|
||||||
#define _WALK_H
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "journal.h"
|
#ifndef _FILTERS_H
|
||||||
|
#define _FILTERS_H
|
||||||
|
|
||||||
|
#include "handler.h"
|
||||||
|
#include "predicate.h"
|
||||||
#include "entry.h"
|
#include "entry.h"
|
||||||
#include "account.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
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>
|
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>
|
class pass_down_xacts : public item_handler<xact_t>
|
||||||
{
|
{
|
||||||
pass_down_xacts();
|
pass_down_xacts();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
pass_down_xacts(xact_handler_ptr handler, xacts_iterator& iter)
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~pass_down_xacts() {
|
virtual ~pass_down_xacts() {
|
||||||
TRACE_DTOR(pass_down_xacts);
|
TRACE_DTOR(pass_down_xacts);
|
||||||
|
|
@ -265,7 +171,7 @@ public:
|
||||||
class sort_entries : public item_handler<xact_t>
|
class sort_entries : public item_handler<xact_t>
|
||||||
{
|
{
|
||||||
sort_xacts sorter;
|
sort_xacts sorter;
|
||||||
entry_t * last_entry;
|
entry_t * last_entry;
|
||||||
|
|
||||||
sort_entries();
|
sort_entries();
|
||||||
|
|
||||||
|
|
@ -432,7 +338,7 @@ public:
|
||||||
class related_xacts : public item_handler<xact_t>
|
class related_xacts : public item_handler<xact_t>
|
||||||
{
|
{
|
||||||
xacts_list xacts;
|
xacts_list xacts;
|
||||||
bool also_matching;
|
bool also_matching;
|
||||||
|
|
||||||
related_xacts();
|
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>
|
class set_comm_as_payee : public item_handler<xact_t>
|
||||||
{
|
{
|
||||||
std::list<entry_t> entry_temps;
|
std::list<entry_t> entry_temps;
|
||||||
std::list<xact_t> xact_temps;
|
std::list<xact_t> xact_temps;
|
||||||
|
|
||||||
set_comm_as_payee();
|
set_comm_as_payee();
|
||||||
|
|
||||||
|
|
@ -646,8 +552,8 @@ public:
|
||||||
|
|
||||||
class set_code_as_payee : public item_handler<xact_t>
|
class set_code_as_payee : public item_handler<xact_t>
|
||||||
{
|
{
|
||||||
std::list<entry_t> entry_temps;
|
std::list<entry_t> entry_temps;
|
||||||
std::list<xact_t> xact_temps;
|
std::list<xact_t> xact_temps;
|
||||||
|
|
||||||
set_code_as_payee();
|
set_code_as_payee();
|
||||||
|
|
||||||
|
|
@ -694,9 +600,9 @@ protected:
|
||||||
typedef std::pair<interval_t, xact_t *> pending_xacts_pair;
|
typedef std::pair<interval_t, xact_t *> pending_xacts_pair;
|
||||||
typedef std::list<pending_xacts_pair> pending_xacts_list;
|
typedef std::list<pending_xacts_pair> pending_xacts_list;
|
||||||
|
|
||||||
pending_xacts_list pending_xacts;
|
pending_xacts_list pending_xacts;
|
||||||
std::list<entry_t> entry_temps;
|
std::list<entry_t> entry_temps;
|
||||||
std::list<xact_t> xact_temps;
|
std::list<xact_t> xact_temps;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
generate_xacts(xact_handler_ptr handler)
|
generate_xacts(xact_handler_ptr handler)
|
||||||
|
|
@ -714,12 +620,12 @@ public:
|
||||||
virtual void add_xact(const interval_t& period, xact_t& xact);
|
virtual void add_xact(const interval_t& period, xact_t& xact);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class budget_xacts : public generate_xacts
|
||||||
|
{
|
||||||
#define BUDGET_NO_BUDGET 0x00
|
#define BUDGET_NO_BUDGET 0x00
|
||||||
#define BUDGET_BUDGETED 0x01
|
#define BUDGET_BUDGETED 0x01
|
||||||
#define BUDGET_UNBUDGETED 0x02
|
#define BUDGET_UNBUDGETED 0x02
|
||||||
|
|
||||||
class budget_xacts : public generate_xacts
|
|
||||||
{
|
|
||||||
unsigned short flags;
|
unsigned short flags;
|
||||||
|
|
||||||
budget_xacts();
|
budget_xacts();
|
||||||
|
|
@ -764,204 +670,33 @@ class forecast_xacts : public generate_xacts
|
||||||
virtual void flush();
|
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>
|
class clear_account_xdata : public item_handler<account_t>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void operator()(account_t& acct) {
|
virtual void operator()(account_t& acct) {
|
||||||
if (acct.data) {
|
acct.clear_xdata();
|
||||||
checked_delete(static_cast<account_xdata_t *>(acct.data));
|
|
||||||
acct.data = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Iterator>
|
class accounts_iterator;
|
||||||
|
|
||||||
class pass_down_accounts : public item_handler<account_t>
|
class pass_down_accounts : public item_handler<account_t>
|
||||||
{
|
{
|
||||||
pass_down_accounts();
|
pass_down_accounts();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
pass_down_accounts(acct_handler_ptr handler, Iterator& iter)
|
pass_down_accounts(acct_handler_ptr handler, accounts_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~pass_down_accounts() {
|
virtual ~pass_down_accounts() {
|
||||||
TRACE_DTOR(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
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _WALK_H
|
#endif // _FILTERS_H
|
||||||
13
format.cc
13
format.cc
|
|
@ -30,10 +30,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "error.h"
|
#include "account.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -70,15 +67,15 @@ void format_t::element_t::dump(std::ostream& out) const
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
string partial_account_name(const account_t& account)
|
string partial_account_name(account_t& account)
|
||||||
{
|
{
|
||||||
string name;
|
string name;
|
||||||
|
|
||||||
for (const account_t * acct = &account;
|
for (account_t * acct = &account;
|
||||||
acct && acct->parent;
|
acct && acct->parent;
|
||||||
acct = acct->parent) {
|
acct = acct->parent) {
|
||||||
if (account_has_xdata(*acct) &&
|
if (acct->has_xdata() &&
|
||||||
account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED)
|
acct->xdata().has_flags(ACCOUNT_EXT_DISPLAYED))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
|
|
|
||||||
1
format.h
1
format.h
|
|
@ -34,7 +34,6 @@
|
||||||
|
|
||||||
#include "journal.h"
|
#include "journal.h"
|
||||||
#include "expr.h"
|
#include "expr.h"
|
||||||
#include "walk.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
19
gnucash.cc
19
gnucash.cc
|
|
@ -30,24 +30,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gnucash.h"
|
#include "gnucash.h"
|
||||||
#include "journal.h"
|
#include "account.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
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
72
handler.h
Normal file
72
handler.h
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HANDLER_H
|
||||||
|
#define _HANDLER_H
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "xact.h"
|
||||||
|
#include "account.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct item_handler : public noncopyable
|
||||||
|
{
|
||||||
|
shared_ptr<item_handler> handler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
item_handler() {
|
||||||
|
TRACE_CTOR(item_handler, "");
|
||||||
|
}
|
||||||
|
item_handler(shared_ptr<item_handler> _handler) : handler(_handler) {
|
||||||
|
TRACE_CTOR(item_handler, "shared_ptr<item_handler>");
|
||||||
|
}
|
||||||
|
virtual ~item_handler() {
|
||||||
|
TRACE_DTOR(item_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void flush() {
|
||||||
|
if (handler.get())
|
||||||
|
handler->flush();
|
||||||
|
}
|
||||||
|
virtual void operator()(T& item) {
|
||||||
|
if (handler.get())
|
||||||
|
(*handler.get())(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef shared_ptr<item_handler<xact_t> > xact_handler_ptr;
|
||||||
|
typedef shared_ptr<item_handler<account_t> > acct_handler_ptr;
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _HANDLER_H
|
||||||
205
help.cc
Normal file
205
help.cc
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "help.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
void help(std::ostream& out)
|
||||||
|
{
|
||||||
|
out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
|
||||||
|
Use -H to see all the help text on one page, or:\n\
|
||||||
|
--help-calc calculation options\n\
|
||||||
|
--help-disp display options\n\
|
||||||
|
--help-comm commodity options\n\n\
|
||||||
|
Basic options:\n\
|
||||||
|
-h, --help display this help text\n\
|
||||||
|
-v, --version show version information\n\
|
||||||
|
-f, --file FILE read ledger data from FILE\n\
|
||||||
|
-o, --output FILE write output to FILE\n\
|
||||||
|
-i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
|
||||||
|
--cache FILE use FILE as a binary cache when --file is not used\n\
|
||||||
|
--no-cache don't use a cache, even if it would be appropriate\n\
|
||||||
|
-a, --account NAME use NAME for the default account (useful with QIF)\n\n\
|
||||||
|
Commands:\n\
|
||||||
|
balance [REGEXP]... show balance totals for matching accounts\n\
|
||||||
|
register [REGEXP]... show register of matching transactions\n\
|
||||||
|
print [REGEXP]... print all matching entries\n\
|
||||||
|
xml [REGEXP]... print matching entries in XML format\n\
|
||||||
|
equity [REGEXP]... output equity entries for matching accounts\n\
|
||||||
|
prices [REGEXP]... display price history for matching commodities\n\
|
||||||
|
entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_help(std::ostream& out)
|
||||||
|
{
|
||||||
|
out << "Options to control how a report is calculated:\n\
|
||||||
|
-c, --current show only current and past entries (not future)\n\
|
||||||
|
-b, --begin DATE set report begin date\n\
|
||||||
|
-e, --end DATE set report end date\n\
|
||||||
|
-p, --period STR report using the given period\n\
|
||||||
|
--period-sort EXPR sort each report period's entries by EXPR\n\
|
||||||
|
-C, --cleared consider only cleared transactions\n\
|
||||||
|
-U, --uncleared consider only uncleared transactions\n\
|
||||||
|
-R, --real consider only real (non-virtual) transactions\n\
|
||||||
|
-L, --actual consider only actual (non-automated) transactions\n\
|
||||||
|
-r, --related calculate report using related transactions\n\
|
||||||
|
--budget generate budget entries based on periodic entries\n\
|
||||||
|
--add-budget show all transactions plus the budget\n\
|
||||||
|
--unbudgeted show only unbudgeted transactions\n\
|
||||||
|
--forecast EXPR generate forecast entries while EXPR is true\n\
|
||||||
|
-l, --limit EXPR calculate only transactions matching EXPR\n\
|
||||||
|
-t, --amount EXPR use EXPR to calculate the displayed amount\n\
|
||||||
|
-T, --total EXPR use EXPR to calculate the displayed total\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void disp_help(std::ostream& out)
|
||||||
|
{
|
||||||
|
out << "Output to control how report results are displayed:\n\
|
||||||
|
-n, --collapse register: collapse entries; balance: no grand total\n\
|
||||||
|
-s, --subtotal balance: show sub-accounts; other: show subtotals\n\
|
||||||
|
-P, --by-payee show summarized totals by payee\n\
|
||||||
|
-x, --comm-as-payee set commodity name as the payee, for reporting\n\
|
||||||
|
-E, --empty balance: show accounts with zero balance\n\
|
||||||
|
-W, --weekly show weekly sub-totals\n\
|
||||||
|
-M, --monthly show monthly sub-totals\n\
|
||||||
|
-Y, --yearly show yearly sub-totals\n\
|
||||||
|
--dow show a days-of-the-week report\n\
|
||||||
|
-S, --sort EXPR sort report according to the value expression EXPR\n\
|
||||||
|
-w, --wide for the default register report, use 132 columns\n\
|
||||||
|
--head COUNT show only the first COUNT entries (negative inverts)\n\
|
||||||
|
--tail COUNT show only the last COUNT entries (negative inverts)\n\
|
||||||
|
--pager PAGER send all output through the given PAGER program\n\
|
||||||
|
-A, --average report average transaction amount\n\
|
||||||
|
-D, --deviation report deviation from the average\n\
|
||||||
|
-%, --percentage report balance totals as a percentile of the parent\n\
|
||||||
|
--totals in the \"xml\" report, include running total\n\
|
||||||
|
-j, --amount-data print only raw amount data (useful for scripting)\n\
|
||||||
|
-J, --total-data print only raw total data\n\
|
||||||
|
-d, --display EXPR display only transactions matching EXPR\n\
|
||||||
|
-y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
|
||||||
|
-F, --format STR use STR as the format; for each report type, use:\n\
|
||||||
|
--balance-format --register-format --print-format\n\
|
||||||
|
--plot-amount-format --plot-total-format --equity-format\n\
|
||||||
|
--prices-format --wide-register-format\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void comm_help(std::ostream& out)
|
||||||
|
{
|
||||||
|
out << "Options to control how commodity values are determined:\n\
|
||||||
|
--price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
|
||||||
|
-Z, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
|
||||||
|
-Q, --download download price information when needed\n\
|
||||||
|
-O, --quantity report commodity totals (this is the default)\n\
|
||||||
|
-B, --basis report cost basis of commodities\n\
|
||||||
|
-V, --market report last known market value\n\
|
||||||
|
-g, --performance report gain/loss for each displayed transaction\n\
|
||||||
|
-G, --gain report net gain/loss\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void full_help(std::ostream& out)
|
||||||
|
{
|
||||||
|
out << "usage: ledger [options] COMMAND [ACCT REGEX]... [-- [PAYEE REGEX]...]\n\n\
|
||||||
|
Basic options:\n\
|
||||||
|
-H, --full-help display this help text\n\
|
||||||
|
-h, --help display summarized help text\n\
|
||||||
|
-v, --version show version information\n\
|
||||||
|
-f, --file FILE read ledger data from FILE\n\
|
||||||
|
-o, --output FILE write output to FILE\n\
|
||||||
|
-i, --init-file FILE initialize ledger using FILE (default: ~/.ledgerrc)\n\
|
||||||
|
--cache FILE use FILE as a binary cache when --file is not used\n\
|
||||||
|
--no-cache don't use a cache, even if it would be appropriate\n\
|
||||||
|
-a, --account NAME use NAME for the default account (useful with QIF)\n\n\
|
||||||
|
Report filtering:\n\
|
||||||
|
-c, --current show only current and past entries (not future)\n\
|
||||||
|
-b, --begin DATE set report begin date\n\
|
||||||
|
-e, --end DATE set report end date\n\
|
||||||
|
-p, --period STR report using the given period\n\
|
||||||
|
--period-sort EXPR sort each report period's entries by EXPR\n\
|
||||||
|
-C, --cleared consider only cleared transactions\n\
|
||||||
|
-U, --uncleared consider only uncleared transactions\n\
|
||||||
|
-R, --real consider only real (non-virtual) transactions\n\
|
||||||
|
-L, --actual consider only actual (non-automated) transactions\n\
|
||||||
|
-r, --related calculate report using related transactions\n\
|
||||||
|
--budget generate budget entries based on periodic entries\n\
|
||||||
|
--add-budget show all transactions plus the budget\n\
|
||||||
|
--unbudgeted show only unbudgeted transactions\n\
|
||||||
|
--forecast EXPR generate forecast entries while EXPR is true\n\
|
||||||
|
-l, --limit EXPR calculate only transactions matching EXPR\n\
|
||||||
|
-t, --amount EXPR use EXPR to calculate the displayed amount\n\
|
||||||
|
-T, --total EXPR use EXPR to calculate the displayed total\n\n\
|
||||||
|
Output customization:\n\
|
||||||
|
-n, --collapse register: collapse entries; balance: no grand total\n\
|
||||||
|
-s, --subtotal balance: show sub-accounts; other: show subtotals\n\
|
||||||
|
-P, --by-payee show summarized totals by payee\n\
|
||||||
|
-x, --comm-as-payee set commodity name as the payee, for reporting\n\
|
||||||
|
-E, --empty balance: show accounts with zero balance\n\
|
||||||
|
-W, --weekly show weekly sub-totals\n\
|
||||||
|
-M, --monthly show monthly sub-totals\n\
|
||||||
|
-Y, --yearly show yearly sub-totals\n\
|
||||||
|
--dow show a days-of-the-week report\n\
|
||||||
|
-S, --sort EXPR sort report according to the value expression EXPR\n\
|
||||||
|
-w, --wide for the default register report, use 132 columns\n\
|
||||||
|
--head COUNT show only the first COUNT entries (negative inverts)\n\
|
||||||
|
--tail COUNT show only the last COUNT entries (negative inverts)\n\
|
||||||
|
--pager PAGER send all output through the given PAGER program\n\
|
||||||
|
-A, --average report average transaction amount\n\
|
||||||
|
-D, --deviation report deviation from the average\n\
|
||||||
|
-%, --percentage report balance totals as a percentile of the parent\n\
|
||||||
|
--totals in the \"xml\" report, include running total\n\
|
||||||
|
-j, --amount-data print only raw amount data (useful for scripting)\n\
|
||||||
|
-J, --total-data print only raw total data\n\
|
||||||
|
-d, --display EXPR display only transactions matching EXPR\n\
|
||||||
|
-y, --date-format STR use STR as the date format (default: %Y/%m/%d)\n\
|
||||||
|
-F, --format STR use STR as the format; for each report type, use:\n\
|
||||||
|
--balance-format --register-format --print-format\n\
|
||||||
|
--plot-amount-format --plot-total-format --equity-format\n\
|
||||||
|
--prices-format --wide-register-format\n\n\
|
||||||
|
Commodity reporting:\n\
|
||||||
|
--price-db FILE sets the price database to FILE (def: ~/.pricedb)\n\
|
||||||
|
-L, --price-exp MINS download quotes only if newer than MINS (def: 1440)\n\
|
||||||
|
-Q, --download download price information when needed\n\
|
||||||
|
-O, --quantity report commodity totals (this is the default)\n\
|
||||||
|
-B, --basis report cost basis of commodities\n\
|
||||||
|
-V, --market report last known market value\n\
|
||||||
|
-g, --performance report gain/loss for each displayed transaction\n\
|
||||||
|
-G, --gain report net gain/loss\n\n\
|
||||||
|
Commands:\n\
|
||||||
|
balance [REGEXP]... show balance totals for matching accounts\n\
|
||||||
|
register [REGEXP]... show register of matching transactions\n\
|
||||||
|
print [REGEXP]... print all matching entries\n\
|
||||||
|
xml [REGEXP]... print matching entries in XML format\n\
|
||||||
|
equity [REGEXP]... output equity entries for matching accounts\n\
|
||||||
|
prices [REGEXP]... display price history for matching commodities\n\
|
||||||
|
entry DATE PAYEE AMT output a derived entry, based on the arguments\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
49
help.h
Normal file
49
help.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HELP_H
|
||||||
|
#define _HELP_H
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
void help(std::ostream& out);
|
||||||
|
|
||||||
|
void calc_help(std::ostream& out);
|
||||||
|
void disp_help(std::ostream& out);
|
||||||
|
void comm_help(std::ostream& out);
|
||||||
|
|
||||||
|
void full_help(std::ostream& out);
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _HELP_H
|
||||||
200
iterators.cc
Normal file
200
iterators.cc
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "iterators.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "compare.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
void entries_iterator::reset(session_t& session)
|
||||||
|
{
|
||||||
|
journals_i = session.journals.begin();
|
||||||
|
journals_end = session.journals.end();
|
||||||
|
|
||||||
|
journals_uninitialized = false;
|
||||||
|
|
||||||
|
if (journals_i != journals_end) {
|
||||||
|
entries_i = (*journals_i).entries.begin();
|
||||||
|
entries_end = (*journals_i).entries.end();
|
||||||
|
|
||||||
|
entries_uninitialized = false;
|
||||||
|
} else {
|
||||||
|
entries_uninitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_t * entries_iterator::operator()()
|
||||||
|
{
|
||||||
|
if (entries_i == entries_end) {
|
||||||
|
journals_i++;
|
||||||
|
if (journals_i == journals_end)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
entries_i = (*journals_i).entries.begin();
|
||||||
|
entries_end = (*journals_i).entries.end();
|
||||||
|
}
|
||||||
|
return *entries_i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_xacts_iterator::reset(session_t& session)
|
||||||
|
{
|
||||||
|
entries.reset(session);
|
||||||
|
entry_t * entry = entries();
|
||||||
|
if (entry != NULL)
|
||||||
|
xacts.reset(*entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
xact_t * session_xacts_iterator::operator()()
|
||||||
|
{
|
||||||
|
xact_t * xact = xacts();
|
||||||
|
if (xact == NULL) {
|
||||||
|
entry_t * entry = entries();
|
||||||
|
if (entry != NULL) {
|
||||||
|
xacts.reset(*entry);
|
||||||
|
xact = xacts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xact;
|
||||||
|
}
|
||||||
|
|
||||||
|
account_t * basic_accounts_iterator::operator()()
|
||||||
|
{
|
||||||
|
while (! accounts_i.empty() &&
|
||||||
|
accounts_i.back() == accounts_end.back()) {
|
||||||
|
accounts_i.pop_back();
|
||||||
|
accounts_end.pop_back();
|
||||||
|
}
|
||||||
|
if (accounts_i.empty())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
account_t * account = (*(accounts_i.back()++)).second;
|
||||||
|
assert(account);
|
||||||
|
|
||||||
|
// If this account has children, queue them up to be iterated next.
|
||||||
|
if (! account->accounts.empty())
|
||||||
|
push_back(*account);
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sorted_accounts_iterator::sort_accounts(account_t& account,
|
||||||
|
accounts_deque_t& deque)
|
||||||
|
{
|
||||||
|
foreach (accounts_map::value_type& pair, account.accounts)
|
||||||
|
deque.push_back(pair.second);
|
||||||
|
|
||||||
|
std::stable_sort(deque.begin(), deque.end(),
|
||||||
|
compare_items<account_t>(sort_cmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
account_t * sorted_accounts_iterator::operator()()
|
||||||
|
{
|
||||||
|
while (! sorted_accounts_i.empty() &&
|
||||||
|
sorted_accounts_i.back() == sorted_accounts_end.back()) {
|
||||||
|
sorted_accounts_i.pop_back();
|
||||||
|
sorted_accounts_end.pop_back();
|
||||||
|
assert(! accounts_list.empty());
|
||||||
|
accounts_list.pop_back();
|
||||||
|
}
|
||||||
|
if (sorted_accounts_i.empty())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
account_t * account = *sorted_accounts_i.back()++;
|
||||||
|
assert(account);
|
||||||
|
|
||||||
|
// If this account has children, queue them up to be iterated next.
|
||||||
|
if (! account->accounts.empty())
|
||||||
|
push_back(*account);
|
||||||
|
|
||||||
|
account->xdata().drop_flags(ACCOUNT_EXT_SORT_CALC);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
void journals_iterator::reset(session_t& session)
|
||||||
|
{
|
||||||
|
journals_i = session.journals.begin();
|
||||||
|
journals_end = session.journals.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
journal_t * journals_iterator::operator()()
|
||||||
|
{
|
||||||
|
if (journals_i == journals_end)
|
||||||
|
return NULL;
|
||||||
|
return &(*journals_i++);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// jww (2008-08-03): This needs to be changed into a commodities->xacts
|
||||||
|
// iterator.
|
||||||
|
|
||||||
|
// jww (2008-08-03): We then could also use a payees->xacts iterator
|
||||||
|
|
||||||
|
void walk_commodities(commodity_pool_t::commodities_by_ident& commodities,
|
||||||
|
item_handler<xact_t>& handler)
|
||||||
|
{
|
||||||
|
std::list<xact_t> xact_temps;
|
||||||
|
std::list<entry_t> entry_temps;
|
||||||
|
std::list<account_t> acct_temps;
|
||||||
|
|
||||||
|
for (commodity_pool_t::commodities_by_ident::iterator
|
||||||
|
i = commodities.begin();
|
||||||
|
i != commodities.end();
|
||||||
|
i++) {
|
||||||
|
if ((*i)->has_flags(COMMODITY_STYLE_NOMARKET))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
entry_temps.push_back(entry_t());
|
||||||
|
acct_temps.push_back(account_t(NULL, (*i)->symbol()));
|
||||||
|
|
||||||
|
if ((*i)->history())
|
||||||
|
foreach (const commodity_t::history_map::value_type& pair,
|
||||||
|
(*i)->history()->prices) {
|
||||||
|
entry_temps.back()._date = pair.first.date();
|
||||||
|
|
||||||
|
xact_temps.push_back(xact_t(&acct_temps.back()));
|
||||||
|
xact_t& temp = xact_temps.back();
|
||||||
|
temp.entry = &entry_temps.back();
|
||||||
|
temp.amount = pair.second;
|
||||||
|
temp.add_flags(XACT_TEMP);
|
||||||
|
entry_temps.back().add_xact(&temp);
|
||||||
|
|
||||||
|
handler(xact_temps.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.flush();
|
||||||
|
|
||||||
|
clear_entries_xacts(entry_temps);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
226
iterators.h
Normal file
226
iterators.h
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ITERATORS_H
|
||||||
|
#define _ITERATORS_H
|
||||||
|
|
||||||
|
#include "journal.h"
|
||||||
|
#include "entry.h"
|
||||||
|
#include "account.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
class xacts_iterator : public noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual xact_t * operator()() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class entry_xacts_iterator : public xacts_iterator
|
||||||
|
{
|
||||||
|
xacts_list::iterator xacts_i;
|
||||||
|
xacts_list::iterator xacts_end;
|
||||||
|
|
||||||
|
bool xacts_uninitialized;
|
||||||
|
|
||||||
|
public:
|
||||||
|
entry_xacts_iterator() : xacts_uninitialized(true) {
|
||||||
|
TRACE_CTOR(entry_xacts_iterator, "");
|
||||||
|
}
|
||||||
|
entry_xacts_iterator(entry_t& entry)
|
||||||
|
: xacts_uninitialized(true) {
|
||||||
|
TRACE_CTOR(entry_xacts_iterator, "entry_t&");
|
||||||
|
reset(entry);
|
||||||
|
}
|
||||||
|
virtual ~entry_xacts_iterator() throw() {
|
||||||
|
TRACE_DTOR(entry_xacts_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(entry_t& entry) {
|
||||||
|
xacts_i = entry.xacts.begin();
|
||||||
|
xacts_end = entry.xacts.end();
|
||||||
|
|
||||||
|
xacts_uninitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual xact_t * operator()() {
|
||||||
|
if (xacts_i == xacts_end || xacts_uninitialized)
|
||||||
|
return NULL;
|
||||||
|
return *xacts_i++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class entries_iterator : public noncopyable
|
||||||
|
{
|
||||||
|
ptr_list<journal_t>::iterator journals_i;
|
||||||
|
ptr_list<journal_t>::iterator journals_end;
|
||||||
|
|
||||||
|
bool journals_uninitialized;
|
||||||
|
|
||||||
|
entries_list::iterator entries_i;
|
||||||
|
entries_list::iterator entries_end;
|
||||||
|
|
||||||
|
bool entries_uninitialized;
|
||||||
|
|
||||||
|
public:
|
||||||
|
entries_iterator()
|
||||||
|
: journals_uninitialized(true), entries_uninitialized(true) {
|
||||||
|
TRACE_CTOR(entries_iterator, "");
|
||||||
|
}
|
||||||
|
entries_iterator(session_t& session)
|
||||||
|
: journals_uninitialized(true), entries_uninitialized(true) {
|
||||||
|
TRACE_CTOR(entries_iterator, "session_t&");
|
||||||
|
reset(session);
|
||||||
|
}
|
||||||
|
~entries_iterator() throw() {
|
||||||
|
TRACE_DTOR(entries_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(session_t& session);
|
||||||
|
|
||||||
|
entry_t * operator()();
|
||||||
|
};
|
||||||
|
|
||||||
|
class session_xacts_iterator : public xacts_iterator
|
||||||
|
{
|
||||||
|
entries_iterator entries;
|
||||||
|
entry_xacts_iterator xacts;
|
||||||
|
|
||||||
|
public:
|
||||||
|
session_xacts_iterator() {
|
||||||
|
TRACE_CTOR(session_xacts_iterator, "");
|
||||||
|
}
|
||||||
|
session_xacts_iterator(session_t& session) {
|
||||||
|
TRACE_CTOR(session_xacts_iterator, "session_t&");
|
||||||
|
reset(session);
|
||||||
|
}
|
||||||
|
virtual ~session_xacts_iterator() throw() {
|
||||||
|
TRACE_DTOR(session_xacts_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(session_t& session);
|
||||||
|
|
||||||
|
virtual xact_t * operator()();
|
||||||
|
};
|
||||||
|
|
||||||
|
class accounts_iterator : public noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual account_t * operator()() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class basic_accounts_iterator : public accounts_iterator
|
||||||
|
{
|
||||||
|
std::list<accounts_map::const_iterator> accounts_i;
|
||||||
|
std::list<accounts_map::const_iterator> accounts_end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
basic_accounts_iterator() {
|
||||||
|
TRACE_CTOR(basic_accounts_iterator, "");
|
||||||
|
}
|
||||||
|
basic_accounts_iterator(account_t& account) {
|
||||||
|
TRACE_CTOR(basic_accounts_iterator, "account_t&");
|
||||||
|
push_back(account);
|
||||||
|
}
|
||||||
|
virtual ~basic_accounts_iterator() throw() {
|
||||||
|
TRACE_DTOR(basic_accounts_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(account_t& account) {
|
||||||
|
accounts_i.push_back(account.accounts.begin());
|
||||||
|
accounts_end.push_back(account.accounts.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual account_t * operator()();
|
||||||
|
};
|
||||||
|
|
||||||
|
class sorted_accounts_iterator : public accounts_iterator
|
||||||
|
{
|
||||||
|
expr_t sort_cmp;
|
||||||
|
|
||||||
|
typedef std::deque<account_t *> accounts_deque_t;
|
||||||
|
|
||||||
|
std::list<accounts_deque_t> accounts_list;
|
||||||
|
std::list<accounts_deque_t::const_iterator> sorted_accounts_i;
|
||||||
|
std::list<accounts_deque_t::const_iterator> sorted_accounts_end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sorted_accounts_iterator(const string& sort_order) {
|
||||||
|
TRACE_CTOR(sorted_accounts_iterator, "const string&");
|
||||||
|
sort_cmp = expr_t(sort_order);
|
||||||
|
}
|
||||||
|
sorted_accounts_iterator(account_t& account, const string& sort_order) {
|
||||||
|
TRACE_CTOR(sorted_accounts_iterator, "account_t&, const string&");
|
||||||
|
sort_cmp = expr_t(sort_order);
|
||||||
|
push_back(account);
|
||||||
|
}
|
||||||
|
virtual ~sorted_accounts_iterator() throw() {
|
||||||
|
TRACE_DTOR(sorted_accounts_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort_accounts(account_t& account, accounts_deque_t& deque);
|
||||||
|
|
||||||
|
void push_back(account_t& account) {
|
||||||
|
accounts_list.push_back(accounts_deque_t());
|
||||||
|
sort_accounts(account, accounts_list.back());
|
||||||
|
|
||||||
|
sorted_accounts_i.push_back(accounts_list.back().begin());
|
||||||
|
sorted_accounts_end.push_back(accounts_list.back().end());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual account_t * operator()();
|
||||||
|
};
|
||||||
|
|
||||||
|
class journals_iterator : public noncopyable
|
||||||
|
{
|
||||||
|
ptr_list<journal_t>::iterator journals_i;
|
||||||
|
ptr_list<journal_t>::iterator journals_end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
journals_iterator() {
|
||||||
|
TRACE_CTOR(journals_iterator, "");
|
||||||
|
}
|
||||||
|
journals_iterator(session_t& session) {
|
||||||
|
TRACE_CTOR(journals_iterator, "session_t&");
|
||||||
|
reset(session);
|
||||||
|
}
|
||||||
|
virtual ~journals_iterator() throw() {
|
||||||
|
TRACE_DTOR(journals_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(session_t& session);
|
||||||
|
|
||||||
|
virtual journal_t * operator()();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _ITERATORS_H
|
||||||
663
main.cc
663
main.cc
|
|
@ -29,15 +29,21 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* 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 "option.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "help.h"
|
||||||
|
|
||||||
|
#include "textual.h"
|
||||||
|
#include "qif.h"
|
||||||
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
||||||
|
#include "xml.h"
|
||||||
#include "gnucash.h"
|
#include "gnucash.h"
|
||||||
#endif
|
#endif
|
||||||
#include "qif.h"
|
#ifdef HAVE_LIBOFX
|
||||||
#include "ofx.h"
|
#include "ofx.h"
|
||||||
|
#endif
|
||||||
#include <ledger.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_UNIX_PIPES
|
#ifdef HAVE_UNIX_PIPES
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
@ -46,352 +52,351 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ledger {
|
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);
|
string format_name;
|
||||||
ptr_t<std::ostream> ostream(args, 1);
|
|
||||||
|
|
||||||
report->xacts_report
|
public:
|
||||||
(xact_handler_ptr(new format_xacts
|
xacts_report(const string& _format_name)
|
||||||
(*ostream, report->session.register_format)));
|
: format_name(_format_name) {}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_and_report(ledger::report_t& report, int argc, char * argv[],
|
value_t operator()(call_scope_t& args)
|
||||||
char * envp[])
|
{
|
||||||
{
|
ptr_t<std::ostream> ostream(args, 0);
|
||||||
using namespace ledger;
|
var_t<string> format(args, format_name);
|
||||||
|
|
||||||
session_t& session(report.session);
|
find_scope<report_t>(args).xacts_report
|
||||||
|
(xact_handler_ptr(new Formatter(*ostream, *format)));
|
||||||
// Handle the command-line arguments
|
return true;
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
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)
|
if (status == -1)
|
||||||
perror("dup2");
|
throw_(std::logic_error, "Failed to create pipe");
|
||||||
|
|
||||||
// Close unuseful file descriptors: the pipe's writing and
|
status = fork();
|
||||||
// reading ends (the latter is not needed anymore, after the
|
if (status < 0) {
|
||||||
// duplication).
|
throw_(std::logic_error, "Failed to fork child process");
|
||||||
close(pfd[1]);
|
}
|
||||||
close(pfd[0]);
|
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
|
// Close unuseful file descriptors: the pipe's writing and
|
||||||
// rightmost '/' character in the pager pathname. See manpage
|
// reading ends (the latter is not needed anymore, after the
|
||||||
// for strrchr.
|
// duplication).
|
||||||
execlp(report.pager->native_file_string().c_str(),
|
close(pfd[1]);
|
||||||
basename(*report.pager).c_str(), NULL);
|
close(pfd[0]);
|
||||||
perror("execl");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
else { // parent
|
|
||||||
close(pfd[0]);
|
|
||||||
out = new boost::fdostream(pfd[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Read the command word and create a command object based on it
|
// Find command name: its the substring starting right of the
|
||||||
|
// rightmost '/' character in the pager pathname. See manpage
|
||||||
string verb = *arg++;
|
// for strrchr.
|
||||||
|
execlp(report.pager->native_file_string().c_str(),
|
||||||
if (verb == "parse") {
|
basename(*report.pager).c_str(), NULL);
|
||||||
expr_t expr(*arg);
|
perror("execl");
|
||||||
|
exit(1);
|
||||||
*out << "Value expression as input: " << *arg << std::endl;
|
}
|
||||||
|
else { // parent
|
||||||
*out << "Value expression as parsed: ";
|
close(pfd[0]);
|
||||||
expr.print(*out, report);
|
out = new boost::fdostream(pfd[1]);
|
||||||
*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;
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the initialization file, which can only be textual; then
|
// Read the command word and see if it's any of the debugging commands
|
||||||
// parse the journal data.
|
// 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);
|
*out << "Value expression as parsed: ";
|
||||||
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;
|
|
||||||
expr.print(*out, report);
|
expr.print(*out, report);
|
||||||
*out << std::endl << std::endl;
|
*out << std::endl;
|
||||||
*out << "Result of calculation: ";
|
|
||||||
|
*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;
|
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[])
|
int main(int argc, char * argv[], char * envp[])
|
||||||
|
|
@ -469,7 +474,7 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
if (DO_VERIFY())
|
if (DO_VERIFY())
|
||||||
ledger::set_session_context();
|
ledger::set_session_context();
|
||||||
else
|
else
|
||||||
session.release(); // don't free anything!
|
session.release(); // don't free anything! just let it leak
|
||||||
}
|
}
|
||||||
catch (const std::exception& err) {
|
catch (const std::exception& err) {
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
|
|
|
||||||
169
option.cc
169
option.cc
|
|
@ -212,175 +212,6 @@ void process_arguments(int, char ** argv, const bool anywhere,
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#if 0
|
#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
|
// Basic options
|
||||||
|
|
|
||||||
282
output.cc
Normal file
282
output.cc
Normal file
|
|
@ -0,0 +1,282 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
format_xacts::format_xacts(std::ostream& _output_stream,
|
||||||
|
const string& format)
|
||||||
|
: output_stream(_output_stream), last_entry(NULL), last_xact(NULL)
|
||||||
|
{
|
||||||
|
TRACE_CTOR(format_xacts, "std::ostream&, const string&");
|
||||||
|
|
||||||
|
const char * f = format.c_str();
|
||||||
|
if (const char * p = std::strstr(f, "%/")) {
|
||||||
|
first_line_format.parse(string(f, 0, p - f));
|
||||||
|
next_lines_format.parse(string(p + 2));
|
||||||
|
} else {
|
||||||
|
first_line_format.parse(format);
|
||||||
|
next_lines_format.parse(format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_xacts::operator()(xact_t& xact)
|
||||||
|
{
|
||||||
|
if (! xact.has_xdata() ||
|
||||||
|
! xact.xdata().has_flags(XACT_EXT_DISPLAYED)) {
|
||||||
|
if (last_entry != xact.entry) {
|
||||||
|
first_line_format.format(output_stream, xact);
|
||||||
|
last_entry = xact.entry;
|
||||||
|
}
|
||||||
|
else if (last_xact && last_xact->date() != xact.date()) {
|
||||||
|
first_line_format.format(output_stream, xact);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
next_lines_format.format(output_stream, xact);
|
||||||
|
}
|
||||||
|
|
||||||
|
xact.xdata().add_flags(XACT_EXT_DISPLAYED);
|
||||||
|
last_xact = &xact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_entries::format_last_entry()
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
foreach (xact_t * xact, last_entry->xacts) {
|
||||||
|
if (xact->has_xdata() &&
|
||||||
|
xact->xdata().has_flags(XACT_EXT_TO_DISPLAY)) {
|
||||||
|
if (first) {
|
||||||
|
first_line_format.format(output_stream, *xact);
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
next_lines_format.format(output_stream, *xact);
|
||||||
|
}
|
||||||
|
xact->xdata().add_flags(XACT_EXT_DISPLAYED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_entries::operator()(xact_t& xact)
|
||||||
|
{
|
||||||
|
xact.xdata().add_flags(XACT_EXT_TO_DISPLAY);
|
||||||
|
|
||||||
|
if (last_entry && xact.entry != last_entry)
|
||||||
|
format_last_entry();
|
||||||
|
|
||||||
|
last_entry = xact.entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_entry(std::ostream& out, const entry_base_t& entry_base,
|
||||||
|
const string& prefix)
|
||||||
|
{
|
||||||
|
string print_format;
|
||||||
|
|
||||||
|
if (dynamic_cast<const entry_t *>(&entry_base)) {
|
||||||
|
print_format = (prefix + "%D %X%C%P\n" +
|
||||||
|
prefix + " %-34A %12o\n%/" +
|
||||||
|
prefix + " %-34A %12o\n");
|
||||||
|
}
|
||||||
|
else if (const auto_entry_t * entry =
|
||||||
|
dynamic_cast<const auto_entry_t *>(&entry_base)) {
|
||||||
|
out << "= " << entry->predicate.predicate.text() << '\n';
|
||||||
|
print_format = prefix + " %-34A %12o\n";
|
||||||
|
}
|
||||||
|
else if (const period_entry_t * entry =
|
||||||
|
dynamic_cast<const period_entry_t *>(&entry_base)) {
|
||||||
|
out << "~ " << entry->period_string << '\n';
|
||||||
|
print_format = prefix + " %-34A %12o\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
format_entries formatter(out, print_format);
|
||||||
|
walk_xacts(const_cast<xacts_list&>(entry_base.xacts), formatter);
|
||||||
|
formatter.flush();
|
||||||
|
|
||||||
|
clear_xact_xdata cleaner;
|
||||||
|
walk_xacts(const_cast<xacts_list&>(entry_base.xacts), cleaner);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_accounts::operator()(account_t& account)
|
||||||
|
{
|
||||||
|
if (display_account(account)) {
|
||||||
|
if (! account.parent) {
|
||||||
|
account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY);
|
||||||
|
} else {
|
||||||
|
format.format(output_stream, account);
|
||||||
|
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool format_accounts::disp_subaccounts_p(account_t& account,
|
||||||
|
account_t *& to_show)
|
||||||
|
{
|
||||||
|
bool display = false;
|
||||||
|
unsigned int counted = 0;
|
||||||
|
bool matches = disp_pred(account);
|
||||||
|
bool computed = false;
|
||||||
|
value_t acct_total;
|
||||||
|
value_t result;
|
||||||
|
|
||||||
|
to_show = NULL;
|
||||||
|
|
||||||
|
foreach (accounts_map::value_type pair, account.accounts) {
|
||||||
|
if (! disp_pred(*pair.second))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
compute_total(result, *pair.second);
|
||||||
|
#endif
|
||||||
|
if (! computed) {
|
||||||
|
#if 0
|
||||||
|
compute_total(acct_total, account);
|
||||||
|
#endif
|
||||||
|
computed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result != acct_total) || counted > 0) {
|
||||||
|
display = matches;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
to_show = pair.second;
|
||||||
|
counted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return display;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool format_accounts::display_account(account_t& account)
|
||||||
|
{
|
||||||
|
// Never display an account that has already been displayed.
|
||||||
|
if (account.has_xdata() &&
|
||||||
|
account.xdata().has_flags(ACCOUNT_EXT_DISPLAYED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// At this point, one of two possibilities exists: the account is a
|
||||||
|
// leaf which matches the predicate restrictions; or it is a parent
|
||||||
|
// and two or more children must be subtotaled; or it is a parent
|
||||||
|
// and its child has been hidden by the predicate. So first,
|
||||||
|
// determine if it is a parent that must be displayed regardless of
|
||||||
|
// the predicate.
|
||||||
|
|
||||||
|
account_t * account_to_show = NULL;
|
||||||
|
if (disp_subaccounts_p(account, account_to_show))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return ! account_to_show && disp_pred(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
format_equity::format_equity(std::ostream& _output_stream,
|
||||||
|
const string& _format,
|
||||||
|
const string& display_predicate)
|
||||||
|
: format_accounts(_output_stream, "", display_predicate)
|
||||||
|
{
|
||||||
|
const char * f = _format.c_str();
|
||||||
|
|
||||||
|
if (const char * p = std::strstr(f, "%/")) {
|
||||||
|
first_line_format.parse(string(f, 0, p - f));
|
||||||
|
next_lines_format.parse(string(p + 2));
|
||||||
|
} else {
|
||||||
|
first_line_format.parse(_format);
|
||||||
|
next_lines_format.parse(_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_t header_entry;
|
||||||
|
header_entry.payee = "Opening Balances";
|
||||||
|
header_entry._date = current_date;
|
||||||
|
first_line_format.format(output_stream, header_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_equity::flush()
|
||||||
|
{
|
||||||
|
account_t summary(NULL, "Equity:Opening Balances");
|
||||||
|
|
||||||
|
account_t::xdata_t& xdata(summary.xdata());
|
||||||
|
|
||||||
|
xdata.value = total.negate();
|
||||||
|
|
||||||
|
if (total.type() >= value_t::BALANCE) {
|
||||||
|
const balance_t * bal;
|
||||||
|
if (total.is_type(value_t::BALANCE))
|
||||||
|
bal = &(total.as_balance());
|
||||||
|
else if (total.is_type(value_t::BALANCE_PAIR))
|
||||||
|
bal = &(total.as_balance_pair().quantity());
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
|
||||||
|
xdata.value = pair.second;
|
||||||
|
xdata.value.negate();
|
||||||
|
next_lines_format.format(output_stream, summary);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next_lines_format.format(output_stream, summary);
|
||||||
|
}
|
||||||
|
output_stream.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_equity::operator()(account_t& account)
|
||||||
|
{
|
||||||
|
if (display_account(account)) {
|
||||||
|
if (account.has_xdata()) {
|
||||||
|
value_t val = account.xdata().value;
|
||||||
|
|
||||||
|
if (val.type() >= value_t::BALANCE) {
|
||||||
|
const balance_t * bal;
|
||||||
|
if (val.is_type(value_t::BALANCE))
|
||||||
|
bal = &(val.as_balance());
|
||||||
|
else if (val.is_type(value_t::BALANCE_PAIR))
|
||||||
|
bal = &(val.as_balance_pair().quantity());
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
foreach (balance_t::amounts_map::value_type pair, bal->amounts) {
|
||||||
|
account.xdata().value = pair.second;
|
||||||
|
next_lines_format.format(output_stream, account);
|
||||||
|
}
|
||||||
|
account.xdata().value = val;
|
||||||
|
} else {
|
||||||
|
next_lines_format.format(output_stream, account);
|
||||||
|
}
|
||||||
|
total += val;
|
||||||
|
}
|
||||||
|
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
143
output.h
Normal file
143
output.h
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of New Artisans LLC nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _OUTPUT_H
|
||||||
|
#define _OUTPUT_H
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
#include "handler.h"
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
class format_xacts : public item_handler<xact_t>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::ostream& output_stream;
|
||||||
|
format_t first_line_format;
|
||||||
|
format_t next_lines_format;
|
||||||
|
entry_t * last_entry;
|
||||||
|
xact_t * last_xact;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_xacts(std::ostream& _output_stream,
|
||||||
|
const string& format);
|
||||||
|
virtual ~format_xacts() {
|
||||||
|
TRACE_DTOR(format_xacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void flush() {
|
||||||
|
output_stream.flush();
|
||||||
|
}
|
||||||
|
virtual void operator()(xact_t& xact);
|
||||||
|
};
|
||||||
|
|
||||||
|
class format_entries : public format_xacts
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
format_entries(std::ostream& output_stream, const string& format)
|
||||||
|
: format_xacts(output_stream, format) {
|
||||||
|
TRACE_CTOR(format_entries, "std::ostream&, const string&");
|
||||||
|
}
|
||||||
|
virtual ~format_entries() {
|
||||||
|
TRACE_DTOR(format_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void format_last_entry();
|
||||||
|
|
||||||
|
virtual void flush() {
|
||||||
|
if (last_entry) {
|
||||||
|
format_last_entry();
|
||||||
|
last_entry = NULL;
|
||||||
|
}
|
||||||
|
format_xacts::flush();
|
||||||
|
}
|
||||||
|
virtual void operator()(xact_t& xact);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void print_entry(std::ostream& out,
|
||||||
|
const entry_base_t& entry,
|
||||||
|
const string& prefix = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class format_accounts : public item_handler<account_t>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::ostream& output_stream;
|
||||||
|
|
||||||
|
item_predicate<account_t> disp_pred;
|
||||||
|
|
||||||
|
bool disp_subaccounts_p(account_t& account, account_t *& to_show);
|
||||||
|
bool display_account(account_t& account);
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_t format;
|
||||||
|
|
||||||
|
format_accounts(std::ostream& _output_stream,
|
||||||
|
const string& _format,
|
||||||
|
const string& display_predicate = "")
|
||||||
|
: output_stream(_output_stream), disp_pred(display_predicate),
|
||||||
|
format(_format) {
|
||||||
|
TRACE_CTOR(format_accounts, "std::ostream&, const string&, const string&");
|
||||||
|
}
|
||||||
|
virtual ~format_accounts() {
|
||||||
|
TRACE_DTOR(format_accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void flush() {
|
||||||
|
output_stream.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void operator()(account_t& account);
|
||||||
|
};
|
||||||
|
|
||||||
|
class format_equity : public format_accounts
|
||||||
|
{
|
||||||
|
format_t first_line_format;
|
||||||
|
format_t next_lines_format;
|
||||||
|
|
||||||
|
mutable value_t total;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_equity(std::ostream& _output_stream,
|
||||||
|
const string& _format,
|
||||||
|
const string& display_predicate = "");
|
||||||
|
virtual ~format_equity() {
|
||||||
|
TRACE_DTOR(format_equity);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void flush();
|
||||||
|
virtual void operator()(account_t& account);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _OUTPUT_H
|
||||||
|
|
@ -30,7 +30,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "reconcile.h"
|
#include "reconcile.h"
|
||||||
#include "walk.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@
|
||||||
#define _RECONCILE_H
|
#define _RECONCILE_H
|
||||||
|
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "walk.h"
|
#include "iterators.h"
|
||||||
|
#include "filters.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
295
report.cc
295
report.cc
|
|
@ -372,7 +372,7 @@ report_t::chain_xact_handlers(xact_handler_ptr base_handler,
|
||||||
forecast_xacts * forecast_handler
|
forecast_xacts * forecast_handler
|
||||||
= new forecast_xacts(handler, forecast_limit);
|
= new forecast_xacts(handler, forecast_limit);
|
||||||
forecast_handler->add_period_entries(journal->period_entries);
|
forecast_handler->add_period_entries(journal->period_entries);
|
||||||
handler.reset(forecast_handler;
|
handler.reset(forecast_handler);
|
||||||
|
|
||||||
// See above, under budget_xacts.
|
// See above, under budget_xacts.
|
||||||
if (! predicate.empty())
|
if (! predicate.empty())
|
||||||
|
|
@ -415,7 +415,7 @@ void report_t::sum_all_accounts()
|
||||||
(chain_xact_handlers(xact_handler_ptr(new set_account_value), false),
|
(chain_xact_handlers(xact_handler_ptr(new set_account_value), false),
|
||||||
walker);
|
walker);
|
||||||
// no flush() needed with set_account_value
|
// no flush() needed with set_account_value
|
||||||
sum_accounts(*session.master);
|
session.master->calculate_sums();
|
||||||
}
|
}
|
||||||
|
|
||||||
void report_t::accounts_report(acct_handler_ptr handler,
|
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();
|
sum_all_accounts();
|
||||||
|
|
||||||
if (sort_string.empty()) {
|
if (sort_string.empty()) {
|
||||||
accounts_iterator walker(*session.master);
|
basic_accounts_iterator walker(*session.master);
|
||||||
pass_down_accounts<accounts_iterator>(handler, walker);
|
pass_down_accounts(handler, walker);
|
||||||
} else {
|
} else {
|
||||||
sorted_accounts_iterator walker(*session.master, sort_string);
|
sorted_accounts_iterator walker(*session.master, sort_string);
|
||||||
pass_down_accounts<sorted_accounts_iterator>(handler, walker);
|
pass_down_accounts(handler, walker);
|
||||||
}
|
}
|
||||||
handler->flush();
|
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)
|
expr_t::ptr_op_t report_t::lookup(const string& name)
|
||||||
{
|
{
|
||||||
const char * p = name.c_str();
|
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);
|
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
|
} // namespace ledger
|
||||||
|
|
|
||||||
116
report.h
116
report.h
|
|
@ -33,8 +33,7 @@
|
||||||
#define _REPORT_H
|
#define _REPORT_H
|
||||||
|
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "format.h"
|
#include "handler.h"
|
||||||
#include "walk.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -195,13 +194,6 @@ public:
|
||||||
|
|
||||||
void entry_report(const entry_t& entry, const string& format);
|
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
|
// Config options
|
||||||
//
|
//
|
||||||
|
|
@ -257,112 +249,6 @@ public:
|
||||||
virtual expr_t::ptr_op_t lookup(const string& name);
|
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
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _REPORT_H
|
#endif // _REPORT_H
|
||||||
|
|
|
||||||
47
session.cc
47
session.cc
|
|
@ -31,7 +31,9 @@
|
||||||
|
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "report.h"
|
#include "report.h"
|
||||||
#include "walk.h"
|
#include "handler.h"
|
||||||
|
#include "iterators.h"
|
||||||
|
#include "filters.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -256,9 +258,9 @@ void session_t::clean_xacts(entry_t& entry)
|
||||||
|
|
||||||
void session_t::clean_accounts()
|
void session_t::clean_accounts()
|
||||||
{
|
{
|
||||||
accounts_iterator acct_walker(*master);
|
basic_accounts_iterator acct_walker(*master);
|
||||||
pass_down_accounts<accounts_iterator>
|
pass_down_accounts(acct_handler_ptr(new clear_account_xdata),
|
||||||
(acct_handler_ptr(new clear_account_xdata), acct_walker);
|
acct_walker);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
@ -297,6 +299,43 @@ expr_t::ptr_op_t session_t::lookup(const string& name)
|
||||||
{
|
{
|
||||||
const char * p = name.c_str();
|
const char * p = name.c_str();
|
||||||
switch (*p) {
|
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':
|
case 'o':
|
||||||
if (std::strncmp(p, "opt_", 4) == 0) {
|
if (std::strncmp(p, "opt_", 4) == 0) {
|
||||||
p = p + 4;
|
p = p + 4;
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ xact_t * parse_xact(char * line, account_t * account, entry_t * entry = NULL)
|
||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
// Add this amount to the related account now
|
// 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 (! xact->amount.is_null()) {
|
||||||
if (xdata.value.is_null())
|
if (xdata.value.is_null())
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,7 @@
|
||||||
#define _TEXTUAL_H
|
#define _TEXTUAL_H
|
||||||
|
|
||||||
#include "journal.h"
|
#include "journal.h"
|
||||||
#include "format.h"
|
#include "handler.h"
|
||||||
#include "walk.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
28
token.cc
28
token.cc
|
|
@ -243,36 +243,8 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
|
||||||
kind = COLON;
|
kind = COLON;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
|
||||||
case 'C':
|
|
||||||
case 'p':
|
|
||||||
case 'w':
|
|
||||||
case 'W':
|
|
||||||
case 'e':
|
|
||||||
case '/': {
|
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);
|
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
|
// Read in the regexp
|
||||||
char buf[256];
|
char buf[256];
|
||||||
|
|
|
||||||
8
xact.h
8
xact.h
|
|
@ -184,10 +184,10 @@ public:
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// This variable holds a pointer to "extended data" which is usually
|
// This variable holds optional "extended data" which is usually produced
|
||||||
// produced only during reporting, and only for the transaction set being
|
// only during reporting, and only for the transaction set being reported.
|
||||||
// reported. It's a memory-saving measure to delay allocation until the
|
// It's a memory-saving measure to delay allocation until the last possible
|
||||||
// last possible moment.
|
// moment.
|
||||||
mutable optional<xdata_t> xdata_;
|
mutable optional<xdata_t> xdata_;
|
||||||
|
|
||||||
bool has_xdata() const {
|
bool has_xdata() const {
|
||||||
|
|
|
||||||
1
xml.h
1
xml.h
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
#include "journal.h"
|
#include "journal.h"
|
||||||
#include "report.h"
|
#include "report.h"
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue