rewrote the way registers are computed -- again

This commit is contained in:
John Wiegley 2004-08-04 03:12:26 -04:00
parent f570e6644f
commit 1741c80fe4
26 changed files with 1305 additions and 1154 deletions

View file

@ -1,17 +1,34 @@
CODE = amount.cc balance.cc account.cc ledger.cc \
item.cc expr.cc format.cc textual.cc binary.cc
CODE = account.cc \
amount.cc \
autoxact.cc \
balance.cc \
binary.cc \
datetime.cc \
error.cc \
format.cc \
ledger.cc \
textual.cc \
valexpr.cc \
walk.cc
OBJS = $(patsubst %.cc,%.o,$(CODE))
#CXX = cc
CXX = g++
CFLAGS = -Wall -ansi -pedantic
#DFLAGS = -O3 -fomit-frame-pointer
DFLAGS = -g -DDEBUG=1
#DFLAGS = -g -pg
INCS = -I/sw/include -I/usr/include/gcc/darwin/3.3/c++ -I/usr/include/gcc/darwin/3.3/c++/ppc-darwin
INCS = -I/sw/include \
-I/usr/include/gcc/darwin/3.3/c++ \
-I/usr/include/gcc/darwin/3.3/c++/ppc-darwin
LIBS = -L/sw/lib -lgmpxx -lgmp -lpcre
ifdef GNUCASH
CODE := $(CODE) gnucash.cc
OBJS := $(OBJS) gnucash.o
CFLAGS := $(CFLAGS) -DREAD_GNUCASH=1
INCS := $(INCS) -I/usr/include/httpd/xml
LIBS := $(LIBS) -L/sw/lib -lxmlparse
@ -34,8 +51,8 @@ libledger.a: $(OBJS)
ledger: libledger.a main.o
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -o $@ main.o -L. -lledger $(LIBS)
report: libledger.a report.cc
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -DTEST -o $@ report.cc \
valexpr: libledger.a valexpr.cc
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -DTEST -o $@ valexpr.cc \
-L. -lledger $(LIBS)
ledger.info: ledger.texi
@ -48,9 +65,9 @@ ledger.pdf: ledger.texi
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -c -o $@ $<
clean:
rm -f ledger report libledger.a *.o *.elc *~ .\#*
rm -f ledger valexpr libledger.a *.o *.elc *~ .\#*
rm -f *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr
rm -f .gdb_history
rm -f .gdb_history gmon.out out
distclean fullclean: clean
rm -f *.texi *.info *.html *.pdf *.elc make.deps TAGS
@ -60,7 +77,7 @@ rebuild: clean deps all
deps: make.deps
make.deps: Makefile
cc -M $(INCS) $(CODE) > $@
cc -M $(INCS) $(CODE) main.cc > $@
include make.deps
@ -95,11 +112,6 @@ dist:
mv t Makefile && \
perl -ne 'print if 1 .. /^include make.deps/;' Makefile > t && \
mv t Makefile && \
cd $(HOME)/Public && \
tar cvzf ledger-$(VERSION).tar.gz /tmp/ledger-$(VERSION))
publish: dist
(cd $(HOME)/Public && \
ln -sf ledger-$(VERSION).tar.gz ledger.tar.gz && \
rm -fr /tmp/ledger-$(VERSION) && \
upload)
cd /tmp && \
tar cvzf $(HOME)/Sites/ledger/ledger-$(VERSION).tar.gz \
ledger-$(VERSION))

2
README
View file

@ -201,7 +201,7 @@ amount, if it is the same as the first line:
For this entry, Ledger will figure out that $-23.00 must come from
"Assets:Checking" in order to balance the entry.
** Transactions: Additions and Subtractions
** Stating where money goes
Accountants will talk of `credits' and `debits', but their meaning is
often different from the layman's definitions. To avoid this semantic

View file

@ -15,30 +15,26 @@ account_t::~account_t()
delete (*i).second;
}
account_t * account_t::find_account(const std::string& ident,
account_t * account_t::find_account(const std::string& name,
const bool auto_create)
{
accounts_map::const_iterator c = accounts_cache.find(ident);
if (c != accounts_cache.end())
return (*c).second;
accounts_map::const_iterator i = accounts.find(ident);
accounts_map::const_iterator i = accounts.find(name);
if (i != accounts.end())
return (*i).second;
static char buf[256];
std::string::size_type sep = ident.find(':');
std::string::size_type sep = name.find(':');
const char * first, * rest;
if (sep == std::string::npos) {
first = ident.c_str();
first = name.c_str();
rest = NULL;
} else {
std::strncpy(buf, ident.c_str(), sep);
std::strncpy(buf, name.c_str(), sep);
buf[sep] = '\0';
first = buf;
rest = ident.c_str() + sep + 1;
rest = name.c_str() + sep + 1;
}
account_t * account;
@ -56,8 +52,6 @@ account_t * account_t::find_account(const std::string& ident,
if (rest)
account = account->find_account(rest, auto_create);
accounts_cache.insert(accounts_pair(ident, account));
return account;
}
@ -76,6 +70,9 @@ bool account_t::remove_transaction(transaction_t * xact)
std::string account_t::fullname() const
{
if (! _fullname.empty()) {
return _fullname;
} else {
const account_t * first = this;
std::string fullname = name;
@ -85,7 +82,10 @@ std::string account_t::fullname() const
fullname = first->name + ":" + fullname;
}
_fullname = fullname;
return fullname;
}
}
} // namespace ledger

112
amount.cc
View file

@ -10,6 +10,24 @@
namespace ledger {
static mpz_t full_divisor;
static mpz_t true_value;
static class init_amounts
{
public:
init_amounts() {
mpz_init(full_divisor);
mpz_init(true_value);
mpz_ui_pow_ui(full_divisor, 10, MAX_PRECISION);
mpz_mul_ui(true_value, full_divisor, 1);
}
~init_amounts() {
mpz_clear(full_divisor);
mpz_clear(true_value);
}
} initializer;
static void mpz_round(mpz_t out, mpz_t value, int precision)
{
mpz_t divisor;
@ -52,7 +70,49 @@ static void mpz_round(mpz_t out, mpz_t value, int precision)
mpz_clear(remainder);
}
// destructor
amount_t::amount_t(const bool value)
: quantity(NULL), commodity(NULL)
{
if (value) {
commodity = commodity_t::null_commodity;
quantity = new MP_INT;
mpz_init_set(MPZ(quantity), true_value);
}
}
amount_t::amount_t(const int value)
: quantity(NULL), commodity(NULL)
{
if (value != 0) {
_init();
commodity = commodity_t::null_commodity;
mpz_set_si(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
}
}
amount_t::amount_t(const unsigned int value)
: quantity(NULL), commodity(NULL)
{
if (value != 0) {
_init();
commodity = commodity_t::null_commodity;
mpz_set_ui(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
}
}
amount_t::amount_t(const double value)
: quantity(NULL), commodity(NULL)
{
if (value != 0.0) {
_init();
commodity = commodity_t::null_commodity;
mpz_set_d(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
}
}
void amount_t::_clear()
{
mpz_clear(MPZ(quantity));
@ -92,6 +152,22 @@ amount_t& amount_t::operator=(const amount_t& amt)
return *this;
}
amount_t& amount_t::operator=(const bool value)
{
if (! value) {
if (quantity) {
_clear();
quantity = NULL;
commodity = NULL;
}
} else {
commodity = commodity_t::null_commodity;
quantity = new MP_INT;
mpz_init_set(MPZ(quantity), true_value);
}
return *this;
}
amount_t& amount_t::operator=(const int value)
{
if (value == 0) {
@ -101,10 +177,9 @@ amount_t& amount_t::operator=(const int value)
commodity = NULL;
}
} else {
std::string str;
std::ostringstream strstr(str);
strstr << value;
parse(strstr.str());
commodity = commodity_t::null_commodity;
mpz_set_si(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
}
return *this;
}
@ -118,10 +193,9 @@ amount_t& amount_t::operator=(const unsigned int value)
commodity = NULL;
}
} else {
std::string str;
std::ostringstream strstr(str);
strstr << value;
parse(strstr.str());
commodity = commodity_t::null_commodity;
mpz_set_ui(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
}
return *this;
}
@ -135,10 +209,9 @@ amount_t& amount_t::operator=(const double value)
commodity = NULL;
}
} else {
std::string str;
std::ostringstream strstr(str);
strstr << value;
parse(strstr.str());
commodity = commodity_t::null_commodity;
mpz_set_d(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
}
return *this;
}
@ -279,7 +352,16 @@ amount_t::operator bool() const
{
if (quantity) {
assert(commodity);
return mpz_sgn(MPZ(round().quantity)) != 0;
mpz_t temp;
mpz_t divisor;
mpz_init_set(temp, MPZ(quantity));
mpz_init(divisor);
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - commodity->precision);
mpz_tdiv_q(temp, temp, divisor);
bool zero = mpz_sgn(temp) == 0;
mpz_clear(divisor);
mpz_clear(temp);
return ! zero;
} else {
return false;
}
@ -682,6 +764,8 @@ void (*commodity_t::updater)(commodity_t * commodity,
const std::time_t moment) = NULL;
commodities_map commodity_t::commodities;
commodity_t * commodity_t::null_commodity =
commodity_t::find_commodity("", true);
struct cleanup_commodities
{

244
amount.h Normal file
View file

@ -0,0 +1,244 @@
#ifndef _AMOUNT_H
#define _AMOUNT_H
#include <map>
#include <string>
#include <ctime>
#include <iostream>
#include <sstream>
namespace ledger {
class commodity_t;
class amount_t
{
typedef void * base_type;
void _init();
void _copy(const amount_t& amt);
void _clear();
public:
base_type quantity; // amount, to MAX_PRECISION
commodity_t * commodity;
bool valid() const {
if (quantity)
return commodity != NULL;
else
return commodity == NULL;
}
// constructors
amount_t(commodity_t * _commodity = NULL)
: quantity(NULL), commodity(_commodity) {}
amount_t(const amount_t& amt) : quantity(NULL) {
if (amt.quantity)
_copy(amt);
else
commodity = amt.commodity;
}
amount_t(const std::string& value) {
_init();
std::istringstream str(value);
str >> *this;
}
amount_t(const bool value);
amount_t(const int value);
amount_t(const unsigned int value);
amount_t(const double value);
// destructor
~amount_t() {
if (quantity)
_clear();
}
// assignment operator
amount_t& operator=(const amount_t& amt);
amount_t& operator=(const std::string& value);
amount_t& operator=(const bool value);
amount_t& operator=(const int value);
amount_t& operator=(const unsigned int value);
amount_t& operator=(const double value);
// general methods
amount_t round(int precision = -1) const;
// in-place arithmetic
amount_t& operator*=(const amount_t& amt);
amount_t& operator/=(const amount_t& amt);
amount_t& operator%=(const amount_t& amt);
amount_t& operator+=(const amount_t& amt);
amount_t& operator-=(const amount_t& amt);
// simple arithmetic
amount_t operator*(const amount_t& amt) const {
amount_t temp = *this;
temp *= amt;
return temp;
}
amount_t operator/(const amount_t& amt) const {
amount_t temp = *this;
temp /= amt;
return temp;
}
amount_t operator%(const amount_t& amt) const {
amount_t temp = *this;
temp %= amt;
return temp;
}
amount_t operator+(const amount_t& amt) const {
amount_t temp = *this;
temp += amt;
return temp;
}
amount_t operator-(const amount_t& amt) const {
amount_t temp = *this;
temp -= amt;
return temp;
}
// unary negation
amount_t& negate();
amount_t negated() const {
amount_t temp = *this;
temp.negate();
return temp;
}
amount_t operator-() const {
return negated();
}
// test for non-zero (use ! for zero)
operator bool() const;
// comparisons to zero
bool operator<(const int num) const;
bool operator<=(const int num) const;
bool operator>(const int num) const;
bool operator>=(const int num) const;
// comparisons between amounts
bool operator<(const amount_t& amt) const;
bool operator<=(const amount_t& amt) const;
bool operator>(const amount_t& amt) const;
bool operator>=(const amount_t& amt) const;
bool operator==(const amount_t& amt) const;
bool operator!=(const amount_t& amt) const {
if (commodity != amt.commodity)
return true;
return ! (*this == amt);
}
amount_t value(const std::time_t moment) const;
operator std::string() const;
void parse(std::istream& in);
void parse(const std::string& str) {
std::istringstream stream(str);
parse(stream);
}
void write_quantity(std::ostream& out) const;
void read_quantity(std::istream& in);
friend std::istream& operator>>(std::istream& in, amount_t& amt);
};
void parse_quantity(std::istream& in, std::string& value);
void parse_commodity(std::istream& in, std::string& symbol);
inline amount_t abs(const amount_t& amt) {
return amt < 0 ? amt.negated() : amt;
}
inline std::istream& operator>>(std::istream& in, amount_t& amt) {
amt.parse(in);
return in;
}
std::ostream& operator<<(std::ostream& out, const amount_t& amt);
#define COMMODITY_STYLE_DEFAULTS 0x00
#define COMMODITY_STYLE_SUFFIXED 0x01
#define COMMODITY_STYLE_SEPARATED 0x02
#define COMMODITY_STYLE_EUROPEAN 0x04
#define COMMODITY_STYLE_THOUSANDS 0x08
#define COMMODITY_STYLE_CONSULTED 0x10
#define COMMODITY_STYLE_NOMARKET 0x20
typedef std::map<const std::time_t, amount_t> history_map;
typedef std::pair<const std::time_t, amount_t> history_pair;
typedef std::map<const std::string, commodity_t *> commodities_map;
typedef std::pair<const std::string, commodity_t *> commodities_pair;
class commodity_t
{
public:
std::string symbol;
std::string name;
std::string note;
unsigned int precision;
unsigned int flags;
history_map history;
amount_t conversion;
unsigned long ident;
// If set, this global function pointer is called to determine
// whether prices have been updated in the meanwhile.
static void (*updater)(commodity_t * commodity,
const std::time_t date,
const amount_t& price,
const std::time_t moment);
// This map remembers all commodities that have been
// defined thus far.
static commodities_map commodities;
static commodity_t * null_commodity;
static void add_commodity(commodity_t * commodity,
const std::string symbol = "") {
commodities.insert(commodities_pair((symbol.empty() ?
commodity->symbol : symbol),
commodity));
}
static bool remove_commodity(commodity_t * commodity) {
commodities_map::size_type n = commodities.erase(commodity->symbol);
return n > 0;
}
static commodity_t * find_commodity(const std::string& symbol,
bool auto_create = false);
// Now the per-object constructor and methods
commodity_t(const std::string& _symbol = "",
unsigned int _precision = 2,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: symbol(_symbol), precision(_precision), flags(_flags) {}
void add_price(const std::time_t date, const amount_t& price) {
history.insert(history_pair(date, price));
}
bool remove_price(const std::time_t date) {
history_map::size_type n = history.erase(date);
return n > 0;
}
void set_conversion(const amount_t& price) {
conversion = price;
}
amount_t value(const std::time_t moment = std::time(NULL));
};
} // namespace ledger
#endif // _AMOUNT_H

28
autoxact.cc Normal file
View file

@ -0,0 +1,28 @@
#include "autoxact.h"
namespace ledger {
void automated_transaction_t::extend_entry(entry_t * entry)
{
for (transactions_list::iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++)
if (matches(masks, *((*i)->account))) {
for (transactions_list::iterator t = transactions.begin();
t != transactions.end();
t++) {
amount_t amt;
if ((*t)->amount.commodity->symbol.empty())
amt = (*i)->amount * (*t)->amount;
else
amt = (*t)->amount;
transaction_t * xact
= new transaction_t(entry, (*t)->account, amt, amt,
(*t)->flags | TRANSACTION_AUTO);
entry->add_transaction(xact);
}
}
}
} // namespace ledger

79
autoxact.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef _AUTOXACT_H
#define _AUTOXACT_H
#include "ledger.h"
#include "valexpr.h"
#include <deque>
namespace ledger {
class automated_transaction_t
{
public:
masks_list masks;
transactions_list transactions;
automated_transaction_t(masks_list& _masks,
transactions_list& _transactions) {
masks.insert(masks.begin(), _masks.begin(), _masks.end());
transactions.insert(transactions.begin(),
_transactions.begin(), _transactions.end());
// Take over ownership of the pointers
_transactions.clear();
}
~automated_transaction_t() {
for (transactions_list::iterator i = transactions.begin();
i != transactions.end();
i++)
delete *i;
}
void extend_entry(entry_t * entry);
};
typedef std::deque<automated_transaction_t *> automated_transactions_deque;
class automated_transactions_t
{
public:
automated_transactions_deque automated_transactions;
~automated_transactions_t() {
for (automated_transactions_deque::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
i++)
delete *i;
}
void extend_entry(entry_t * entry) {
for (automated_transactions_deque::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
i++)
(*i)->extend_entry(entry);
}
void add_automated_transaction(automated_transaction_t * auto_xact) {
automated_transactions.push_back(auto_xact);
}
bool remove_automated_transaction(automated_transaction_t * auto_xact) {
for (automated_transactions_deque::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
i++) {
if (*i == auto_xact) {
automated_transactions.erase(i);
return true;
}
}
return false;
}
};
} // namespace ledger
#endif // _AUTOXACT_H

View file

@ -1,5 +1,4 @@
#include "ledger.h"
#include "balance.h"
namespace ledger {
@ -86,7 +85,6 @@ void balance_t::write(std::ostream& out,
}
}
balance_pair_t::balance_pair_t(const transaction_t& xact)
: quantity(xact.amount), cost(xact.cost) {}

View file

@ -1,7 +1,11 @@
#ifndef _BALANCE_H
#define _BALANCE_H
#include "ledger.h"
#include <map>
#include <ctime>
#include <iostream>
#include "amount.h"
namespace ledger {
@ -349,6 +353,7 @@ inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) {
}
#endif
class transaction_t;
class balance_pair_t
{

View file

@ -14,7 +14,7 @@
namespace ledger {
unsigned long magic_number = 0xFFEED765;
unsigned long binary_magic_number = 0xFFEED765;
static unsigned long format_version = 0x00020009;
static char buf[4096];
@ -292,7 +292,7 @@ unsigned int read_binary_ledger(std::istream& in,
unsigned long magic;
in.read((char *)&magic, sizeof(magic));
if (magic != magic_number)
if (magic != binary_magic_number)
return 0;
#ifdef DEBUG
@ -560,7 +560,7 @@ void write_binary_account(std::ostream& out, account_t * account)
void write_binary_ledger(std::ostream& out, ledger_t * ledger,
const std::string& leader)
{
out.write((char *)&magic_number, sizeof(magic_number));
out.write((char *)&binary_magic_number, sizeof(binary_magic_number));
#ifdef DEBUG
{

View file

@ -5,7 +5,7 @@
namespace ledger {
extern unsigned long magic_number;
extern unsigned long binary_magic_number;
extern unsigned int read_binary_ledger(std::istream& in,
const std::string& leader,

125
datetime.cc Normal file
View file

@ -0,0 +1,125 @@
#include "datetime.h"
#include <ctime>
namespace ledger {
static std::time_t now = std::time(NULL);
struct std::tm * now_tm = std::localtime(&now);
static std::time_t base = -1;
static int base_year = -1;
static const int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const char * formats[] = {
"%Y/%m/%d",
"%m/%d",
"%Y.%m.%d",
"%m.%d",
"%Y-%m-%d",
"%m-%d",
"%a",
"%A",
"%b",
"%B",
"%Y",
NULL
};
bool parse_date_mask(const char * date_str, struct std::tm * result)
{
for (const char ** f = formats; *f; f++) {
memset(result, INT_MAX, sizeof(struct std::tm));
if (strptime(date_str, *f, result))
return true;
}
return false;
}
bool parse_date(const char * date_str, std::time_t * result, const int year)
{
struct std::tm when;
if (! parse_date_mask(date_str, &when))
return false;
when.tm_hour = 0;
when.tm_min = 0;
when.tm_sec = 0;
if (when.tm_year == -1)
when.tm_year = ((year == -1) ? now_tm->tm_year : (year - 1900));
if (when.tm_mon == -1)
when.tm_mon = 0;
if (when.tm_mday == -1)
when.tm_mday = 1;
*result = std::mktime(&when);
return true;
}
bool quick_parse_date(char * date_str, std::time_t * result)
{
int year = -1, month = -1, day, num = 0;
for (char * p = date_str; *p; p++) {
if (*p == '/' || *p == '-' || *p == '.') {
if (year == -1)
year = num;
else
month = num;
num = 0;
}
else if (*p < '0' || *p > '9') {
return false;
}
else {
num *= 10;
num += *p - '0';
}
}
day = num;
if (month == -1) {
month = year;
year = -1;
}
if (base == -1 || year != base_year) {
struct std::tm when;
when.tm_hour = 0;
when.tm_min = 0;
when.tm_sec = 0;
base_year = year == -1 ? now_tm->tm_year + 1900 : year;
when.tm_year = year == -1 ? now_tm->tm_year : year - 1900;
when.tm_mon = 0;
when.tm_mday = 1;
base = std::mktime(&when);
}
*result = base;
--month;
while (--month >= 0) {
*result += month_days[month] * 24 * 60 * 60;
if (month == 1 && year % 4 == 0 && year != 2000) // february in leap years
*result += 24 * 60 * 60;
}
if (--day)
*result += day * 24 * 60 * 60;
return true;
}
} // namespace ledger

19
datetime.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef _DATETIME_H
#define _DATETIME_H
#include "ledger.h"
namespace ledger {
extern bool parse_date_mask(const char * date_str, struct std::tm * result);
extern bool parse_date(const char * date_str, std::time_t * result,
const int year = -1);
extern bool quick_parse_date(char * date_str, std::time_t * result);
extern struct std::tm * now_tm;
} // namespace ledger
#endif // _DATETIME_H

21
error.h
View file

@ -1,8 +1,19 @@
#ifndef _ERROR_H
#define _ERROR_H
#include "ledger.h"
#include <exception>
#include <sstream>
#include <string>
#ifdef DEBUG
#include <cassert>
#else
#ifdef assert
#undef assert
#endif
#define assert(x)
#endif
namespace ledger {
@ -49,13 +60,9 @@ class parse_error : public error
: error(reason), line(_line), file(_file) {}
virtual ~parse_error() throw() {}
virtual const char* what() const throw() {
static std::ostringstream msg;
msg << file << ", line " << line << ": " << error::what();
return msg.str().c_str();
}
virtual const char* what() const throw();
};
} // namespace ledger
#endif // _CONSTRAINT_H
#endif // _ERROR_H

134
format.cc
View file

@ -14,13 +14,18 @@ std::string truncated(const std::string& str, unsigned int width)
return buf;
}
std::string maximal_account_name(const item_t * item, const item_t * parent)
std::string partial_account_name(const account_t * account,
const unsigned int start_depth)
{
std::string name = item->account->name;
for (const item_t * i = item->parent;
i && i->account && i != parent;
i = i->parent)
name = i->account->name + ":" + name;
std::string name = account->name;
const account_t * acct = account->parent;
for (int i = account->depth - start_depth - 1;
--i >= 0 && acct->parent; ) {
assert(acct);
name = acct->name + ":" + name;
acct = acct->parent;
}
return name;
}
@ -139,14 +144,10 @@ element_t * format_t::parse_elements(const std::string& fmt)
return result;
}
void format_t::format_elements(std::ostream& out, const item_t * item,
const item_t * displayed_parent) const
void format_t::format_elements(std::ostream& out,
const details_t& details) const
{
std::string result;
for (const element_t * elem = elements;
elem;
elem = elem->next) {
for (const element_t * elem = elements.get(); elem; elem = elem->next) {
if (elem->align_left)
out << std::left;
else
@ -161,16 +162,18 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
break;
case element_t::VALUE_EXPR: {
balance_t value = elem->val_expr->compute(item);
value.write(out, elem->min_width,
elem->max_width > 0 ? elem->max_width : elem->min_width);
balance_t value;
elem->val_expr->compute(value, details);
value.write(out, elem->min_width, (elem->max_width > 0 ?
elem->max_width : elem->min_width));
break;
}
case element_t::DATE_STRING:
if (item->date != -1) {
if (details.entry && details.entry->date != -1) {
char buf[256];
std::strftime(buf, 255, elem->chars.c_str(), std::gmtime(&item->date));
std::strftime(buf, 255, elem->chars.c_str(),
std::gmtime(&details.entry->date));
out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
} else {
out << " ";
@ -178,35 +181,38 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
break;
case element_t::CLEARED:
if (item->state == entry_t::CLEARED)
if (details.entry && details.entry->state == entry_t::CLEARED)
out << "* ";
else
out << "";
break;
case element_t::CODE:
if (! item->code.empty())
out << "(" << item->code << ") ";
if (details.entry && ! details.entry->code.empty())
out << "(" << details.entry->code << ") ";
else
out << "";
break;
case element_t::PAYEE:
if (details.entry)
out << (elem->max_width == 0 ?
item->payee : truncated(item->payee, elem->max_width));
details.entry->payee : truncated(details.entry->payee,
elem->max_width));
break;
case element_t::ACCOUNT_NAME:
case element_t::ACCOUNT_FULLNAME:
if (item->account) {
if (details.account) {
std::string name = (elem->type == element_t::ACCOUNT_FULLNAME ?
item->account->fullname() :
maximal_account_name(item, displayed_parent));
details.account->fullname() :
partial_account_name(details.account,
details.depth));
if (elem->max_width > 0)
name = truncated(name, elem->max_width);
if (item->flags & TRANSACTION_VIRTUAL) {
if (item->flags & TRANSACTION_BALANCE)
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
if (details.xact->flags & TRANSACTION_BALANCE)
name = "[" + name + "]";
else
name = "(" + name + ")";
@ -218,81 +224,67 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
break;
case element_t::OPT_AMOUNT: {
if (! details.entry || ! details.xact)
break;
std::string disp;
bool use_disp = false;
if (std::find(displayed_parent->subitems.begin(),
displayed_parent->subitems.end(), item) !=
displayed_parent->subitems.end()) {
if (displayed_parent->subitems.size() == 2 &&
item == displayed_parent->subitems.back() &&
(displayed_parent->subitems.front()->value.quantity ==
displayed_parent->subitems.front()->value.cost) &&
(displayed_parent->subitems.front()->value ==
- displayed_parent->subitems.back()->value)) {
if (std::find(details.entry->transactions.begin(),
details.entry->transactions.end(), details.xact) !=
details.entry->transactions.end()) {
if (details.entry->transactions.size() == 2 &&
details.xact == details.entry->transactions.back() &&
(details.entry->transactions.front()->amount ==
details.entry->transactions.front()->cost) &&
(details.entry->transactions.front()->amount ==
- details.entry->transactions.back()->amount)) {
use_disp = true;
}
else if (displayed_parent->subitems.size() != 2 &&
item->value.quantity != item->value.cost &&
item->value.quantity.amounts.size() == 1 &&
item->value.cost.amounts.size() == 1 &&
((*item->value.quantity.amounts.begin()).first !=
(*item->value.cost.amounts.begin()).first)) {
amount_t unit_cost
= ((*item->value.cost.amounts.begin()).second /
(*item->value.quantity.amounts.begin()).second);
else if (details.entry->transactions.size() != 2 &&
details.xact->amount != details.xact->cost) {
amount_t unit_cost = details.xact->cost / details.xact->amount;
std::ostringstream stream;
stream << item->value.quantity << " @ " << unit_cost;
stream << details.xact->amount << " @ " << unit_cost;
disp = stream.str();
use_disp = true;
}
}
if (use_disp)
if (! use_disp)
disp = std::string(details.xact->amount);
out << disp;
else
item->value.quantity.write(out, elem->min_width,
elem->max_width > 0 ?
elem->max_width : elem->min_width);
// jww (2004-07-31): this should be handled differently
if (! item->note.empty())
out << " ; " << item->note;
if (! details.xact->note.empty())
out << " ; " << details.xact->note;
break;
}
case element_t::VALUE: {
balance_t value = compute_value(item);
value.write(out, elem->min_width,
elem->max_width > 0 ? elem->max_width : elem->min_width);
balance_t value;
compute_value(value, details);
value.write(out, elem->min_width, (elem->max_width > 0 ?
elem->max_width : elem->min_width));
break;
}
case element_t::TOTAL: {
balance_t value = compute_total(item);
value.write(out, elem->min_width,
elem->max_width > 0 ? elem->max_width : elem->min_width);
balance_t value;
compute_total(value, details);
value.write(out, elem->min_width, (elem->max_width > 0 ?
elem->max_width : elem->min_width));
break;
}
case element_t::SPACER: {
int depth = 0;
for (const item_t * i = item; i->parent; i = i->parent)
depth++;
for (const item_t * i = item->parent;
i && i->account && i != displayed_parent;
i = i->parent)
depth--;
while (--depth >= 0) {
case element_t::SPACER:
for (unsigned int i = 0; i < details.depth; i++) {
if (elem->min_width > 0 || elem->max_width > 0)
out.width(elem->min_width > elem->max_width ?
elem->min_width : elem->max_width);
out << " ";
}
break;
}
default:
assert(0);

View file

@ -2,13 +2,14 @@
#define _FORMAT_H
#include "ledger.h"
#include "balance.h"
#include "expr.h"
#include "valexpr.h"
namespace ledger {
std::string truncated(const std::string& str, unsigned int width);
std::string maximal_account_name(const item_t * item, const item_t * parent);
std::string partial_account_name(const account_t * account,
const unsigned int start_depth);
struct element_t
{
@ -48,28 +49,27 @@ struct element_t
struct format_t
{
element_t * elements;
std::auto_ptr<element_t> elements;
static std::auto_ptr<node_t> value_expr;
static std::auto_ptr<node_t> total_expr;
format_t(const std::string& _format) {
elements = parse_elements(_format);
}
~format_t() {
if (elements) delete elements;
elements.reset(parse_elements(_format));
}
static element_t * parse_elements(const std::string& fmt);
void format_elements(std::ostream& out, const item_t * item,
const item_t * displayed_parent = NULL) const;
void format_elements(std::ostream& out, const details_t& details) const;
static balance_t compute_value(const item_t * item) {
return value_expr.get() ? value_expr->compute(item) : balance_t();
static void compute_value(balance_t& result, const details_t& details) {
if (value_expr.get())
value_expr->compute(result, details);
}
static balance_t compute_total(const item_t * item) {
return total_expr.get() ? total_expr->compute(item) : balance_t();
static void compute_total(balance_t& result, const details_t& details) {
if (total_expr.get())
total_expr->compute(result, details);
}
};

196
item.cc
View file

@ -1,196 +0,0 @@
#include "item.h"
#include "expr.h"
namespace ledger {
static inline void sum_items(const item_t * top,
const bool show_subtotals,
item_t * item)
{
if (top->account == item->account) {
item->value += top->value;
if (show_subtotals)
item->total += top->value;
}
for (items_deque::const_iterator i = top->subitems.begin();
i != top->subitems.end();
i++)
sum_items(*i, show_subtotals, item);
}
item_t * walk_accounts(const item_t * top,
account_t * account,
const node_t * predicate,
const bool show_subtotals,
const bool show_flattened)
{
item_t * item = new item_t;
item->account = account;
if (top) {
sum_items(top, show_subtotals, item);
} else {
std::time_t latest = 0;
for (transactions_list::iterator i
= std::find_if(account->transactions.begin(),
account->transactions.end(),
value_predicate(predicate));
i != account->transactions.end();
i = std::find_if(++i, account->transactions.end(),
value_predicate(predicate))) {
if (std::difftime(latest, (*i)->entry->date) < 0)
latest = (*i)->entry->date;
item->value += *(*i);
if (show_subtotals)
item->total += *(*i);
}
item->date = latest;
}
for (accounts_map::iterator i = account->accounts.begin();
i != account->accounts.end();
i++) {
std::auto_ptr<item_t>
subitem(walk_accounts(top, (*i).second, predicate, show_subtotals,
show_flattened));
subitem->parent = item;
if (std::difftime(item->date, subitem->date) < 0)
item->date = subitem->date;
if (show_flattened) {
item_t * ptr = item;
balance_pair_t total;
for (items_deque::const_iterator i = subitem->subitems.begin();
i != subitem->subitems.end();
i++)
if (show_subtotals ? (*i)->total : (*i)->value) {
if (! account->parent) {
if (! total) {
item_t * temp = new item_t;
temp->date = top ? top->date : item->date;
temp->payee = "Opening balance";
item->subitems.push_back(temp);
ptr = temp;
}
total += show_subtotals ? (*i)->total : (*i)->value;
}
ptr->subitems.push_back(new item_t(*i));
ptr->subitems.back()->date = ptr->date;
ptr->subitems.back()->payee = ptr->payee;
}
if (total) {
item_t * temp = new item_t;
temp->date = ptr->date;
temp->payee = ptr->payee;
temp->account = account->find_account("Equity:Opening Balances");
temp->value = total;
temp->value.negate();
ptr->subitems.push_back(temp);
}
}
if (show_subtotals)
item->total += subitem->total;
if ((! show_flattened || account->parent) &&
show_subtotals ? subitem->total : subitem->value)
item->subitems.push_back(subitem.release());
}
return item;
}
item_t * walk_entries(entries_list::const_iterator begin,
entries_list::const_iterator end,
const node_t * predicate,
const bool show_related,
const bool show_inverted)
{
unsigned int count = 0;
item_t * result = NULL;
value_predicate pred_obj(predicate);
for (entries_list::const_iterator i = std::find_if(begin, end, pred_obj);
i != end;
i = std::find_if(++i, end, pred_obj)) {
transactions_list reckoned;
item_t * item = NULL;
for (transactions_list::const_iterator j
= std::find_if((*i)->transactions.begin(),
(*i)->transactions.end(), pred_obj);
j != (*i)->transactions.end();
j = std::find_if(++j,
transactions_list::const_iterator((*i)->transactions.end()),
pred_obj)) {
assert(*i == (*j)->entry);
if (! item) {
item = new item_t(*i);
item->index = count++;
}
// If show_inverted is true, it implies show_related.
if (! show_inverted &&
std::find(reckoned.begin(),
reckoned.end(), *j) == reckoned.end()) {
item->add_item(new item_t(*j));
reckoned.push_back(*j);
}
if (show_related)
for (transactions_list::iterator k = (*i)->transactions.begin();
k != (*i)->transactions.end();
k++) {
if (*k == *j || ((*k)->flags & TRANSACTION_AUTO) ||
std::find(reckoned.begin(),
reckoned.end(), *k) != reckoned.end())
continue;
item->add_item(new item_t(*k));
if (show_inverted)
item->subitems.back()->value.negate();
reckoned.push_back(*k);
}
}
if (item) {
if (! result)
result = new item_t;
item->parent = result;
result->subitems.push_back(item);
if (std::difftime(result->date, item->date) < 0)
result->date = item->date;
}
}
return result;
}
struct cmp_items {
const node_t * sort_order;
cmp_items(const node_t * _sort_order) : sort_order(_sort_order) {
assert(sort_order);
}
bool operator()(const item_t * left, const item_t * right) const {
assert(left);
assert(right);
return sort_order->compute(left) < sort_order->compute(right);
}
};
void item_t::sort(const node_t * sort_order)
{
std::stable_sort(subitems.begin(), subitems.end(), cmp_items(sort_order));
}
} // namespace ledger

82
item.h
View file

@ -1,82 +0,0 @@
#ifndef _ITEM_H
#define _ITEM_H
#include "ledger.h"
#include "balance.h"
#include <deque>
namespace ledger {
struct node_t;
struct item_t;
typedef std::deque<item_t *> items_deque;
struct item_t
{
struct item_t * parent;
items_deque subitems;
unsigned int index;
std::time_t date;
entry_t::entry_state_t state;
std::string code;
std::string payee;
unsigned int flags;
const account_t * account;
balance_pair_t value;
balance_pair_t total;
std::string note;
item_t() : parent(NULL), index(0), date(-1),
state(entry_t::UNCLEARED), flags(0), account(NULL) {}
item_t(const item_t * item)
: parent(NULL), index(0), date(item->date), state(item->state),
code(item->code), payee(item->payee), flags(item->flags),
account(item->account), value(item->value), total(item->total),
note(item->note) {}
item_t(const entry_t * entry)
: parent(NULL), index(0), date(entry->date), state(entry->state),
code(entry->code), payee(entry->payee) {}
item_t(const transaction_t * xact)
: parent(NULL), index(0), date(xact->entry->date),
state(xact->entry->state), code(xact->entry->code),
payee(xact->entry->payee), flags(xact->flags),
account(xact->account), value(*xact), note(xact->note) {}
~item_t() {
for (items_deque::iterator i = subitems.begin();
i != subitems.end();
i++)
delete *i;
}
void add_item(item_t * item) {
item->parent = this;
value += item->value;
subitems.push_back(item);
}
void sort(const node_t * sort_order);
};
struct node_t;
item_t * walk_accounts(const item_t * top,
account_t * account,
const node_t * predicate = NULL,
const bool show_subtotals = true,
const bool show_flattened = false);
item_t * walk_entries(entries_list::const_iterator begin,
entries_list::const_iterator end,
const node_t * predicate = NULL,
const bool show_related = false,
const bool show_inverted = false);
} // namespace report
#endif // _REPORT_H

View file

@ -1,5 +1,6 @@
#include "ledger.h"
#include "expr.h"
#include "valexpr.h"
#include "datetime.h"
#include "textual.h"
#include "binary.h"
@ -9,6 +10,29 @@ namespace ledger {
const std::string version = "2.0b";
#if 0
struct cmp_items {
const node_t * sort_order;
cmp_items(const node_t * _sort_order) : sort_order(_sort_order) {
assert(sort_order);
}
bool operator()(const item_t * left, const item_t * right) const {
assert(left);
assert(right);
return sort_order->compute(left) < sort_order->compute(right);
}
};
void item_t::sort(const node_t * sort_order)
{
std::stable_sort(subitems.begin(), subitems.end(), cmp_items(sort_order));
}
#endif
ledger_t::~ledger_t()
{
delete master;
@ -200,7 +224,7 @@ int parse_ledger_file(char * p, ledger_t * journal)
stream.read((char *)&magic, sizeof(magic));
stream.seekg(start);
if (magic == magic_number)
if (magic == binary_magic_number)
return read_binary_ledger(stream, "", journal, master);
else
return parse_textual_ledger(stream, journal, master);

307
ledger.h
View file

@ -14,281 +14,22 @@
#include <list>
#include <string>
#include <ctime>
#include <cctype>
#include <iostream>
#include <sstream>
#ifdef DEBUG
#include <cassert>
#else
#ifdef assert
#undef assert
#endif
#define assert(x)
#endif
#include "amount.h"
#include "balance.h"
namespace ledger {
extern const std::string version;
#define TRANSACTION_NORMAL 0x00
#define TRANSACTION_VIRTUAL 0x01
#define TRANSACTION_BALANCE 0x02
#define TRANSACTION_AUTO 0x04
#define TRANSACTION_HANDLED 0x08
#define TRANSACTION_DISPLAYED 0x10
class commodity_t;
class amount_t;
class transaction_t;
class entry_t;
class account_t;
class ledger_t;
class amount_t
{
typedef void * base_type;
void _init();
void _copy(const amount_t& amt);
void _clear();
public:
base_type quantity; // amount, to MAX_PRECISION
commodity_t * commodity;
bool valid() const {
if (quantity)
return commodity != NULL;
else
return commodity == NULL;
}
// constructors
amount_t(commodity_t * _commodity = NULL)
: quantity(NULL), commodity(_commodity) {}
amount_t(const amount_t& amt) : quantity(NULL) {
if (amt.quantity)
_copy(amt);
else
commodity = amt.commodity;
}
amount_t(const std::string& value) {
_init();
std::istringstream str(value);
str >> *this;
}
amount_t(const int value) : quantity(NULL), commodity(NULL) {
if (value != 0) {
std::string str;
std::ostringstream strstr(str);
strstr << value;
parse(strstr.str());
}
}
amount_t(const unsigned int value) : quantity(NULL), commodity(NULL) {
if (value != 0) {
std::string str;
std::ostringstream strstr(str);
strstr << value;
parse(strstr.str());
}
}
amount_t(const double value) : quantity(NULL), commodity(NULL) {
if (value != 0.0) {
std::string str;
std::ostringstream strstr(str);
strstr << value;
parse(strstr.str());
}
}
// destructor
~amount_t() {
if (quantity)
_clear();
}
// assignment operator
amount_t& operator=(const amount_t& amt);
amount_t& operator=(const std::string& value);
amount_t& operator=(const int value);
amount_t& operator=(const unsigned int value);
amount_t& operator=(const double value);
// general methods
amount_t round(int precision = -1) const;
// in-place arithmetic
amount_t& operator*=(const amount_t& amt);
amount_t& operator/=(const amount_t& amt);
amount_t& operator%=(const amount_t& amt);
amount_t& operator+=(const amount_t& amt);
amount_t& operator-=(const amount_t& amt);
// simple arithmetic
amount_t operator*(const amount_t& amt) const {
amount_t temp = *this;
temp *= amt;
return temp;
}
amount_t operator/(const amount_t& amt) const {
amount_t temp = *this;
temp /= amt;
return temp;
}
amount_t operator%(const amount_t& amt) const {
amount_t temp = *this;
temp %= amt;
return temp;
}
amount_t operator+(const amount_t& amt) const {
amount_t temp = *this;
temp += amt;
return temp;
}
amount_t operator-(const amount_t& amt) const {
amount_t temp = *this;
temp -= amt;
return temp;
}
// unary negation
amount_t& negate();
amount_t negated() const {
amount_t temp = *this;
temp.negate();
return temp;
}
amount_t operator-() const {
return negated();
}
// test for non-zero (use ! for zero)
operator bool() const;
// comparisons to zero
bool operator<(const int num) const;
bool operator<=(const int num) const;
bool operator>(const int num) const;
bool operator>=(const int num) const;
// comparisons between amounts
bool operator<(const amount_t& amt) const;
bool operator<=(const amount_t& amt) const;
bool operator>(const amount_t& amt) const;
bool operator>=(const amount_t& amt) const;
bool operator==(const amount_t& amt) const;
bool operator!=(const amount_t& amt) const {
if (commodity != amt.commodity)
return true;
return ! (*this == amt);
}
amount_t value(const std::time_t moment) const;
operator std::string() const;
void parse(std::istream& in);
void parse(const std::string& str) {
std::istringstream stream(str);
parse(stream);
}
void write_quantity(std::ostream& out) const;
void read_quantity(std::istream& in);
friend std::istream& operator>>(std::istream& in, amount_t& amt);
};
void parse_quantity(std::istream& in, std::string& value);
void parse_commodity(std::istream& in, std::string& symbol);
inline amount_t abs(const amount_t& amt) {
return amt < 0 ? amt.negated() : amt;
}
inline std::istream& operator>>(std::istream& in, amount_t& amt) {
amt.parse(in);
return in;
}
std::ostream& operator<<(std::ostream& out, const amount_t& amt);
#define COMMODITY_STYLE_DEFAULTS 0x00
#define COMMODITY_STYLE_SUFFIXED 0x01
#define COMMODITY_STYLE_SEPARATED 0x02
#define COMMODITY_STYLE_EUROPEAN 0x04
#define COMMODITY_STYLE_THOUSANDS 0x08
#define COMMODITY_STYLE_CONSULTED 0x10
#define COMMODITY_STYLE_NOMARKET 0x20
typedef std::map<const std::time_t, amount_t> history_map;
typedef std::pair<const std::time_t, amount_t> history_pair;
typedef std::map<const std::string, commodity_t *> commodities_map;
typedef std::pair<const std::string, commodity_t *> commodities_pair;
class commodity_t
{
public:
std::string symbol;
std::string name;
std::string note;
unsigned int precision;
unsigned int flags;
history_map history;
amount_t conversion;
unsigned long ident;
// If set, this global function pointer is called to determine
// whether prices have been updated in the meanwhile.
static void (*updater)(commodity_t * commodity,
const std::time_t date,
const amount_t& price,
const std::time_t moment);
// This map remembers all commodities that have been
// defined thus far.
static commodities_map commodities;
static void add_commodity(commodity_t * commodity,
const std::string symbol = "") {
commodities.insert(commodities_pair((symbol.empty() ?
commodity->symbol : symbol),
commodity));
}
static bool remove_commodity(commodity_t * commodity) {
commodities_map::size_type n = commodities.erase(commodity->symbol);
return n > 0;
}
static commodity_t * find_commodity(const std::string& symbol,
bool auto_create = false);
// Now the per-object constructor and methods
commodity_t(const std::string& _symbol = "",
unsigned int _precision = 2,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: symbol(_symbol), precision(_precision), flags(_flags) {}
void add_price(const std::time_t date, const amount_t& price) {
history.insert(history_pair(date, price));
}
bool remove_price(const std::time_t date) {
history_map::size_type n = history.erase(date);
return n > 0;
}
void set_conversion(const amount_t& price) {
conversion = price;
}
amount_t value(const std::time_t moment = std::time(NULL));
};
#define TRANSACTION_NORMAL 0x0
#define TRANSACTION_VIRTUAL 0x1
#define TRANSACTION_BALANCE 0x2
#define TRANSACTION_AUTO 0x4
class transaction_t
{
@ -351,23 +92,27 @@ class entry_t
typedef std::map<const std::string, account_t *> accounts_map;
typedef std::pair<const std::string, account_t *> accounts_pair;
inline std::ostream& operator<<(std::ostream& out, const account_t& acct);
class account_t
{
public:
const account_t * parent;
account_t * parent;
std::string name;
std::string note;
accounts_map accounts;
mutable accounts_map accounts_cache;
transactions_list transactions;
balance_pair_t value;
balance_pair_t total;
unsigned long depth;
unsigned long ident;
mutable std::string _fullname;
static unsigned long next_ident;
account_t(const account_t * _parent, const std::string& _name = "",
account_t(account_t * _parent,
const std::string& _name = "",
const std::string& _note = "")
: parent(_parent), name(_name), note(_note) {}
: parent(_parent), name(_name), note(_note),
depth(parent ? parent->depth + 1 : 0) {}
~account_t();
@ -411,7 +156,7 @@ class ledger_t
public:
account_t * master;
entries_list entries;
mutable accounts_map accounts_cache;
std::list<std::string> sources;
ledger_t() {
@ -430,10 +175,18 @@ class ledger_t
}
account_t * find_account(const std::string& name, bool auto_create = true) {
return master->find_account(name, auto_create);
accounts_map::iterator c = accounts_cache.find(name);
if (c != accounts_cache.end())
return (*c).second;
account_t * account = master->find_account(name, auto_create);
accounts_cache.insert(accounts_pair(name, account));
return account;
}
account_t * find_account(const std::string& name) const {
return master->find_account(name, false);
// With auto_create false, the other `find_account' will not
// change the object.
return const_cast<ledger_t *>(this)->find_account(name, false);
}
bool add_entry(entry_t * entry);
@ -444,6 +197,8 @@ class ledger_t
int parse_ledger_file(char * p, ledger_t * journal);
extern const std::string version;
} // namespace ledger
#endif // _LEDGER_H

86
main.cc
View file

@ -1,11 +1,10 @@
#include "ledger.h"
#include "balance.h"
#include "error.h"
#include "textual.h"
#include "binary.h"
#include "item.h"
#include "expr.h"
#include "valexpr.h"
#include "format.h"
#include "walk.h"
#include <fstream>
#include <cstring>
@ -22,6 +21,8 @@ namespace ledger {
static const std::string bal_fmt = "%20T %2_%-n\n";
#if 0
unsigned int show_balances(std::ostream& out,
items_deque& items,
const node_t * predicate,
@ -80,6 +81,7 @@ void balance_report(std::ostream& out,
}
}
#endif
//////////////////////////////////////////////////////////////////////
//
@ -96,6 +98,8 @@ static const std::string print_fmt
static bool show_commodities_revalued = false;
static bool show_commodities_revalued_only = false;
#if 0
static void report_value_change(std::ostream& out,
const std::time_t date,
const balance_pair_t& balance,
@ -218,6 +222,7 @@ void register_report(std::ostream& out,
first_line_format, next_lines_format);
}
#endif
void set_price_conversion(const std::string& setting)
{
@ -362,6 +367,7 @@ int main(int argc, char * argv[])
bool show_expanded = false;
bool show_related = false;
bool show_inverted = false;
bool show_empty = false;
#ifdef DEBUG
bool debug = false;
@ -492,11 +498,9 @@ int main(int argc, char * argv[])
format_string = optarg;
break;
#if 0
case 'E':
show_empty = true;
break;
#endif
case 'n':
show_subtotals = false;
@ -708,7 +712,11 @@ int main(int argc, char * argv[])
else
predicate_string += "(";
first = false;
} else {
}
else if (argv[index][0] == '-') {
predicate_string += "&";
}
else {
predicate_string += "|";
}
@ -736,6 +744,8 @@ int main(int argc, char * argv[])
if (first)
first = false;
else if (argv[index][0] == '-')
predicate_string += "&";
else
predicate_string += "|";
@ -753,7 +763,7 @@ int main(int argc, char * argv[])
predicate_string += ")";
}
// Compile the predicate
// Compile the predicates
if (! predicate_string.empty()) {
#ifdef DEBUG
@ -763,6 +773,9 @@ int main(int argc, char * argv[])
predicate.reset(ledger::parse_expr(predicate_string));
}
if (display_predicate_string.empty() && command == "b" && ! show_empty)
display_predicate_string = "T";
if (! display_predicate_string.empty()) {
#ifdef DEBUG
if (debug)
@ -795,39 +808,6 @@ int main(int argc, char * argv[])
show_inverted = true;
}
std::auto_ptr<ledger::item_t> top;
std::auto_ptr<ledger::item_t> list;
if (command == "e") {
top.reset(new ledger::item_t);
ledger::item_t * item = new ledger::item_t(new_entry.get());
for (ledger::transactions_list::const_iterator i
= new_entry->transactions.begin();
i != new_entry->transactions.end();
i++)
item->add_item(new ledger::item_t(*i));
top->add_item(item);
}
else if ((! show_related || ! predicate.get()) &&
(command == "b" || command == "E")) {
top.reset(ledger::walk_accounts(NULL, journal->master, predicate.get(),
command != "E" && show_subtotals,
command == "E"));
}
else {
top.reset(ledger::walk_entries(journal->entries.begin(),
journal->entries.end(), predicate.get(),
show_related, show_inverted));
if (top.get() && command == "b" || command == "E") {
list.reset(top.release());
top.reset(ledger::walk_accounts(list.get(), journal->master,
predicate.get(),
command != "E" && show_subtotals,
command == "E"));
}
}
if (top.get()) {
const char * f;
if (! format_string.empty())
f = format_string.c_str();
@ -840,9 +820,19 @@ int main(int argc, char * argv[])
if (command == "b") {
std::auto_ptr<ledger::format_t> format(new ledger::format_t(f));
ledger::balance_report(std::cout, top.get(), display_predicate.get(),
sort_order.get(), *format, show_expanded,
show_subtotals);
ledger::walk_accounts(journal->master,
ledger::format_account(std::cout, *format.get()),
predicate.get(), show_related, show_inverted,
show_subtotals, display_predicate.get());
if (! display_predicate.get() ||
ledger::item_predicate(display_predicate.get())(journal->master)) {
std::string end_format = "--------------------\n";
end_format += f;
format.get()->elements.reset(ledger::format_t::parse_elements(end_format));
ledger::format_account(std::cout, *format.get())(journal->master, true);
}
} else {
std::string first_line_format;
std::string next_lines_format;
@ -859,10 +849,12 @@ int main(int argc, char * argv[])
std::auto_ptr<ledger::format_t>
nformat(new ledger::format_t(next_lines_format));
ledger::register_report(std::cout, top.get(), display_predicate.get(),
sort_order.get(), *format, *nformat,
show_expanded);
}
ledger::walk_entries(journal->entries.begin(), journal->entries.end(),
ledger::format_transaction(std::cout,
first_line_format,
next_lines_format),
predicate.get(), show_related, show_inverted,
display_predicate.get());
}
// Save the cache, if need be

View file

@ -1,8 +1,9 @@
#include "textual.h"
#include "datetime.h"
#include "autoxact.h"
#include "valexpr.h"
#include "error.h"
#include "expr.h"
#include <vector>
#include <fstream>
#include <sstream>
#include <cstring>
@ -13,11 +14,6 @@
namespace ledger {
#if 0
static const std::string entry1_fmt = "%10d %p";
static const std::string entryn_fmt = " %-30a %15t";
#endif
#define MAX_LINE 1024
std::string path;
@ -29,31 +25,6 @@ static account_t * last_account;
static std::string last_desc;
#endif
static std::time_t now = std::time(NULL);
static struct std::tm * now_tm = std::localtime(&now);
static std::time_t base = -1;
static int base_year = -1;
static const int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const char * formats[] = {
"%Y/%m/%d",
"%m/%d",
"%Y.%m.%d",
"%m.%d",
"%Y-%m-%d",
"%m-%d",
"%a",
"%A",
"%b",
"%B",
"%Y",
NULL
};
inline char * skip_ws(char * ptr)
{
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
@ -61,6 +32,16 @@ inline char * skip_ws(char * ptr)
return ptr;
}
inline char peek_next_nonws(std::istream& in)
{
char c = in.peek();
while (! in.eof() && std::isspace(c) && c != '\n') {
in.get(c);
c = in.peek();
}
return c;
}
inline char * next_element(char * buf, bool variable = false)
{
for (char * p = buf; *p; p++) {
@ -83,109 +64,6 @@ inline char * next_element(char * buf, bool variable = false)
return NULL;
}
bool parse_date_mask(const char * date_str, struct std::tm * result)
{
for (const char ** f = formats; *f; f++) {
memset(result, INT_MAX, sizeof(struct std::tm));
if (strptime(date_str, *f, result))
return true;
}
return false;
}
bool parse_date(const char * date_str, std::time_t * result, const int year)
{
struct std::tm when;
if (! parse_date_mask(date_str, &when))
return false;
when.tm_hour = 0;
when.tm_min = 0;
when.tm_sec = 0;
if (when.tm_year == -1)
when.tm_year = ((year == -1) ? now_tm->tm_year : (year - 1900));
if (when.tm_mon == -1)
when.tm_mon = 0;
if (when.tm_mday == -1)
when.tm_mday = 1;
*result = std::mktime(&when);
return true;
}
static bool quick_parse_date(char * date_str, std::time_t * result)
{
int year = -1, month = -1, day, num = 0;
for (char * p = date_str; *p; p++) {
if (*p == '/' || *p == '-' || *p == '.') {
if (year == -1)
year = num;
else
month = num;
num = 0;
}
else if (*p < '0' || *p > '9') {
return false;
}
else {
num *= 10;
num += *p - '0';
}
}
day = num;
if (month == -1) {
month = year;
year = -1;
}
if (base == -1 || year != base_year) {
struct std::tm when;
when.tm_hour = 0;
when.tm_min = 0;
when.tm_sec = 0;
base_year = year == -1 ? now_tm->tm_year + 1900 : year;
when.tm_year = year == -1 ? now_tm->tm_year : year - 1900;
when.tm_mon = 0;
when.tm_mday = 1;
base = std::mktime(&when);
}
*result = base;
--month;
while (--month >= 0) {
*result += month_days[month] * 24 * 60 * 60;
if (month == 1 && year % 4 == 0 && year != 2000) // february in leap years
*result += 24 * 60 * 60;
}
if (--day)
*result += day * 24 * 60 * 60;
return true;
}
inline char peek_next_nonws(std::istream& in)
{
char c = in.peek();
while (! in.eof() && std::isspace(c) && c != '\n') {
in.get(c);
c = in.peek();
}
return c;
}
transaction_t * parse_transaction_text(char * line, account_t * account,
entry_t * entry)
{
@ -233,9 +111,9 @@ transaction_t * parse_transaction_text(char * line, account_t * account,
xact->account = account->find_account(p);
if (! xact->amount.commodity)
xact->amount.commodity = commodity_t::find_commodity("", true);
xact->amount.commodity = commodity_t::null_commodity;
if (! xact->cost.commodity)
xact->cost.commodity = commodity_t::find_commodity("", true);
xact->cost.commodity = commodity_t::null_commodity;
return xact;
}
@ -250,95 +128,6 @@ transaction_t * parse_transaction(std::istream& in, account_t * account,
return parse_transaction_text(line, account, entry);
}
class automated_transaction_t
{
public:
masks_list masks;
transactions_list transactions;
automated_transaction_t(masks_list& _masks,
transactions_list& _transactions) {
masks.insert(masks.begin(), _masks.begin(), _masks.end());
transactions.insert(transactions.begin(),
_transactions.begin(), _transactions.end());
// Take over ownership of the pointers
_transactions.clear();
}
~automated_transaction_t() {
for (transactions_list::iterator i = transactions.begin();
i != transactions.end();
i++)
delete *i;
}
void extend_entry(entry_t * entry);
};
typedef std::vector<automated_transaction_t *>
automated_transactions_vector;
void automated_transaction_t::extend_entry(entry_t * entry)
{
for (transactions_list::iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++)
if (matches(masks, *((*i)->account))) {
for (transactions_list::iterator t = transactions.begin();
t != transactions.end();
t++) {
amount_t amt;
if ((*t)->amount.commodity->symbol.empty())
amt = (*i)->amount * (*t)->amount;
else
amt = (*t)->amount;
transaction_t * xact
= new transaction_t(entry, (*t)->account, amt, amt,
(*t)->flags | TRANSACTION_AUTO);
entry->add_transaction(xact);
}
}
}
class automated_transactions_t
{
public:
automated_transactions_vector automated_transactions;
~automated_transactions_t() {
for (automated_transactions_vector::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
i++)
delete *i;
}
void extend_entry(entry_t * entry) {
for (automated_transactions_vector::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
i++)
(*i)->extend_entry(entry);
}
void add_automated_transaction(automated_transaction_t * auto_xact) {
automated_transactions.push_back(auto_xact);
}
bool remove_automated_transaction(automated_transaction_t * auto_xact) {
for (automated_transactions_vector::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
i++) {
if (*i == auto_xact) {
automated_transactions.erase(i);
return true;
}
}
return false;
}
};
void parse_automated_transactions(std::istream& in, account_t * account,
automated_transactions_t& auto_xacts)
{
@ -498,11 +287,6 @@ entry_t * parse_entry(std::istream& in, account_t * master)
return curr;
}
//////////////////////////////////////////////////////////////////////
//
// Textual ledger parser
//
unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
account_t * master)
{
@ -676,7 +460,8 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
amount_t price;
parse_commodity(in, symbol);
in >> line; // the price
in.getline(line, MAX_LINE);
linenum++;
price.parse(line);
commodity_t * commodity = commodity_t::find_commodity(symbol, true);
@ -733,7 +518,8 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
unsigned int curr_linenum = linenum;
std::string curr_path = path;
count += parse_textual_ledger(stream, journal, account_stack.front());
count += parse_textual_ledger(stream, journal,
account_stack.front());
linenum = curr_linenum;
path = curr_path;

View file

@ -8,11 +8,6 @@ namespace ledger {
extern unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
account_t * master = NULL);
extern bool parse_date_mask(const char * date_str, struct std::tm * result);
extern bool parse_date(const char * date_str, std::time_t * result,
const int year = -1);
} // namespace ledger
#endif // _TEXTUAL_H

View file

@ -1,5 +1,6 @@
#include "expr.h"
#include "valexpr.h"
#include "error.h"
#include "datetime.h"
#include "textual.h"
#include <pcre.h>
@ -90,138 +91,199 @@ bool matches(const masks_list& regexps, const std::string& str,
#endif
balance_t node_t::compute(const item_t * item) const
void node_t::compute(balance_t& result, const details_t& details) const
{
balance_t temp;
switch (type) {
case CONSTANT_A:
temp = constant_a;
result = constant_a;
break;
case CONSTANT_T:
temp = amount_t((unsigned int) constant_t);
result = (unsigned int) constant_t;
break;
case AMOUNT:
temp = item->value.quantity;
if (details.xact)
result = details.xact->amount;
else if (details.account)
result = details.account->value.quantity;
break;
case COST:
temp = item->value.cost;
if (details.xact)
result = details.xact->cost;
else if (details.account)
result = details.account->value.cost;
break;
case BALANCE:
temp = item->total.quantity - item->value.quantity;
if (details.balance) {
result = details.balance->quantity;
if (details.xact)
result -= details.xact->amount;
else if (details.account)
result -= details.account->value.quantity;
}
break;
case COST_BALANCE:
temp = item->total.cost - item->value.cost;
if (details.balance) {
result = details.balance->cost;
if (details.xact)
result -= details.xact->cost;
else if (details.account)
result -= details.account->value.cost;
}
break;
case TOTAL:
temp = item->total.quantity;
if (details.balance)
result = details.balance->quantity;
else if (details.account)
result = details.account->total.quantity;
break;
case COST_TOTAL:
temp = item->total.cost;
if (details.balance)
result = details.balance->cost;
else if (details.account)
result = details.account->total.cost;
break;
case DATE:
temp = amount_t((unsigned int) item->date);
if (details.entry)
result = (unsigned int) details.entry->date;
break;
case CLEARED:
temp = amount_t(item->state == entry_t::CLEARED ? 1 : 0);
if (details.entry)
result = details.entry->state == entry_t::CLEARED;
break;
case REAL:
temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1);
if (details.xact)
result = bool(details.xact->flags & TRANSACTION_VIRTUAL);
break;
case INDEX:
temp = amount_t(item->index + 1);
if (details.index)
result = *details.index + 1;
break;
case F_ARITH_MEAN:
if (details.index) {
assert(left);
temp = left->compute(item);
temp /= amount_t(item->index + 1);
left->compute(result, details);
result /= amount_t(*details.index + 1);
}
break;
case F_NEG:
assert(left);
temp = left->compute(item).negated();
left->compute(result, details);
result.negate();
break;
case F_ABS:
assert(left);
temp = abs(left->compute(item));
left->compute(result, details);
result = abs(result);
break;
case F_PAYEE_MASK:
assert(mask);
temp = (mask->match(item->payee) || mask->match(item->note)) ? 1 : 0;
if (details.entry)
result = mask->match(details.entry->payee);
break;
case F_ACCOUNT_MASK:
assert(mask);
temp = (item->account &&
mask->match(item->account->fullname())) ? 1 : 0;
if (details.account)
result = mask->match(details.account->fullname());
break;
case F_VALUE: {
assert(left);
temp = left->compute(item);
left->compute(result, details);
std::time_t moment = -1;
if (right) {
if (right && details.entry) {
switch (right->type) {
case DATE: moment = item->date; break;
case DATE: moment = details.entry->date; break;
default:
throw compute_error("Invalid date passed to P(v,d)");
}
}
temp = temp.value(moment);
result = result.value(moment);
break;
}
case O_NOT:
temp = left->compute(item) ? 0 : 1;
left->compute(result, details);
result = result ? false : true;
break;
case O_QUES:
temp = left->compute(item);
if (temp)
temp = right->left->compute(item);
assert(left);
assert(right);
assert(right->type == O_COL);
left->compute(result, details);
if (result)
right->left->compute(result, details);
else
temp = right->right->compute(item);
right->right->compute(result, details);
break;
case O_AND:
assert(left);
assert(right);
left->compute(result, details);
if (result)
right->compute(result, details);
break;
case O_OR:
assert(left);
assert(right);
left->compute(result, details);
if (! result)
right->compute(result, details);
break;
case O_EQ:
case O_LT:
case O_LTE:
case O_GT:
case O_GTE:
case O_GTE: {
assert(left);
assert(right);
left->compute(result, details);
balance_t temp = result;
right->compute(result, details);
switch (type) {
case O_EQ: result = temp == result; break;
case O_LT: result = temp < result; break;
case O_LTE: result = temp <= result; break;
case O_GT: result = temp > result; break;
case O_GTE: result = temp >= result; break;
default: assert(0); break;
}
break;
}
case O_ADD:
case O_SUB:
case O_MUL:
case O_DIV: {
assert(left);
assert(right);
balance_t left_bal = left->compute(item);
balance_t right_bal = right->compute(item);
right->compute(result, details);
balance_t temp = result;
left->compute(result, details);
switch (type) {
case O_AND: temp = (left_bal && right_bal) ? 1 : 0; break;
case O_OR: temp = (left_bal || right_bal) ? 1 : 0; break;
case O_EQ: temp = (left_bal == right_bal) ? 1 : 0; break;
case O_LT: temp = (left_bal < right_bal) ? 1 : 0; break;
case O_LTE: temp = (left_bal <= right_bal) ? 1 : 0; break;
case O_GT: temp = (left_bal > right_bal) ? 1 : 0; break;
case O_GTE: temp = (left_bal >= right_bal) ? 1 : 0; break;
case O_ADD: temp = left_bal + right_bal; break;
case O_SUB: temp = left_bal - right_bal; break;
case O_MUL: temp = left_bal * right_bal; break;
case O_DIV: temp = left_bal / right_bal; break;
case O_ADD: result += temp; break;
case O_SUB: result -= temp; break;
case O_MUL: result *= temp; break;
case O_DIV: result /= temp; break;
default: assert(0); break;
}
break;
@ -232,8 +294,6 @@ balance_t node_t::compute(const item_t * item) const
assert(0);
break;
}
return temp;
}
node_t * parse_term(std::istream& in);
@ -608,8 +668,13 @@ namespace ledger {
static void dump_tree(std::ostream& out, node_t * node)
{
switch (node->type) {
case node_t::CONSTANT_A: out << "CONST[" << node->constant_a << "]"; break;
case node_t::CONSTANT_T: out << "DATE/TIME[" << node->constant_t << "]"; break;
case node_t::CONSTANT_A:
out << "CONST[" << node->constant_a << "]";
break;
case node_t::CONSTANT_T:
out << "DATE/TIME[" << node->constant_t << "]";
break;
case node_t::AMOUNT: out << "AMOUNT"; break;
case node_t::COST: out << "COST"; break;
case node_t::DATE: out << "DATE"; break;
@ -674,11 +739,36 @@ static void dump_tree(std::ostream& out, node_t * node)
case node_t::O_AND:
case node_t::O_OR:
out << "(";
dump_tree(out, node->left);
switch (node->type) {
case node_t::O_AND: out << " & "; break;
case node_t::O_OR: out << " | "; break;
default: assert(0); break;
}
dump_tree(out, node->right);
out << ")";
break;
case node_t::O_EQ:
case node_t::O_LT:
case node_t::O_LTE:
case node_t::O_GT:
case node_t::O_GTE:
out << "(";
dump_tree(out, node->left);
switch (node->type) {
case node_t::O_EQ: out << "="; break;
case node_t::O_LT: out << "<"; break;
case node_t::O_LTE: out << "<="; break;
case node_t::O_GT: out << ">"; break;
case node_t::O_GTE: out << ">="; break;
default: assert(0); break;
}
dump_tree(out, node->right);
out << ")";
break;
case node_t::O_ADD:
case node_t::O_SUB:
case node_t::O_MUL:
@ -686,13 +776,6 @@ static void dump_tree(std::ostream& out, node_t * node)
out << "(";
dump_tree(out, node->left);
switch (node->type) {
case node_t::O_AND: out << " & "; break;
case node_t::O_OR: out << " | "; break;
case node_t::O_EQ: out << "="; break;
case node_t::O_LT: out << "<"; break;
case node_t::O_LTE: out << "<="; break;
case node_t::O_GT: out << ">"; break;
case node_t::O_GTE: out << ">="; break;
case node_t::O_ADD: out << "+"; break;
case node_t::O_SUB: out << "-"; break;
case node_t::O_MUL: out << "*"; break;
@ -714,7 +797,7 @@ static void dump_tree(std::ostream& out, node_t * node)
int main(int argc, char *argv[])
{
ledger::dump_tree(std::cout, ledger::parse_expr(argv[1], NULL));
ledger::dump_tree(std::cout, ledger::parse_expr(argv[1]));
std::cout << std::endl;
}

View file

@ -2,8 +2,6 @@
#define _EXPR_H
#include "ledger.h"
#include "balance.h"
#include "item.h"
namespace ledger {
@ -30,6 +28,33 @@ bool matches(const masks_list& regexps, const std::string& str,
#endif
struct details_t
{
const entry_t * entry;
const transaction_t * xact;
const account_t * account;
const balance_pair_t * balance;
const unsigned int * index;
const unsigned int depth;
details_t(const entry_t * _entry,
const balance_pair_t * _balance = NULL,
const unsigned int * _index = NULL)
: entry(_entry), xact(NULL), account(NULL),
balance(_balance), index(_index), depth(0) {}
details_t(const transaction_t * _xact,
const balance_pair_t * _balance = NULL,
const unsigned int * _index = NULL)
: entry(_xact->entry), xact(_xact), account(_xact->account),
balance(_balance), index(_index), depth(0) {}
details_t(const account_t * _account,
const unsigned int _depth = 0)
: entry(NULL), xact(NULL), account(_account),
balance(NULL), index(NULL), depth(_depth) {}
};
struct node_t
{
enum kind_t {
@ -95,7 +120,7 @@ struct node_t
if (right) delete right;
}
balance_t compute(const item_t * item) const;
void compute(balance_t& result, const details_t& details) const;
};
node_t * parse_expr(std::istream& in);
@ -120,64 +145,6 @@ inline node_t * find_node(node_t * node, node_t::kind_t type) {
return result;
}
void dump_tree(std::ostream& out, node_t * node);
class value_predicate
{
public:
const node_t * predicate;
explicit value_predicate(const node_t * _predicate)
: predicate(_predicate) {}
bool operator ()(const transaction_t * xact) const {
if (! predicate) {
return true;
} else {
item_t temp;
temp.date = xact->entry->date;
temp.state = xact->entry->state;
temp.code = xact->entry->code;
temp.payee = xact->entry->payee;
temp.flags = xact->flags;
temp.account = xact->account;
return predicate->compute(&temp);
}
}
bool operator ()(const entry_t * entry) const {
if (! predicate) {
return true;
} else {
item_t temp;
temp.date = entry->date;
temp.payee = entry->payee;
temp.state = entry->state;
temp.code = entry->code;
// Although there may be conflicting account masks for the whole
// set of transactions -- for example, /rent/&!/expenses/, which
// might match one by not another transactions -- we let the
// entry through if at least one of the transactions meets the
// criterion
for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++) {
temp.flags = (*i)->flags;
temp.account = (*i)->account;
if (predicate->compute(&temp))
return true;
}
return false;
}
}
bool operator ()(const item_t * item) const {
return ! predicate || predicate->compute(item);
}
};
} // namespace report
#endif // _REPORT_H

234
walk.h Normal file
View file

@ -0,0 +1,234 @@
#ifndef _WALK_H
#define _WALK_H
#include "ledger.h"
#include "balance.h"
#include "format.h"
#include "valexpr.h"
#include <iostream>
namespace ledger {
class item_predicate
{
const node_t * predicate;
balance_pair_t * balance;
unsigned int * index;
public:
item_predicate(const node_t * _predicate,
balance_pair_t * _balance = NULL,
unsigned int * _index = NULL)
: predicate(_predicate), balance(_balance), index(_index) {}
bool operator()(const entry_t * entry) const {
if (predicate) {
balance_t result;
predicate->compute(result, details_t(entry, balance, index));
return result;
} else {
return true;
}
}
bool operator()(const transaction_t * xact) const {
if (predicate) {
balance_t result;
predicate->compute(result, details_t(xact, balance, index));
return result;
} else {
return true;
}
}
bool operator()(const account_t * account) const {
if (predicate) {
balance_t result;
predicate->compute(result, details_t(account));
return result;
} else {
return true;
}
}
};
inline void add_to_balance_pair(balance_pair_t& balance,
transaction_t * xact,
const bool inverted = false)
{
if (inverted) {
balance.quantity += - xact->amount;
balance.cost += - xact->cost;
} else {
balance += *xact;
}
}
class format_transaction
{
std::ostream& output_stream;
const format_t& first_line_format;
const format_t& next_lines_format;
mutable entry_t * last_entry;
public:
format_transaction(std::ostream& _output_stream,
const format_t& _first_line_format,
const format_t& _next_lines_format)
: output_stream(_output_stream),
first_line_format(_first_line_format),
next_lines_format(_next_lines_format),
last_entry(NULL) {}
void operator()(transaction_t * xact,
balance_pair_t * balance,
unsigned int * index,
const bool inverted) const;
};
class ignore_transaction
{
public:
void operator()(transaction_t * xact,
balance_pair_t * balance,
unsigned int * index,
const bool inverted) const {}
};
template <typename Function>
void handle_transaction(transaction_t * xact,
Function functor,
item_predicate& pred_functor,
const bool related,
const bool inverted,
balance_pair_t * balance = NULL,
unsigned int * index = NULL)
{
// If inverted is true, it implies related.
if (! inverted && ! (xact->flags & TRANSACTION_HANDLED)) {
xact->flags |= TRANSACTION_HANDLED;
if (pred_functor(xact)) {
xact->flags |= TRANSACTION_DISPLAYED;
functor(xact, balance, index, inverted);
}
}
if (related)
for (transactions_list::iterator i = xact->entry->transactions.begin();
i != xact->entry->transactions.end();
i++) {
if (*i == xact || ((*i)->flags & (TRANSACTION_AUTO |
TRANSACTION_HANDLED)))
continue;
(*i)->flags |= TRANSACTION_HANDLED;
if (pred_functor(xact)) {
xact->flags |= TRANSACTION_DISPLAYED;
functor(*i, balance, index, inverted);
}
}
}
template <typename Function>
void walk_entries(entries_list::iterator begin,
entries_list::iterator end,
Function functor,
const node_t * predicate,
const bool related,
const bool inverted,
const node_t * display_predicate = NULL)
{
balance_pair_t balance;
unsigned int index;
item_predicate pred_functor(predicate, &balance, &index);
item_predicate disp_pred_functor(display_predicate, &balance, &index);
for (entries_list::iterator i = begin; i != end; i++)
for (transactions_list::iterator j = (*i)->transactions.begin();
j != (*i)->transactions.end();
j++)
if (pred_functor(*j))
handle_transaction(*j, functor, disp_pred_functor,
related, inverted, &balance, &index);
}
class format_account
{
std::ostream& output_stream;
const format_t& format;
mutable const account_t * last_account;
public:
format_account(std::ostream& _output_stream, const format_t& _format)
: output_stream(_output_stream), format(_format) {}
void operator()(const account_t * account, bool report_top = false);
};
void calc__accounts(account_t * account,
const node_t * predicate,
const bool related,
const bool inverted,
const bool calc_subtotals);
template <typename Function>
void walk__accounts(const account_t * account,
Function functor,
const node_t * display_predicate)
{
if (! display_predicate || item_predicate(display_predicate)(account))
functor(account);
for (accounts_map::const_iterator i = account->accounts.begin();
i != account->accounts.end();
i++)
walk__accounts((*i).second, functor, display_predicate);
}
template <typename Function>
void walk_accounts(account_t * account,
Function functor,
const node_t * predicate,
const bool related,
const bool inverted,
const bool calc_subtotals,
const node_t * display_predicate = NULL)
{
calc__accounts(account, predicate, related, inverted, calc_subtotals);
walk__accounts<Function>(account, functor, display_predicate);
}
#if 0
void sum_entries(entries_list& entries,
account_t * account,
const bool show_subtotals)
{
for (entries_list::const_iterator i = entries.begin();
i != entries.end();
i++)
for (transactions_list::const_iterator j = (*i)->transactions.begin();
j != (*i)->transactions.end();
j++)
if ((*j)->account == account) {
account->value += *(*j);
if (show_subtotals)
for (account_t * a = account;
a;
a = a->parent)
a->total += *(*j);
}
for (accounts_map::iterator i = account->accounts.begin();
i != account->accounts.end();
i++)
sum_items(entries, *i, show_subtotals);
}
#endif
} // namespace ledger
#endif // _WALK_H