summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile46
-rw-r--r--README2
-rw-r--r--account.cc40
-rw-r--r--amount.cc112
-rw-r--r--amount.h244
-rw-r--r--autoxact.cc28
-rw-r--r--autoxact.h79
-rw-r--r--balance.cc2
-rw-r--r--balance.h7
-rw-r--r--binary.cc6
-rw-r--r--binary.h2
-rw-r--r--datetime.cc125
-rw-r--r--datetime.h19
-rw-r--r--error.h21
-rw-r--r--format.cc138
-rw-r--r--format.h32
-rw-r--r--item.cc196
-rw-r--r--item.h82
-rw-r--r--ledger.cc28
-rw-r--r--ledger.h337
-rw-r--r--main.cc136
-rw-r--r--textual.cc252
-rw-r--r--textual.h5
-rw-r--r--valexpr.cc (renamed from expr.cc)199
-rw-r--r--valexpr.h (renamed from expr.h)89
-rw-r--r--walk.h234
26 files changed, 1306 insertions, 1155 deletions
diff --git a/Makefile b/Makefile
index 4c57bab2..74856448 100644
--- a/Makefile
+++ b/Makefile
@@ -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
-LIBS = -L/sw/lib -lgmpxx -lgmp -lpcre
+
+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))
diff --git a/README b/README
index fc992bad..18e177bb 100644
--- a/README
+++ b/README
@@ -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
diff --git a/account.cc b/account.cc
index df57622b..697aa609 100644
--- a/account.cc
+++ b/account.cc
@@ -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,16 +70,22 @@ bool account_t::remove_transaction(transaction_t * xact)
std::string account_t::fullname() const
{
- const account_t * first = this;
- std::string fullname = name;
+ if (! _fullname.empty()) {
+ return _fullname;
+ } else {
+ const account_t * first = this;
+ std::string fullname = name;
- while (first->parent) {
- first = first->parent;
- if (! first->name.empty())
- fullname = first->name + ":" + fullname;
- }
+ while (first->parent) {
+ first = first->parent;
+ if (! first->name.empty())
+ fullname = first->name + ":" + fullname;
+ }
+
+ _fullname = fullname;
- return fullname;
+ return fullname;
+ }
}
} // namespace ledger
diff --git a/amount.cc b/amount.cc
index d1dd6150..c86177b1 100644
--- a/amount.cc
+++ b/amount.cc
@@ -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
{
diff --git a/amount.h b/amount.h
new file mode 100644
index 00000000..bfcea2c2
--- /dev/null
+++ b/amount.h
@@ -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
diff --git a/autoxact.cc b/autoxact.cc
new file mode 100644
index 00000000..c49d1d38
--- /dev/null
+++ b/autoxact.cc
@@ -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
diff --git a/autoxact.h b/autoxact.h
new file mode 100644
index 00000000..717ebe96
--- /dev/null
+++ b/autoxact.h
@@ -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
diff --git a/balance.cc b/balance.cc
index 5d806080..eeef3d49 100644
--- a/balance.cc
+++ b/balance.cc
@@ -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) {}
diff --git a/balance.h b/balance.h
index edc7db2f..a41bcf6f 100644
--- a/balance.h
+++ b/balance.h
@@ -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
{
diff --git a/binary.cc b/binary.cc
index 9f2bed96..68fe9608 100644
--- a/binary.cc
+++ b/binary.cc
@@ -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
{
diff --git a/binary.h b/binary.h
index 46c7f55b..e56d2f17 100644
--- a/binary.h
+++ b/binary.h
@@ -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,
diff --git a/datetime.cc b/datetime.cc
new file mode 100644
index 00000000..19f54f92
--- /dev/null
+++ b/datetime.cc
@@ -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
diff --git a/datetime.h b/datetime.h
new file mode 100644
index 00000000..6c9d001a
--- /dev/null
+++ b/datetime.h
@@ -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
diff --git a/error.h b/error.h
index 51d152cd..4f506d70 100644
--- a/error.h
+++ b/error.h
@@ -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
diff --git a/format.cc b/format.cc
index 33adc15c..189d84ff 100644
--- a/format.cc
+++ b/format.cc
@@ -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:
- out << (elem->max_width == 0 ?
- item->payee : truncated(item->payee, elem->max_width));
+ if (details.entry)
+ out << (elem->max_width == 0 ?
+ 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)
- out << disp;
- else
- item->value.quantity.write(out, elem->min_width,
- elem->max_width > 0 ?
- elem->max_width : elem->min_width);
+ if (! use_disp)
+ disp = std::string(details.xact->amount);
+ out << disp;
// 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);
diff --git a/format.h b/format.h
index f2bdad10..661e7884 100644
--- a/format.h
+++ b/format.h
@@ -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;
+ 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);
}
};
diff --git a/item.cc b/item.cc
deleted file mode 100644
index 62ea9b8c..00000000
--- a/item.cc
+++ /dev/null
@@ -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
diff --git a/item.h b/item.h
deleted file mode 100644
index 15c710f3..00000000
--- a/item.h
+++ /dev/null
@@ -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
diff --git a/ledger.cc b/ledger.cc
index 25ac845f..fd400654 100644
--- a/ledger.cc
+++ b/ledger.cc
@@ -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);
diff --git a/ledger.h b/ledger.h
index 5d9d0283..a1e2e7ae 100644
--- a/ledger.h
+++ b/ledger.h
@@ -14,291 +14,32 @@
#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
{
public:
- entry_t * entry;
- account_t * account;
- amount_t amount;
- amount_t cost;
- unsigned int flags;
- std::string note;
+ entry_t * entry;
+ account_t * account;
+ amount_t amount;
+ amount_t cost;
+ unsigned int flags;
+ std::string note;
transaction_t(entry_t * _entry, account_t * _account)
: entry(_entry), account(_account), flags(TRANSACTION_NORMAL) {}
@@ -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;
- std::string name;
- std::string note;
- accounts_map accounts;
- mutable accounts_map accounts_cache;
- transactions_list transactions;
- unsigned long ident;
- static unsigned long next_ident;
-
- account_t(const account_t * _parent, const std::string& _name = "",
+ account_t * parent;
+ std::string name;
+ std::string note;
+ accounts_map accounts;
+ 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(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();
@@ -409,9 +154,9 @@ typedef std::list<entry_t *> entries_list;
class ledger_t
{
public:
- account_t * master;
- entries_list entries;
-
+ 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
diff --git a/main.cc b/main.cc
index c9cfdd66..9234e629 100644
--- a/main.cc
+++ b/main.cc
@@ -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>
@@ -20,7 +19,9 @@ namespace ledger {
// The command-line balance report
//
-static const std::string bal_fmt = "%20T%2_%-n\n";
+static const std::string bal_fmt = "%20T %2_%-n\n";
+
+#if 0
unsigned int show_balances(std::ostream& out,
items_deque& items,
@@ -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,74 +808,53 @@ 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"));
+ const char * f;
+ if (! format_string.empty())
+ f = format_string.c_str();
+ else if (command == "b")
+ f = ledger::bal_fmt.c_str();
+ else if (command == "r")
+ f = ledger::reg_fmt.c_str();
+ else
+ f = ledger::print_fmt.c_str();
+
+ if (command == "b") {
+ std::auto_ptr<ledger::format_t> format(new ledger::format_t(f));
+
+ 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;
- if (top.get()) {
- const char * f;
- if (! format_string.empty())
- f = format_string.c_str();
- else if (command == "b")
- f = ledger::bal_fmt.c_str();
- else if (command == "r")
- f = ledger::reg_fmt.c_str();
- else
- f = ledger::print_fmt.c_str();
-
- 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);
+ if (const char * p = std::strstr(f, "%/")) {
+ first_line_format = std::string(f, 0, p - f);
+ next_lines_format = std::string(p + 2);
} else {
- std::string first_line_format;
- std::string next_lines_format;
-
- if (const char * p = std::strstr(f, "%/")) {
- first_line_format = std::string(f, 0, p - f);
- next_lines_format = std::string(p + 2);
- } else {
- first_line_format = next_lines_format = f;
- }
-
- std::auto_ptr<ledger::format_t>
- format(new ledger::format_t(first_line_format));
- 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);
+ first_line_format = next_lines_format = f;
}
+
+ std::auto_ptr<ledger::format_t>
+ format(new ledger::format_t(first_line_format));
+ std::auto_ptr<ledger::format_t>
+ nformat(new ledger::format_t(next_lines_format));
+
+ 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
diff --git a/textual.cc b/textual.cc
index e262d864..2e7ef86f 100644
--- a/textual.cc
+++ b/textual.cc
@@ -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;
diff --git a/textual.h b/textual.h
index 55d7da97..8ae24b83 100644
--- a/textual.h
+++ b/textual.h
@@ -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
diff --git a/expr.cc b/valexpr.cc
index c49dc33b..889c57c8 100644
--- a/expr.cc
+++ b/valexpr.cc
@@ -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:
- assert(left);
- temp = left->compute(item);
- temp /= amount_t(item->index + 1);
+ if (details.index) {
+ assert(left);
+ 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,25 +739,43 @@ 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:
- case node_t::O_ADD:
- case node_t::O_SUB:
- case node_t::O_MUL:
- case node_t::O_DIV:
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;
+ 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:
+ case node_t::O_DIV:
+ out << "(";
+ dump_tree(out, node->left);
+ switch (node->type) {
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;
}
diff --git a/expr.h b/valexpr.h
index 937e2fa8..0d4413e7 100644
--- a/expr.h
+++ b/valexpr.h
@@ -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
diff --git a/walk.h b/walk.h
new file mode 100644
index 00000000..b509d1dc
--- /dev/null
+++ b/walk.h
@@ -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