summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2004-07-30 21:57:02 -0400
committerJohn Wiegley <johnw@newartisans.com>2004-07-30 21:57:02 -0400
commit94e76ae87e883291d13320738fe165c7a2a2415b (patch)
treeb90eff2ee3737ecdfea96dbee52ecd239fcb2578
parent5087a60deef7c618a07562511e9a1fbf2414776c (diff)
downloadfork-ledger-94e76ae87e883291d13320738fe165c7a2a2415b.tar.gz
fork-ledger-94e76ae87e883291d13320738fe165c7a2a2415b.tar.bz2
fork-ledger-94e76ae87e883291d13320738fe165c7a2a2415b.zip
two major changes
Complete changed the way format strings are handled. They are now compiled first, which is far more efficient than what was being done before. Also, there is now a global ledger::commodity_t::commodities map, which saves me from having to pass the current journal around to a zillion different functions, for the sole purpose of making sure that all commodity symbols that are parsed refer to the same commodity object.
-rw-r--r--amount.cc45
-rw-r--r--binary.cc10
-rw-r--r--binary.h2
-rw-r--r--constraint.cc4
-rw-r--r--constraint.h10
-rw-r--r--error.h7
-rw-r--r--expr.cc230
-rw-r--r--expr.h106
-rw-r--r--format.cc259
-rw-r--r--format.h85
-rw-r--r--gnucash.cc10
-rw-r--r--item.cc4
-rw-r--r--ledger.cc33
-rw-r--r--ledger.h57
-rw-r--r--main.cc275
-rw-r--r--textual.cc73
16 files changed, 643 insertions, 567 deletions
diff --git a/amount.cc b/amount.cc
index 5d1348af..80fd3ee9 100644
--- a/amount.cc
+++ b/amount.cc
@@ -10,8 +10,6 @@
namespace ledger {
-commodity_t * amount_t::null_commodity = NULL;
-
static void mpz_round(mpz_t value, int precision)
{
mpz_t divisor;
@@ -544,7 +542,7 @@ void parse_commodity(std::istream& in, std::string& symbol)
}
}
-void amount_t::parse(std::istream& in, ledger_t * ledger)
+void amount_t::parse(std::istream& in)
{
// The possible syntax for an amount is:
//
@@ -607,25 +605,10 @@ void amount_t::parse(std::istream& in, ledger_t * ledger)
assert(precision <= MAX_PRECISION);
// Create the commodity if has not already been seen.
- if (ledger) {
- commodity = ledger->find_commodity(symbol, true);
- commodity->flags |= flags;
- if (precision > commodity->precision)
- commodity->precision = precision;
- }
- else if (symbol.empty()) {
- if (! null_commodity) {
- commodity = null_commodity = new commodity_t(symbol, precision, flags);
- } else {
- commodity = null_commodity;
- commodity->flags |= flags;
- if (precision > commodity->precision)
- commodity->precision = precision;
- }
- }
- else {
- commodity = new commodity_t(symbol, precision, flags);
- }
+ commodity = commodity_t::find_commodity(symbol, true);
+ commodity->flags |= flags;
+ if (precision > commodity->precision)
+ commodity->precision = precision;
// The number is specified as the user desires, with the commodity
// flags telling how to parse it.
@@ -696,6 +679,24 @@ void (*commodity_t::updater)(commodity_t * commodity,
const amount_t& price,
const std::time_t moment) = NULL;
+commodities_map commodity_t::commodities;
+
+commodity_t * commodity_t::find_commodity(const std::string& symbol,
+ bool auto_create)
+{
+ commodities_map::const_iterator i = commodities.find(symbol);
+ if (i != commodities.end())
+ return (*i).second;
+
+ if (auto_create) {
+ commodity_t * commodity = new commodity_t(symbol);
+ add_commodity(commodity);
+ return commodity;
+ }
+
+ return NULL;
+}
+
amount_t commodity_t::value(const std::time_t moment)
{
std::time_t age = 0;
diff --git a/binary.cc b/binary.cc
index f760b83c..9400fc54 100644
--- a/binary.cc
+++ b/binary.cc
@@ -343,8 +343,8 @@ unsigned int read_binary_ledger(std::istream& in,
for (int i = count; --i >= 0; ) {
commodity_t * commodity = read_binary_commodity(in);
std::pair<commodities_map::iterator, bool> result
- = ledger->commodities.insert(commodities_pair(commodity->symbol,
- commodity));
+ = commodity_t::commodities.insert(commodities_pair(commodity->symbol,
+ commodity));
assert(result.second || master);
}
@@ -593,11 +593,11 @@ void write_binary_ledger(std::ostream& out, ledger_t * ledger,
write_binary_account(out, ledger->master);
- unsigned long count = ledger->commodities.size();
+ unsigned long count = commodity_t::commodities.size();
out.write((char *)&count, sizeof(count));
- for (commodities_map::const_iterator i = ledger->commodities.begin();
- i != ledger->commodities.end();
+ for (commodities_map::const_iterator i = commodity_t::commodities.begin();
+ i != commodity_t::commodities.end();
i++)
write_binary_commodity(out, (*i).second);
diff --git a/binary.h b/binary.h
index 4fe0a5ea..46c7f55b 100644
--- a/binary.h
+++ b/binary.h
@@ -9,7 +9,7 @@ extern unsigned long magic_number;
extern unsigned int read_binary_ledger(std::istream& in,
const std::string& leader,
- ledger_t * book,
+ ledger_t * journal,
account_t * master = NULL);
extern void write_binary_ledger(std::ostream& out,
diff --git a/constraint.cc b/constraint.cc
index 4720e62b..fe00f4d2 100644
--- a/constraint.cc
+++ b/constraint.cc
@@ -93,10 +93,10 @@ bool matches(const masks_list& regexps, const std::string& str,
bool constraints_t::matches_date_range(const std::time_t date) const
{
- if (have_beginning && difftime(date, begin_date) < 0)
+ if (begin_date != -1 && difftime(date, begin_date) < 0)
return false;
- if (have_ending && difftime(date, end_date) >= 0)
+ if (end_date != -1 && difftime(date, end_date) >= 0)
return false;
if (have_date_mask) {
diff --git a/constraint.h b/constraint.h
index f7ecef62..79e01fc6 100644
--- a/constraint.h
+++ b/constraint.h
@@ -104,9 +104,7 @@ class constraints_t
bool show_empty;
std::time_t begin_date;
- bool have_beginning;
std::time_t end_date;
- bool have_ending;
struct std::tm date_mask;
bool have_date_mask;
@@ -128,8 +126,8 @@ class constraints_t
show_subtotals = true;
show_empty = false;
- have_beginning = false;
- have_ending = false;
+ begin_date = -1;
+ end_date = -1;
have_date_mask = false;
period = PERIOD_NONE;
@@ -140,11 +138,11 @@ class constraints_t
~constraints_t();
std::time_t begin() const {
- return have_beginning ? begin_date : 0;
+ return begin_date == -1 ? 0 : begin_date;
}
std::time_t end() const {
- return have_ending ? end_date : std::time(NULL);
+ return end_date == -1 ? std::time(NULL) : end_date;
}
bool matches_date_range(const std::time_t date) const;
diff --git a/error.h b/error.h
index 23386823..51d152cd 100644
--- a/error.h
+++ b/error.h
@@ -32,6 +32,13 @@ class expr_error : public error
virtual ~expr_error() throw() {}
};
+class format_error : public error
+{
+ public:
+ format_error(const std::string& reason) throw() : error(reason) {}
+ virtual ~format_error() throw() {}
+};
+
class parse_error : public error
{
unsigned int line;
diff --git a/expr.cc b/expr.cc
index 7ed5bda2..2020d20f 100644
--- a/expr.cc
+++ b/expr.cc
@@ -149,14 +149,14 @@ balance_t node_t::compute(const item_t * item,
return temp;
}
-node_t * parse_term(std::istream& in, ledger_t * ledger);
+node_t * parse_term(std::istream& in);
-inline node_t * parse_term(const char * p, ledger_t * ledger) {
+inline node_t * parse_term(const char * p) {
std::istringstream stream(p);
- return parse_term(stream, ledger);
+ return parse_term(stream);
}
-node_t * parse_term(std::istream& in, ledger_t * ledger)
+node_t * parse_term(std::istream& in)
{
node_t * node = NULL;
@@ -185,8 +185,8 @@ node_t * parse_term(std::istream& in, ledger_t * ledger)
}
if (! ident.empty()) {
- node = new node_t(CONSTANT_A);
- node->constant_a.parse(ident, ledger);
+ node = new node_t(node_t::CONSTANT_A);
+ node->constant_a.parse(ident);
}
return node;
}
@@ -194,62 +194,62 @@ node_t * parse_term(std::istream& in, ledger_t * ledger)
in.get(c);
switch (c) {
// Basic terms
- case 'a': node = new node_t(AMOUNT); break;
- case 'c': node = new node_t(COST); break;
- case 'd': node = new node_t(DATE); break;
- case 'b': node = new node_t(BEGIN_DATE); break;
- case 'e': node = new node_t(END_DATE); break;
- case 'i': node = new node_t(INDEX); break;
- case 'B': node = new node_t(BALANCE); break;
- case 'T': node = new node_t(TOTAL); break;
- case 'C': node = new node_t(COST_TOTAL); break;
+ case 'a': node = new node_t(node_t::AMOUNT); break;
+ case 'c': node = new node_t(node_t::COST); break;
+ case 'd': node = new node_t(node_t::DATE); break;
+ case 'b': node = new node_t(node_t::BEGIN_DATE); break;
+ case 'e': node = new node_t(node_t::END_DATE); break;
+ case 'i': node = new node_t(node_t::INDEX); break;
+ case 'B': node = new node_t(node_t::BALANCE); break;
+ case 'T': node = new node_t(node_t::TOTAL); break;
+ case 'C': node = new node_t(node_t::COST_TOTAL); break;
// Compound terms
- case 'v': node = parse_expr("P(a,d)", ledger); break;
- case 'V': node = parse_term("P(T,d)", ledger); break;
- case 'g': node = parse_expr("v-c", ledger); break;
- case 'G': node = parse_expr("V-C", ledger); break;
- case 'o': node = parse_expr("d-b", ledger); break;
- case 'w': node = parse_expr("e-d", ledger); break;
+ case 'v': node = parse_expr("P(a,d)"); break;
+ case 'V': node = parse_term("P(T,d)"); break;
+ case 'g': node = parse_expr("v-c"); break;
+ case 'G': node = parse_expr("V-C"); break;
+ case 'o': node = parse_expr("d-b"); break;
+ case 'w': node = parse_expr("e-d"); break;
// Functions
case '-':
- node = new node_t(F_NEG);
- node->left = parse_term(in, ledger);
+ node = new node_t(node_t::F_NEG);
+ node->left = parse_term(in);
break;
- case 'A': // absolute value ("positive")
- node = new node_t(F_ABS);
- node->left = parse_term(in, ledger);
+ case 'A':
+ node = new node_t(node_t::F_ABS);
+ node->left = parse_term(in);
break;
case 'M':
- node = new node_t(F_ARITH_MEAN);
- node->left = parse_term(in, ledger);
+ node = new node_t(node_t::F_ARITH_MEAN);
+ node->left = parse_term(in);
break;
case 'D': {
- node = new node_t(O_SUB);
- node->left = parse_term("a", ledger);
- node->right = parse_term(in, ledger);
+ node = new node_t(node_t::O_SUB);
+ node->left = parse_term("a");
+ node->right = parse_term(in);
break;
}
case 'P':
- node = new node_t(F_VALUE);
+ node = new node_t(node_t::F_VALUE);
if (in.peek() == '(') {
in.get(c);
- node->left = parse_expr(in, ledger);
+ node->left = parse_expr(in);
if (in.peek() == ',') {
in.get(c);
- node->right = parse_expr(in, ledger);
+ node->right = parse_expr(in);
}
if (in.peek() == ')')
in.get(c);
else
throw expr_error("Missing ')'");
} else {
- node->left = parse_term(in, ledger);
+ node->left = parse_term(in);
}
break;
@@ -267,7 +267,7 @@ node_t * parse_term(std::istream& in, ledger_t * ledger)
}
if (c == '/') {
in.get(c);
- node = new node_t(F_REGEXP);
+ node = new node_t(node_t::F_REGEXP);
node->mask = new mask_t(ident);
} else {
throw expr_error("Missing closing '/'");
@@ -276,7 +276,7 @@ node_t * parse_term(std::istream& in, ledger_t * ledger)
}
case '(':
- node = parse_expr(in, ledger);
+ node = parse_expr(in);
if (in.peek() == ')')
in.get(c);
else
@@ -294,7 +294,7 @@ node_t * parse_term(std::istream& in, ledger_t * ledger)
}
if (c == ']') {
in.get(c);
- node = new node_t(CONSTANT_T);
+ node = new node_t(node_t::CONSTANT_T);
if (! parse_date(ident.c_str(), &node->constant_t))
throw expr_error("Failed to parse date");
} else {
@@ -311,11 +311,11 @@ node_t * parse_term(std::istream& in, ledger_t * ledger)
return node;
}
-node_t * parse_mul_expr(std::istream& in, ledger_t * ledger)
+node_t * parse_mul_expr(std::istream& in)
{
node_t * node = NULL;
- node = parse_term(in, ledger);
+ node = parse_term(in);
if (node && ! in.eof()) {
char c = in.peek();
@@ -324,17 +324,17 @@ node_t * parse_mul_expr(std::istream& in, ledger_t * ledger)
switch (c) {
case '*': {
node_t * prev = node;
- node = new node_t(O_MUL);
+ node = new node_t(node_t::O_MUL);
node->left = prev;
- node->right = parse_term(in, ledger);
+ node->right = parse_term(in);
break;
}
case '/': {
node_t * prev = node;
- node = new node_t(O_DIV);
+ node = new node_t(node_t::O_DIV);
node->left = prev;
- node->right = parse_term(in, ledger);
+ node->right = parse_term(in);
break;
}
}
@@ -345,11 +345,11 @@ node_t * parse_mul_expr(std::istream& in, ledger_t * ledger)
return node;
}
-node_t * parse_add_expr(std::istream& in, ledger_t * ledger)
+node_t * parse_add_expr(std::istream& in)
{
node_t * node = NULL;
- node = parse_mul_expr(in, ledger);
+ node = parse_mul_expr(in);
if (node && ! in.eof()) {
char c = in.peek();
@@ -358,17 +358,17 @@ node_t * parse_add_expr(std::istream& in, ledger_t * ledger)
switch (c) {
case '+': {
node_t * prev = node;
- node = new node_t(O_ADD);
+ node = new node_t(node_t::O_ADD);
node->left = prev;
- node->right = parse_mul_expr(in, ledger);
+ node->right = parse_mul_expr(in);
break;
}
case '-': {
node_t * prev = node;
- node = new node_t(O_SUB);
+ node = new node_t(node_t::O_SUB);
node->left = prev;
- node->right = parse_mul_expr(in, ledger);
+ node->right = parse_mul_expr(in);
break;
}
}
@@ -379,19 +379,19 @@ node_t * parse_add_expr(std::istream& in, ledger_t * ledger)
return node;
}
-node_t * parse_logic_expr(std::istream& in, ledger_t * ledger)
+node_t * parse_logic_expr(std::istream& in)
{
node_t * node = NULL;
if (in.peek() == '!') {
char c;
in.get(c);
- node = new node_t(O_NOT);
- node->left = parse_logic_expr(in, ledger);
+ node = new node_t(node_t::O_NOT);
+ node->left = parse_logic_expr(in);
return node;
}
- node = parse_add_expr(in, ledger);
+ node = parse_add_expr(in);
if (node && ! in.eof()) {
char c = in.peek();
@@ -400,33 +400,33 @@ node_t * parse_logic_expr(std::istream& in, ledger_t * ledger)
switch (c) {
case '=': {
node_t * prev = node;
- node = new node_t(O_EQ);
+ node = new node_t(node_t::O_EQ);
node->left = prev;
- node->right = parse_add_expr(in, ledger);
+ node->right = parse_add_expr(in);
break;
}
case '<': {
node_t * prev = node;
- node = new node_t(O_LT);
+ node = new node_t(node_t::O_LT);
if (in.peek() == '=') {
in.get(c);
- node->type = O_LTE;
+ node->type = node_t::O_LTE;
}
node->left = prev;
- node->right = parse_add_expr(in, ledger);
+ node->right = parse_add_expr(in);
break;
}
case '>': {
node_t * prev = node;
- node = new node_t(O_GT);
+ node = new node_t(node_t::O_GT);
if (in.peek() == '=') {
in.get(c);
- node->type = O_GTE;
+ node->type = node_t::O_GTE;
}
node->left = prev;
- node->right = parse_add_expr(in, ledger);
+ node->right = parse_add_expr(in);
break;
}
@@ -443,11 +443,11 @@ node_t * parse_logic_expr(std::istream& in, ledger_t * ledger)
return node;
}
-node_t * parse_expr(std::istream& in, ledger_t * ledger)
+node_t * parse_expr(std::istream& in)
{
node_t * node = NULL;
- node = parse_logic_expr(in, ledger);
+ node = parse_logic_expr(in);
if (node && ! in.eof()) {
char c = in.peek();
@@ -456,27 +456,27 @@ node_t * parse_expr(std::istream& in, ledger_t * ledger)
switch (c) {
case '&': {
node_t * prev = node;
- node = new node_t(O_AND);
+ node = new node_t(node_t::O_AND);
node->left = prev;
- node->right = parse_logic_expr(in, ledger);
+ node->right = parse_logic_expr(in);
break;
}
case '|': {
node_t * prev = node;
- node = new node_t(O_OR);
+ node = new node_t(node_t::O_OR);
node->left = prev;
- node->right = parse_logic_expr(in, ledger);
+ node->right = parse_logic_expr(in);
break;
}
case '?': {
node_t * prev = node;
- node = new node_t(O_QUES);
+ node = new node_t(node_t::O_QUES);
node->left = prev;
- node_t * choices = new node_t(O_COL);
+ node_t * choices = new node_t(node_t::O_COL);
node->right = choices;
- choices->left = parse_logic_expr(in, ledger);
+ choices->left = parse_logic_expr(in);
c = in.peek();
if (c != ':') {
std::ostringstream err;
@@ -484,7 +484,7 @@ node_t * parse_expr(std::istream& in, ledger_t * ledger)
throw expr_error(err.str());
}
in.get(c);
- choices->right = parse_logic_expr(in, ledger);
+ choices->right = parse_logic_expr(in);
break;
}
@@ -512,43 +512,43 @@ namespace ledger {
static void dump_tree(std::ostream& out, node_t * node)
{
switch (node->type) {
- case CONSTANT_A: out << "CONST[" << node->constant_a << "]"; break;
- case CONSTANT_T: out << "DATE/TIME[" << node->constant_t << "]"; break;
- case AMOUNT: out << "AMOUNT"; break;
- case COST: out << "COST"; break;
- case DATE: out << "DATE"; break;
- case INDEX: out << "INDEX"; break;
- case BALANCE: out << "BALANCE"; break;
- case COST_BALANCE: out << "COST_BALANCE"; break;
- case TOTAL: out << "TOTAL"; break;
- case COST_TOTAL: out << "COST_TOTAL"; break;
- case BEGIN_DATE: out << "BEGIN"; break;
- case END_DATE: out << "END"; break;
-
- case F_ARITH_MEAN:
+ 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;
+ case node_t::INDEX: out << "INDEX"; break;
+ case node_t::BALANCE: out << "BALANCE"; break;
+ case node_t::COST_BALANCE: out << "COST_BALANCE"; break;
+ case node_t::TOTAL: out << "TOTAL"; break;
+ case node_t::COST_TOTAL: out << "COST_TOTAL"; break;
+ case node_t::BEGIN_DATE: out << "BEGIN"; break;
+ case node_t::END_DATE: out << "END"; break;
+
+ case node_t::F_ARITH_MEAN:
out << "MEAN(";
dump_tree(out, node->left);
out << ")";
break;
- case F_NEG:
+ case node_t::F_NEG:
out << "ABS(";
dump_tree(out, node->left);
out << ")";
break;
- case F_ABS:
+ case node_t::F_ABS:
out << "ABS(";
dump_tree(out, node->left);
out << ")";
break;
- case F_REGEXP:
+ case node_t::F_REGEXP:
assert(node->mask);
out << "RE(" << node->mask->pattern << ")";
break;
- case F_VALUE:
+ case node_t::F_VALUE:
out << "VALUE(";
dump_tree(out, node->left);
if (node->right) {
@@ -558,12 +558,12 @@ static void dump_tree(std::ostream& out, node_t * node)
out << ")";
break;
- case O_NOT:
+ case node_t::O_NOT:
out << "!";
dump_tree(out, node->left);
break;
- case O_QUES:
+ case node_t::O_QUES:
dump_tree(out, node->left);
out << "?";
dump_tree(out, node->right->left);
@@ -571,38 +571,38 @@ static void dump_tree(std::ostream& out, node_t * node)
dump_tree(out, node->right->right);
break;
- case O_AND:
- case O_OR:
- case O_EQ:
- case O_LT:
- case O_LTE:
- case O_GT:
- case O_GTE:
- case O_ADD:
- case O_SUB:
- case O_MUL:
- case O_DIV:
+ case node_t::O_AND:
+ case node_t::O_OR:
+ 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 O_AND: out << " & "; break;
- case O_OR: out << " | "; break;
- case O_EQ: out << "="; break;
- case O_LT: out << "<"; break;
- case O_LTE: out << "<="; break;
- case O_GT: out << ">"; break;
- case O_GTE: out << ">="; break;
- case O_ADD: out << "+"; break;
- case O_SUB: out << "-"; break;
- case O_MUL: out << "*"; break;
- case O_DIV: out << "/"; break;
+ 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;
+ case node_t::O_DIV: out << "/"; break;
default: assert(0); break;
}
dump_tree(out, node->right);
out << ")";
break;
- case LAST:
+ case node_t::LAST:
default:
assert(0);
break;
diff --git a/expr.h b/expr.h
index 355f34c9..f4961bfc 100644
--- a/expr.h
+++ b/expr.h
@@ -7,55 +7,55 @@
namespace ledger {
-enum kind_t {
- // Constants
- CONSTANT_A,
- CONSTANT_T,
-
- // Item details
- AMOUNT,
- COST,
- DATE,
- INDEX,
-
- // Item totals
- BALANCE,
- COST_BALANCE,
- TOTAL,
- COST_TOTAL,
-
- // Constraint details
- BEGIN_DATE,
- END_DATE,
-
- // Functions
- F_ARITH_MEAN,
- F_VALUE,
- F_NEG,
- F_ABS,
- F_REGEXP,
-
- // Binary operators
- O_ADD,
- O_SUB,
- O_MUL,
- O_DIV,
- O_EQ,
- O_LT,
- O_LTE,
- O_GT,
- O_GTE,
- O_NOT,
- O_AND,
- O_OR,
- O_QUES,
- O_COL,
-
- LAST
-};
-
struct node_t
{
+ enum kind_t {
+ // Constants
+ CONSTANT_A,
+ CONSTANT_T,
+
+ // Item details
+ AMOUNT,
+ COST,
+ DATE,
+ INDEX,
+
+ // Item totals
+ BALANCE,
+ COST_BALANCE,
+ TOTAL,
+ COST_TOTAL,
+
+ // Constraint details
+ BEGIN_DATE,
+ END_DATE,
+
+ // Functions
+ F_ARITH_MEAN,
+ F_VALUE,
+ F_NEG,
+ F_ABS,
+ F_REGEXP,
+
+ // Binary operators
+ O_ADD,
+ O_SUB,
+ O_MUL,
+ O_DIV,
+ O_EQ,
+ O_LT,
+ O_LTE,
+ O_GT,
+ O_GTE,
+ O_NOT,
+ O_AND,
+ O_OR,
+ O_QUES,
+ O_COL,
+
+ LAST
+ };
+
kind_t type;
node_t * left;
node_t * right;
@@ -78,18 +78,18 @@ struct node_t
const std::time_t end = -1) const;
};
-node_t * parse_expr(std::istream& in, ledger_t * ledger);
+node_t * parse_expr(std::istream& in);
-inline node_t * parse_expr(const char * p, ledger_t * ledger) {
+inline node_t * parse_expr(const char * p) {
std::istringstream stream(p);
- return parse_expr(stream, ledger);
+ return parse_expr(stream);
}
-inline node_t * parse_expr(const std::string& str, ledger_t * ledger) {
- return parse_expr(str.c_str(), ledger);
+inline node_t * parse_expr(const std::string& str) {
+ return parse_expr(str.c_str());
}
-inline node_t * find_node(node_t * node, kind_t type) {
+inline node_t * find_node(node_t * node, node_t::kind_t type) {
node_t * result = NULL;
if (node->type == type)
result = node;
diff --git a/format.cc b/format.cc
index 1c7bf12d..6b5a4a9b 100644
--- a/format.cc
+++ b/format.cc
@@ -1,4 +1,5 @@
#include "format.h"
+#include "error.h"
namespace ledger {
@@ -24,20 +25,36 @@ std::string maximal_account_name(const item_t * item,
return name;
}
-std::string format_t::report_line(const item_t * item,
- const item_t * displayed_parent) const
+node_t * format_t::value_expr = NULL;
+node_t * format_t::total_expr = NULL;
+
+element_t * format_t::parse_elements(const std::string& fmt)
{
- std::string result;
+ element_t * result = NULL;
+ element_t * current = NULL;
+ std::string str;
- for (const char * p = format_string.c_str(); *p; p++) {
+ for (const char * p = fmt.c_str(); *p; p++) {
if (*p == '%') {
- bool leftalign = false;
- int width = 0;
- int strict_width = 0;
+ if (! result) {
+ current = result = new element_t;
+ } else {
+ current->next = new element_t;
+ current = current->next;
+ }
+
+ if (! str.empty()) {
+ current->type = element_t::STRING;
+ current->chars = str;
+ str = "";
+
+ current->next = new element_t;
+ current = current->next;
+ }
++p;
if (*p == '-') {
- leftalign = true;
+ current->align_left = true;
++p;
}
@@ -45,7 +62,7 @@ std::string format_t::report_line(const item_t * item,
while (*p && std::isdigit(*p))
num += *p++;
if (! num.empty())
- width = std::atol(num.c_str());
+ current->min_width = std::atol(num.c_str());
if (*p == '.') {
++p;
@@ -53,132 +70,172 @@ std::string format_t::report_line(const item_t * item,
while (*p && std::isdigit(*p))
num += *p++;
if (! num.empty()) {
- strict_width = std::atol(num.c_str());
- if (width == 0)
- width = strict_width;
+ current->max_width = std::atol(num.c_str());
+ if (current->min_width == 0)
+ current->min_width = current->max_width;
}
}
- std::ostringstream out;
-
- if (leftalign)
- out << std::left;
- else
- out << std::right;
-
- if (width > 0)
- out.width(width);
-
switch (*p) {
case '%':
- out << "%";
+ current->type = element_t::STRING;
+ current->chars = "%";
break;
- case '(': {
+ case '(':
++p;
num = "";
while (*p && *p != ')')
num += *p++;
- assert(*p == ')');
+ if (*p != ')')
+ throw format_error("Missing ')'");
- node_t * style = parse_expr(num, NULL);
- balance_t value = style->compute(item);
- value.write(out, width, strict_width > 0 ? strict_width : width);
- delete style;
+ current->type = element_t::VALUE_EXPR;
+ current->val_expr = parse_expr(num);
break;
- }
- case '[': {
+ case '[':
++p;
num = "";
while (*p && *p != ']')
num += *p++;
- assert(*p == ']');
-
- if (item->date != -1) {
- char buf[256];
- std::strftime(buf, 255, num.c_str(), std::gmtime(&item->date));
- out << (strict_width == 0 ? buf : truncated(buf, strict_width));
- } else {
- out << " ";
- }
+ if (*p != ']')
+ throw format_error("Missing ']'");
+
+ current->type = element_t::DATE_STRING;
+ current->chars = num;
break;
- }
- case 'd': {
- if (item->date != -1) {
- char buf[32];
- std::strftime(buf, 31, "%Y/%m/%d", std::gmtime(&item->date));
- out << (strict_width == 0 ? buf : truncated(buf, strict_width));
- } else {
- out << " ";
- }
+ case 'd':
+ current->type = element_t::DATE_STRING;
+ current->chars = "%Y/%m/%d";
break;
+
+ case 'p': current->type = element_t::PAYEE; break;
+ case 'n': current->type = element_t::ACCOUNT_NAME; break;
+ case 'N': current->type = element_t::ACCOUNT_FULLNAME; break;
+ case 't': current->type = element_t::VALUE; break;
+ case 'T': current->type = element_t::TOTAL; break;
+ case '_': current->type = element_t::SPACER; break;
}
+ } else {
+ str += *p;
+ }
+ }
- case 'p':
- out << (strict_width == 0 ?
- item->payee : truncated(item->payee, strict_width));
- break;
+ if (! str.empty()) {
+ if (! result) {
+ current = result = new element_t;
+ } else {
+ current->next = new element_t;
+ current = current->next;
+ }
+ current->type = element_t::STRING;
+ current->chars = str;
+ }
- case 'n':
- if (item->account) {
- std::string name = maximal_account_name(item, displayed_parent);
- out << (strict_width == 0 ? name : truncated(name, strict_width));
- } else {
- out << " ";
- }
- break;
+ return result;
+}
- case 'N':
- if (item->account)
- out << (strict_width == 0 ?
- item->account->fullname() :
- truncated(item->account->fullname(), strict_width));
- else
- out << " ";
- break;
+void format_t::format_elements(std::ostream& out, const item_t * item,
+ const item_t * displayed_parent) const
+{
+ std::string result;
- case 't':
- if (value_style) {
- balance_t value = compute_value(item);
- value.write(out, width, strict_width > 0 ? strict_width : width);
- }
- break;
+ for (const element_t * elem = elements;
+ elem;
+ elem = elem->next) {
+ if (elem->align_left)
+ out << std::left;
+ else
+ out << std::right;
+
+ if (elem->min_width > 0)
+ out.width(elem->min_width);
+
+ switch (elem->type) {
+ case element_t::STRING:
+ out << elem->chars;;
+ 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);
+ break;
+ }
- case 'T':
- if (total_style) {
- balance_t value = compute_total(item);
- value.write(out, width, strict_width > 0 ? strict_width : width);
- }
- break;
+ case element_t::DATE_STRING:
+ if (item->date != -1) {
+ char buf[256];
+ std::strftime(buf, 255, elem->chars.c_str(), std::gmtime(&item->date));
+ out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
+ } else {
+ out << " ";
+ }
+ break;
+
+ case element_t::PAYEE:
+ out << (elem->max_width == 0 ?
+ item->payee : truncated(item->payee, elem->max_width));
+ break;
+
+ case element_t::ACCOUNT_NAME:
+ if (item->account) {
+ std::string name = maximal_account_name(item, displayed_parent);
+ out << (elem->max_width == 0 ? name : truncated(name, elem->max_width));
+ } else {
+ out << " ";
+ }
+ break;
- case '_': {
- int depth = 0;
- for (const item_t * i = item; i->parent; i = i->parent)
- depth++;
+ case element_t::ACCOUNT_FULLNAME:
+ if (item->account)
+ out << (elem->max_width == 0 ?
+ item->account->fullname() :
+ truncated(item->account->fullname(), elem->max_width));
+ else
+ out << " ";
+ 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);
+ break;
+ }
- for (const item_t * i = item->parent;
- i && i->account && i != displayed_parent;
- i = i->parent)
- depth--;
+ 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);
+ break;
+ }
- while (--depth >= 0) {
- if (width > 0 || strict_width > 0)
- out.width(width > strict_width ? width : strict_width);
- out << " ";
- }
- 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) {
+ 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;
+ }
- result += out.str();
- } else {
- result += *p;
+ default:
+ assert(0);
+ break;
}
}
-
- return result;
}
} // namespace ledger
diff --git a/format.h b/format.h
index 298715cb..33443508 100644
--- a/format.h
+++ b/format.h
@@ -11,56 +11,89 @@ 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);
-struct format_t
+struct element_t
{
- std::string format_string;
- node_t * value_style;
- node_t * total_style;
+ enum kind_t {
+ STRING,
+ VALUE_EXPR,
+ DATE_STRING,
+ PAYEE,
+ ACCOUNT_NAME,
+ ACCOUNT_FULLNAME,
+ VALUE,
+ TOTAL,
+ SPACER
+ };
+
+ bool align_left;
+ unsigned int min_width;
+ unsigned int max_width;
+
+ kind_t type;
+ std::string chars;
+ node_t * val_expr;
+
+ struct element_t * next;
- format_t() {
- value_style = NULL;
- total_style = NULL;
+ element_t() : align_left(false), min_width(0), max_width(0),
+ type(STRING), val_expr(NULL), next(NULL) {}
+
+ ~element_t() {
+ if (val_expr) delete val_expr;
+ if (next) delete next; // recursive, but not too deep
}
+};
+
+struct format_t
+{
+ element_t * elements;
+ static node_t * value_expr;
+ static node_t * total_expr;
+
+ format_t(const std::string& _format) {
+ elements = parse_elements(_format);
+ }
~format_t() {
- if (value_style) delete value_style;
- if (total_style) delete total_style;
+ if (elements) delete elements;
}
+ 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;
+
#if 1
- balance_t compute_value(const item_t * item) const {
- if (value_style)
- return value_style->compute(item);
+ static balance_t compute_value(const item_t * item) {
+ if (value_expr)
+ return value_expr->compute(item);
else
return balance_t();
}
- balance_t compute_total(const item_t * item) const {
- if (total_style)
- return total_style->compute(item);
+ static balance_t compute_total(const item_t * item) {
+ if (total_expr)
+ return total_expr->compute(item);
else
return balance_t();
}
#else
- balance_t compute_value(const item_t * item,
- const constraints_t& constraints) const {
- if (value_style)
- return value_style->compute(item, constraints.begin(), constraints.end());
+ static balance_t compute_value(const item_t * item,
+ const constraints_t& constraints) {
+ if (value_expr)
+ return value_expr->compute(item, constraints.begin(), constraints.end());
else
return balance_t();
}
- balance_t compute_total(const item_t * item,
- const constraints_t& constraints) const {
- if (total_style)
- return total_style->compute(item, constraints.begin(), constraints.end());
+ static balance_t compute_total(const item_t * item,
+ const constraints_t& constraints) {
+ if (total_expr)
+ return total_expr->compute(item, constraints.begin(), constraints.end());
else
return balance_t();
}
#endif
-
- std::string report_line(const item_t * item,
- const item_t * displayed_parent = NULL) const;
};
} // namespace ledger
diff --git a/gnucash.cc b/gnucash.cc
index df3ebbf3..7c8589f0 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -113,7 +113,7 @@ static void endElement(void *userData, const char *name)
}
else if (std::strcmp(name, "gnc:commodity") == 0) {
assert(curr_comm);
- curr_ledger->add_commodity(curr_comm);
+ commodity_t::add_commodity(curr_comm);
curr_comm = NULL;
}
else if (std::strcmp(name, "gnc:transaction") == 0) {
@@ -166,9 +166,9 @@ static void dataHandler(void *userData, const char *s, int len)
if (curr_comm)
curr_comm->symbol = std::string(s, len);
else if (curr_account)
- curr_account_comm = curr_ledger->find_commodity(std::string(s, len));
+ curr_account_comm = commodity_t::find_commodity(std::string(s, len));
else if (curr_entry)
- entry_comm = curr_ledger->find_commodity(std::string(s, len));
+ entry_comm = commodity_t::find_commodity(std::string(s, len));
break;
case COMM_NAME:
@@ -271,8 +271,8 @@ int parse_gnucash(std::istream& in, ledger_t * ledger, account_t * master)
// GnuCash uses the USD commodity without defining it, which really
// means $.
commodity_t * usd = new commodity_t("$", 2, COMMODITY_STYLE_THOUSANDS);
- ledger->add_commodity(usd);
- ledger->add_commodity(usd, "USD");
+ commodity_t::add_commodity(usd);
+ commodity_t::add_commodity(usd, "USD");
XML_Parser parser = XML_ParserCreate(NULL);
current_parser = parser;
diff --git a/item.cc b/item.cc
index 6646cf42..abdeea9f 100644
--- a/item.cc
+++ b/item.cc
@@ -12,7 +12,7 @@ item_t * walk_accounts(const account_t * account,
{
item_t * item = new item_t;
item->account = account;
- item->date = constraints.end();
+ item->date = constraints.end() - 1;
for (constrained_transactions_list_const_iterator
i(account->transactions.begin(),
@@ -112,6 +112,7 @@ item_t * walk_entries(entries_list::const_iterator begin,
item_t * subitem = new item_t;
subitem->parent = item;
subitem->date = item->date;
+ subitem->payee = item->payee;
subitem->account = (*j)->account;
subitem->value = *(*j);
item->subitems.push_back(subitem);
@@ -125,6 +126,7 @@ item_t * walk_entries(entries_list::const_iterator begin,
item_t * subitem = new item_t;
subitem->parent = item;
subitem->date = item->date;
+ subitem->payee = item->payee;
subitem->account = (*k)->account;
subitem->value = *(*k);
if (constraints.show_inverted)
diff --git a/ledger.cc b/ledger.cc
index 6654ea1c..741dcbaf 100644
--- a/ledger.cc
+++ b/ledger.cc
@@ -12,11 +12,6 @@ ledger_t::~ledger_t()
{
delete master;
- for (commodities_map::iterator i = commodities.begin();
- i != commodities.end();
- i++)
- delete (*i).second;
-
// Don't bother unhooking each entry's transactions from the
// accounts they refer to, because all accounts are about to
// be deleted.
@@ -26,22 +21,6 @@ ledger_t::~ledger_t()
delete *i;
}
-commodity_t * ledger_t::find_commodity(const std::string& symbol,
- bool auto_create)
-{
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
-
- if (auto_create) {
- commodity_t * commodity = new commodity_t(symbol);
- add_commodity(commodity);
- return commodity;
- }
-
- return NULL;
-}
-
bool ledger_t::add_entry(entry_t * entry)
{
entries.push_back(entry);
@@ -73,7 +52,7 @@ bool ledger_t::remove_entry(entry_t * entry)
return true;
}
-int parse_ledger_file(char * p, ledger_t * book)
+int parse_ledger_file(char * p, ledger_t * journal)
{
char * sep = std::strrchr(p, '=');
if (sep) *sep++ = '\0';
@@ -82,11 +61,11 @@ int parse_ledger_file(char * p, ledger_t * book)
account_t * master;
if (sep)
- master = book->find_account(sep);
+ master = journal->find_account(sep);
else
- master = book->master;
+ master = journal->master;
- book->sources.push_back(p);
+ journal->sources.push_back(p);
unsigned long magic;
std::istream::pos_type start = stream.tellg();
@@ -94,9 +73,9 @@ int parse_ledger_file(char * p, ledger_t * book)
stream.seekg(start);
if (magic == magic_number)
- return read_binary_ledger(stream, "", book, master);
+ return read_binary_ledger(stream, "", journal, master);
else
- return parse_textual_ledger(stream, book, master);
+ return parse_textual_ledger(stream, journal, master);
}
} // namespace ledger
diff --git a/ledger.h b/ledger.h
index 5aa47c9f..840d879d 100644
--- a/ledger.h
+++ b/ledger.h
@@ -50,8 +50,6 @@ class amount_t
base_type quantity; // amount, to MAX_PRECISION
commodity_t * commodity;
- static commodity_t * null_commodity;
-
bool valid() const {
if (quantity)
return commodity != NULL;
@@ -185,10 +183,10 @@ class amount_t
operator std::string() const;
- void parse(std::istream& in, ledger_t * ledger = NULL);
- void parse(const std::string& str, ledger_t * ledger = NULL) {
+ void parse(std::istream& in);
+ void parse(const std::string& str) {
std::istringstream stream(str);
- parse(stream, ledger);
+ parse(stream);
}
void write_quantity(std::ostream& out) const;
@@ -223,6 +221,9 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt);
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:
@@ -235,11 +236,34 @@ class commodity_t
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)
@@ -377,17 +401,14 @@ inline std::ostream& operator<<(std::ostream& out, const account_t& acct) {
}
-typedef std::map<const std::string, commodity_t *> commodities_map;
-typedef std::pair<const std::string, commodity_t *> commodities_pair;
-
typedef std::list<entry_t *> entries_list;
class ledger_t
{
public:
- account_t * master;
- commodities_map commodities;
- entries_list entries;
+ account_t * master;
+ entries_list entries;
+
std::list<std::string> sources;
ledger_t() {
@@ -409,23 +430,11 @@ class ledger_t
return master->find_account(name, auto_create);
}
- void add_commodity(commodity_t * commodity, const std::string symbol = "") {
- commodities.insert(commodities_pair(symbol.empty() ?
- commodity->symbol : symbol, commodity));
- }
- bool remove_commodity(commodity_t * commodity) {
- commodities_map::size_type n = commodities.erase(commodity->symbol);
- return n > 0;
- }
-
- commodity_t * find_commodity(const std::string& symbol,
- bool auto_create = false);
-
bool add_entry(entry_t * entry);
bool remove_entry(entry_t * entry);
};
-int parse_ledger_file(char * p, ledger_t * book);
+int parse_ledger_file(char * p, ledger_t * journal);
} // namespace ledger
diff --git a/main.cc b/main.cc
index 349ada47..d20e8c23 100644
--- a/main.cc
+++ b/main.cc
@@ -48,7 +48,7 @@ void show_balances(std::ostream& out,
if (match && constraints(*i) &&
((*i)->subitems.size() != 1 ||
(*i)->total != (*i)->subitems[0]->total)) {
- out << format.report_line(*i, parent);
+ format.format_elements(out, *i, parent);
parent = *i;
}
@@ -69,9 +69,10 @@ void balance_report(std::ostream& out,
show_balances(out, top->subitems, constraints, format, top);
- if (constraints.show_subtotals && top->subitems.size() > 1 && top->total)
- std::cout << "--------------------\n"
- << format.report_line(top);
+ if (constraints.show_subtotals && top->subitems.size() > 1 && top->total) {
+ std::cout << "--------------------\n";
+ format.format_elements(std::cout, top);
+ }
}
@@ -81,7 +82,7 @@ void balance_report(std::ostream& out,
//
static const std::string reg_fmt
- = "%10d %-.20p %/%-.22N %12.66t %12.80T\n";
+ = "%10d %-.20p %-.22N %12.66t %12.80T\n%/%22_ %-.22N %12.66t %12.80T\n";
static bool show_commodities_revalued = false;
static bool show_commodities_revalued_only = false;
@@ -91,12 +92,10 @@ static void report_value_change(std::ostream& out,
const balance_pair_t& balance,
const balance_pair_t& prev_balance,
const constraints_t& constraints,
- const format_t& format,
- const std::string& first_line_format,
- const std::string& next_lines_format)
+ const format_t& first_line_format,
+ const format_t& next_lines_format)
{
- static std::time_t prev_date = -1;
-
+ static std::time_t prev_date = -1;
if (prev_date == -1) {
prev_date = date;
return;
@@ -105,11 +104,11 @@ static void report_value_change(std::ostream& out,
item_t temp;
temp.date = prev_date;
temp.total = prev_balance;
- balance_t prev_bal = format.compute_total(&temp);
+ balance_t prev_bal = format_t::compute_total(&temp);
temp.date = date;
temp.total = balance;
- balance_t cur_bal = format.compute_total(&temp);
+ balance_t cur_bal = format_t::compute_total(&temp);
if (balance_t diff = cur_bal - prev_bal) {
temp.value = diff;
@@ -117,17 +116,8 @@ static void report_value_change(std::ostream& out,
temp.payee = "Commodities revalued";
if (constraints(&temp)) {
- format_t copy = format;
-
- copy.format_string = first_line_format;
- out << copy.report_line(&temp);
-
- copy.format_string = next_lines_format;
- out << copy.report_line(&temp);
-
- // Prevent double-deletion
- copy.value_style = NULL;
- copy.total_style = NULL;
+ first_line_format.format_elements(out, &temp);
+ next_lines_format.format_elements(out, &temp);
}
}
@@ -137,25 +127,12 @@ static void report_value_change(std::ostream& out,
void register_report(std::ostream& out,
item_t * top,
const constraints_t& constraints,
- const format_t& format)
+ const format_t& first_line_format,
+ const format_t& next_lines_format)
{
if (constraints.sort_order)
top->sort(constraints.sort_order);
- format_t copy = format;
-
- std::string first_line_format;
- std::string next_lines_format;
-
- const char * f = format.format_string.c_str();
- 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 = format.format_string;
- next_lines_format = format.format_string;
- }
-
balance_pair_t balance;
balance_pair_t last_reported;
account_t splits(NULL, "<Total>");
@@ -163,13 +140,6 @@ void register_report(std::ostream& out,
for (items_deque::const_iterator i = top->subitems.begin();
i != top->subitems.end();
i++) {
- copy.format_string = first_line_format;
-
- std::string header = copy.report_line(*i, top);
- unsigned int header_len = header.length();
-
- copy.format_string = next_lines_format;
-
bool first = true;
if ((*i)->subitems.size() > 1 && ! constraints.show_expanded) {
@@ -187,14 +157,14 @@ void register_report(std::ostream& out,
bool show = constraints(&summary);
if (show && show_commodities_revalued)
report_value_change(out, summary.date, balance, last_reported,
- constraints, copy, first_line_format,
- next_lines_format);
+ constraints, first_line_format, next_lines_format);
balance += summary.value;
if (show) {
if (! show_commodities_revalued_only)
- out << header << copy.report_line(&summary, *i);
+ first_line_format.format_elements(out, *i, top);
+
if (show_commodities_revalued)
last_reported = balance;
}
@@ -207,7 +177,7 @@ void register_report(std::ostream& out,
bool show = constraints(*j);
if (show && first && show_commodities_revalued) {
report_value_change(out, (*i)->date, balance, last_reported,
- constraints, copy, first_line_format,
+ constraints, first_line_format,
next_lines_format);
if (show_commodities_revalued_only)
first = false;
@@ -219,13 +189,12 @@ void register_report(std::ostream& out,
if (! show_commodities_revalued_only) {
if (first) {
first = false;
- out << header;
+ first_line_format.format_elements(out, *j, *i);
} else {
- out.width(header_len);
- out << " ";
+ next_lines_format.format_elements(out, *j, *i);
}
- out << copy.report_line(*j, *i);
}
+
if (show_commodities_revalued)
last_reported = balance;
}
@@ -235,12 +204,7 @@ void register_report(std::ostream& out,
if (show_commodities_revalued)
report_value_change(out, constraints.end(), balance, last_reported,
- constraints, copy, first_line_format,
- next_lines_format);
-
- // To stop these from getting deleted when copy goes out of scope
- copy.value_style = NULL;
- copy.total_style = NULL;
+ constraints, first_line_format, next_lines_format);
}
@@ -377,7 +341,7 @@ bool add_new_entry(int index, int argc, char **argv, ledger_t * ledger)
}
-void set_price_conversion(const std::string& setting, ledger_t * ledger)
+void set_price_conversion(const std::string& setting)
{
char buf[128];
std::strcpy(buf, setting.c_str());
@@ -392,9 +356,9 @@ void set_price_conversion(const std::string& setting, ledger_t * ledger)
*p++ = '\0';
amount_t price;
- price.parse(p, ledger);
+ price.parse(p);
- commodity_t * commodity = ledger->find_commodity(c, true);
+ commodity_t * commodity = commodity_t::find_commodity(c, true);
commodity->set_conversion(price);
}
}
@@ -402,7 +366,6 @@ void set_price_conversion(const std::string& setting, ledger_t * ledger)
static long pricing_leeway = 24 * 3600;
static std::string price_db;
-static ledger_t * current_ledger = NULL;
static bool cache_dirty = false;
void download_price_quote(commodity_t * commodity,
@@ -438,7 +401,7 @@ void download_price_quote(commodity_t * commodity,
if (p) *p = '\0';
amount_t current;
- current.parse(buf, current_ledger);
+ current.parse(buf);
commodity->add_price(now, current);
@@ -506,13 +469,12 @@ static void show_help(std::ostream& out)
int main(int argc, char * argv[])
{
std::list<std::string> files;
- ledger::ledger_t * book = new ledger::ledger_t;
+ ledger::ledger_t * journal = new ledger::ledger_t;
ledger::constraints_t constraints;
- ledger::format_t format;
-
- std::string sort_order;
- std::string value_style = "a";
- std::string total_style = "T";
+ std::string format_string;
+ std::string sort_order;
+ std::string value_expr = "a";
+ std::string total_expr = "T";
// Initialize some variables based on environment variable settings
@@ -540,19 +502,17 @@ int main(int argc, char * argv[])
if (access(p, R_OK) != -1) {
std::ifstream instr(p);
if (! ledger::read_binary_ledger(instr, std::getenv("LEDGER"),
- book)) {
+ journal)) {
// We need to throw away what we've read, and create a new
// ledger
- delete book;
- book = new ledger::ledger_t;
+ delete journal;
+ journal = new ledger::ledger_t;
} else {
ledger::cache_dirty = false;
}
}
}
- ledger::current_ledger = book;
-
// Parse the command-line options
int c, index;
@@ -584,7 +544,7 @@ int main(int argc, char * argv[])
break;
case 'p':
- ledger::set_price_conversion(optarg, book);
+ ledger::set_price_conversion(optarg);
break;
// Constraint options
@@ -593,7 +553,6 @@ int main(int argc, char * argv[])
break;
case 'b':
- constraints.have_beginning = true;
if (! ledger::parse_date(optarg, &constraints.begin_date)) {
std::cerr << "Error: Bad begin date: " << optarg << std::endl;
return 1;
@@ -601,7 +560,6 @@ int main(int argc, char * argv[])
break;
case 'e':
- constraints.have_ending = true;
if (! ledger::parse_date(optarg, &constraints.end_date)) {
std::cerr << "Error: Bad end date: " << optarg << std::endl;
return 1;
@@ -609,8 +567,7 @@ int main(int argc, char * argv[])
break;
case 'c':
- constraints.end_date = std::time(NULL);
- constraints.have_ending = true;
+ constraints.end_date = std::time(NULL);
break;
case 'd':
@@ -635,7 +592,7 @@ int main(int argc, char * argv[])
// Customizing output
case 'F':
- format.format_string = optarg;
+ format_string = optarg;
break;
case 'M':
@@ -663,7 +620,7 @@ int main(int argc, char * argv[])
break;
case 'l':
- constraints.predicate = ledger::parse_expr(optarg, book);
+ constraints.predicate = ledger::parse_expr(optarg);
break;
// Commodity reporting
@@ -680,61 +637,61 @@ int main(int argc, char * argv[])
break;
case 't':
- value_style = optarg;
+ value_expr = optarg;
break;
case 'T':
- total_style = optarg;
+ total_expr = optarg;
break;
case 'O':
- value_style = "a";
- total_style = "T";
+ value_expr = "a";
+ total_expr = "T";
break;
case 'B':
- value_style = "c";
- total_style = "C";
+ value_expr = "c";
+ total_expr = "C";
break;
case 'V':
ledger::show_commodities_revalued = true;
- value_style = "v";
- total_style = "V";
+ value_expr = "v";
+ total_expr = "V";
break;
case 'G':
ledger::show_commodities_revalued =
ledger::show_commodities_revalued_only = true;
- value_style = "c";
- total_style = "G";
+ value_expr = "c";
+ total_expr = "G";
break;
case 'A':
- value_style = "a";
- total_style = "MT";
+ value_expr = "a";
+ total_expr = "MT";
break;
case 'D':
- value_style = "a";
- total_style = "DMT";
+ value_expr = "a";
+ total_expr = "DMT";
break;
case 'Z':
- value_style = "a";
- total_style = "MDMT";
+ value_expr = "a";
+ total_expr = "MDMT";
break;
case 'W':
- value_style = "a";
- total_style = "MD(MT*(d-b/e-b))";
+ value_expr = "a";
+ total_expr = "MD(MT*(d-b/e-b))";
break;
case 'X':
- value_style = "a";
- total_style = "a+MD(MT*(d-b/e-b))";
+ value_expr = "a";
+ total_expr = "a+MD(MT*(d-b/e-b))";
break;
}
}
@@ -755,14 +712,14 @@ int main(int argc, char * argv[])
if (files.empty()) {
if (char * p = std::getenv("LEDGER"))
for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":"))
- entry_count += parse_ledger_file(p, book);
+ entry_count += parse_ledger_file(p, journal);
} else {
for (std::list<std::string>::iterator i = files.begin();
i != files.end(); i++) {
char buf[4096];
char * p = buf;
std::strcpy(p, (*i).c_str());
- entry_count += parse_ledger_file(p, book);
+ entry_count += parse_ledger_file(p, journal);
}
}
@@ -772,8 +729,8 @@ int main(int argc, char * argv[])
if (! ledger::price_db.empty()) {
const char * path = ledger::price_db.c_str();
std::ifstream db(path);
- book->sources.push_back(path);
- entry_count += ledger::parse_textual_ledger(db, book, book->master);
+ journal->sources.push_back(path);
+ entry_count += ledger::parse_textual_ledger(db, journal, journal->master);
}
}
catch (ledger::error& err) {
@@ -794,7 +751,7 @@ int main(int argc, char * argv[])
const std::string command = argv[index++];
if (command == "entry")
- return add_new_entry(index, argc, argv, book) ? 0 : 1;
+ return add_new_entry(index, argc, argv, journal) ? 0 : 1;
// Interpret the remaining arguments as regular expressions, used
// for refining report results.
@@ -814,77 +771,100 @@ int main(int argc, char * argv[])
// and total style strings
if (! sort_order.empty())
- constraints.sort_order = ledger::parse_expr(sort_order, book);
- format.value_style = ledger::parse_expr(value_style, book);
- format.total_style = ledger::parse_expr(total_style, book);
+ constraints.sort_order = ledger::parse_expr(sort_order);
+
+ // Setup the meaning of %t and %T encountered in format strings
+
+ ledger::format_t::value_expr = ledger::parse_expr(value_expr);
+ ledger::format_t::total_expr = ledger::parse_expr(total_expr);
// Now handle the command that was identified above.
if (command == "print") {
#if 0
- ledger::item_t * top
- = ledger::walk_entries(book->entries.begin(), book->entries.end(),
- constraints, format);
- ledger::entry_report(std::cout, top, format);
+ if (ledger::item_t * top
+ = ledger::walk_entries(journal->entries.begin(),
+ journal->entries.end(),
+ constraints)) {
+ ledger::format_t * format = new ledger::format_t(format_string);
+ ledger::entry_report(std::cout, top, *format);
#ifdef DEBUG
- delete top;
+ delete top;
+ delete format;
#endif
+ }
#endif
}
else if (command == "equity") {
#if 0
- ledger::item_t * top
- = ledger::walk_accounts(book->master, constraints);
-
- ledger::entry_report(std::cout, top, constraints, format);
-
+ if (ledger::item_t * top
+ = ledger::walk_accounts(journal->master, constraints)) {
+ ledger::format_t * format = new ledger::format_t(format_string);
+ ledger::entry_report(std::cout, top, constraints, *format);
#ifdef DEBUG
- delete top;
+ delete top;
+ delete format;
#endif
+ }
#endif
}
else if (constraints.period == ledger::PERIOD_NONE &&
! constraints.sort_order && ! constraints.show_related &&
(command == "balance" || command == "bal")) {
- if (format.format_string.empty())
- format.format_string = ledger::bal_fmt;
-
if (ledger::item_t * top
- = ledger::walk_accounts(book->master, constraints)) {
- ledger::balance_report(std::cout, top, constraints, format);
+ = ledger::walk_accounts(journal->master, constraints)) {
+ ledger::format_t * format
+ = new ledger::format_t(format_string.empty() ?
+ ledger::bal_fmt : format_string);
+ ledger::balance_report(std::cout, top, constraints, *format);
#ifdef DEBUG
+ delete format;
delete top;
#endif
}
}
else if (command == "balance" || command == "bal") {
- if (format.format_string.empty())
- format.format_string = ledger::bal_fmt;
-
if (ledger::item_t * list
- = ledger::walk_entries(book->entries.begin(),
- book->entries.end(), constraints))
+ = ledger::walk_entries(journal->entries.begin(),
+ journal->entries.end(), constraints))
if (ledger::item_t * top
- = ledger::walk_items(list, book->master, constraints)) {
- ledger::balance_report(std::cout, top, constraints, format);
+ = ledger::walk_items(list, journal->master, constraints)) {
+ ledger::format_t * format
+ = new ledger::format_t(format_string.empty() ?
+ ledger::bal_fmt : format_string);
+ ledger::balance_report(std::cout, top, constraints, *format);
#ifdef DEBUG
+ delete format;
delete top;
delete list;
#endif
}
}
else if (command == "register" || command == "reg") {
- if (format.format_string.empty())
- format.format_string = ledger::reg_fmt;
-
if (constraints.show_related)
constraints.show_inverted = true;
if (ledger::item_t * top
- = ledger::walk_entries(book->entries.begin(),
- book->entries.end(), constraints)) {
- ledger::register_report(std::cout, top, constraints, format);
+ = ledger::walk_entries(journal->entries.begin(),
+ journal->entries.end(), constraints)) {
+ std::string first_line_format;
+ std::string next_lines_format;
+
+ const char * f = (format_string.empty() ?
+ ledger::reg_fmt.c_str() : format_string.c_str());
+ 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 = format_string;
+ next_lines_format = format_string;
+ }
+
+ ledger::format_t * format = new ledger::format_t(first_line_format);
+ ledger::format_t * nformat = new ledger::format_t(next_lines_format);
+ ledger::register_report(std::cout, top, constraints, *format, *nformat);
#ifdef DEBUG
+ delete format;
delete top;
#endif
}
@@ -901,11 +881,24 @@ int main(int argc, char * argv[])
if (const char * p = std::getenv("LEDGER_CACHE")) {
std::ofstream outstr(p);
assert(std::getenv("LEDGER"));
- ledger::write_binary_ledger(outstr, book, std::getenv("LEDGER"));
+ ledger::write_binary_ledger(outstr, journal, std::getenv("LEDGER"));
}
#ifdef DEBUG
- delete book;
+ delete journal;
+
+ if (ledger::format_t::value_expr)
+ delete ledger::format_t::value_expr;
+ if (ledger::format_t::total_expr)
+ delete ledger::format_t::total_expr;
+
+ // jww (2004-07-30): This should be moved into some kind of
+ // "ledger::shutdown" function.
+ for (ledger::commodities_map::iterator i
+ = ledger::commodity_t::commodities.begin();
+ i != ledger::commodity_t::commodities.end();
+ i++)
+ delete (*i).second;
#endif
return 0;
diff --git a/textual.cc b/textual.cc
index 95506e99..e71a3b7a 100644
--- a/textual.cc
+++ b/textual.cc
@@ -186,8 +186,8 @@ inline char peek_next_nonws(std::istream& in)
return c;
}
-transaction_t * parse_transaction_text(char * line, ledger_t * ledger,
- account_t * account, entry_t * entry)
+transaction_t * parse_transaction_text(char * line, account_t * account,
+ entry_t * entry)
{
// The account will be determined later...
@@ -208,10 +208,10 @@ transaction_t * parse_transaction_text(char * line, ledger_t * ledger,
char * price_str = std::strchr(cost_str, '@');
if (price_str) {
*price_str++ = '\0';
- xact->cost.parse(price_str, ledger);
+ xact->cost.parse(price_str);
}
- xact->amount.parse(cost_str, ledger);
+ xact->amount.parse(cost_str);
if (price_str)
xact->cost *= xact->amount;
@@ -233,21 +233,21 @@ transaction_t * parse_transaction_text(char * line, ledger_t * ledger,
xact->account = account->find_account(p);
if (! xact->amount.commodity)
- xact->amount.commodity = ledger->find_commodity("", true);
+ xact->amount.commodity = commodity_t::find_commodity("", true);
if (! xact->cost.commodity)
- xact->cost.commodity = ledger->find_commodity("", true);
+ xact->cost.commodity = commodity_t::find_commodity("", true);
return xact;
}
-transaction_t * parse_transaction(std::istream& in, ledger_t * ledger,
- account_t * account, entry_t * entry)
+transaction_t * parse_transaction(std::istream& in, account_t * account,
+ entry_t * entry)
{
static char line[MAX_LINE + 1];
in.getline(line, MAX_LINE);
linenum++;
- return parse_transaction_text(line, ledger, account, entry);
+ return parse_transaction_text(line, account, entry);
}
class automated_transaction_t
@@ -338,8 +338,7 @@ public:
}
};
-void parse_automated_transactions(std::istream& in, ledger_t * ledger,
- account_t * account,
+void parse_automated_transactions(std::istream& in, account_t * account,
automated_transactions_t& auto_xacts)
{
static char line[MAX_LINE + 1];
@@ -359,7 +358,7 @@ void parse_automated_transactions(std::istream& in, ledger_t * ledger,
transactions_list xacts;
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
- if (transaction_t * xact = parse_transaction(in, ledger, account, NULL)) {
+ if (transaction_t * xact = parse_transaction(in, account, NULL)) {
if (! xact->amount)
throw parse_error(path, linenum,
"All automated transactions must have a value");
@@ -446,8 +445,7 @@ bool finalize_entry(entry_t * entry)
return ! balance;
}
-entry_t * parse_entry(std::istream& in, ledger_t * ledger,
- account_t * master)
+entry_t * parse_entry(std::istream& in, account_t * master)
{
entry_t * curr = new entry_t;
@@ -486,7 +484,7 @@ entry_t * parse_entry(std::istream& in, ledger_t * ledger,
// Parse all of the transactions associated with this entry
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t'))
- if (transaction_t * xact = parse_transaction(in, ledger, master, curr))
+ if (transaction_t * xact = parse_transaction(in, master, curr))
curr->add_transaction(xact);
// If there were no transactions, throw away the entry
@@ -504,7 +502,7 @@ entry_t * parse_entry(std::istream& in, ledger_t * ledger,
// Textual ledger parser
//
-unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
+unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
account_t * master)
{
static char line[MAX_LINE + 1];
@@ -517,11 +515,11 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
automated_transactions_t auto_xacts;
if (! master)
- master = ledger->master;
+ master = journal->master;
account_stack.push_front(master);
- path = ledger->sources.back();
+ path = journal->sources.back();
linenum = 1;
while (! in.eof()) {
@@ -602,14 +600,14 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
char buf[32];
std::sprintf(buf, "%fh", diff);
amount_t amt;
- amt.parse(buf, ledger);
+ amt.parse(buf);
time_commodity = amt.commodity;
transaction_t * xact = new transaction_t(curr, last_account, amt, amt,
TRANSACTION_VIRTUAL);
curr->add_transaction(xact);
- if (! finalize_entry(curr) || ! ledger->add_entry(curr))
+ if (! finalize_entry(curr) || ! journal->add_entry(curr))
assert(0);
count++;
@@ -650,9 +648,9 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
parse_commodity(in, symbol);
in >> line; // the price
- price.parse(line, ledger);
+ price.parse(line);
- commodity_t * commodity = ledger->find_commodity(symbol, true);
+ commodity_t * commodity = commodity_t::find_commodity(symbol, true);
commodity->add_price(date, price);
break;
}
@@ -663,7 +661,7 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
in >> c;
parse_commodity(in, symbol);
- commodity_t * commodity = ledger->find_commodity(line, true);
+ commodity_t * commodity = commodity_t::find_commodity(line, true);
commodity->flags |= (COMMODITY_STYLE_CONSULTED |
COMMODITY_STYLE_NOMARKET);
break;
@@ -677,9 +675,9 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
parse_commodity(in, symbol);
in >> line; // the price
- price.parse(line, ledger);
+ price.parse(line);
- commodity_t * commodity = ledger->find_commodity(symbol, true);
+ commodity_t * commodity = commodity_t::find_commodity(symbol, true);
commodity->set_conversion(price);
break;
}
@@ -700,8 +698,7 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
break;
case '=': // automated transactions
- parse_automated_transactions(in, ledger, account_stack.front(),
- auto_xacts);
+ parse_automated_transactions(in, account_stack.front(), auto_xacts);
break;
case '@': { // account specific
@@ -729,12 +726,12 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
char * p = skip_ws(line);
std::ifstream stream(p);
- ledger->sources.push_back(p);
+ journal->sources.push_back(p);
unsigned int curr_linenum = linenum;
std::string curr_path = path;
- count += parse_textual_ledger(stream, ledger, account_stack.front());
+ count += parse_textual_ledger(stream, journal, account_stack.front());
linenum = curr_linenum;
path = curr_path;
@@ -743,11 +740,11 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
default: {
unsigned int first_line = linenum;
- if (entry_t * entry = parse_entry(in, ledger, account_stack.front())) {
+ if (entry_t * entry = parse_entry(in, account_stack.front())) {
if (! auto_xacts.automated_transactions.empty())
auto_xacts.extend_entry(entry);
- if (ledger->add_entry(entry))
+ if (journal->add_entry(entry))
count++;
else
throw parse_error(path, first_line, "Entry does not balance");
@@ -876,11 +873,11 @@ void print_textual_entry(std::ostream& out, entry_t * entry, bool shortcut)
out << std::endl;
}
-void print_textual_ledger(std::ostream& out, ledger_t * ledger,
+void print_textual_ledger(std::ostream& out, ledger_t * journal,
bool shortcut)
{
- for (entries_list::const_iterator i = ledger->entries.begin();
- i != ledger->entries.end();
+ for (entries_list::const_iterator i = journal->entries.begin();
+ i != journal->entries.end();
i++)
print_textual_entry(out, *i, shortcut);
}
@@ -891,12 +888,12 @@ void print_textual_ledger(std::ostream& out, ledger_t * ledger,
int main(int argc, char *argv[])
{
- book.sources.push_back(argv[1]);
+ journal.sources.push_back(argv[1]);
std::ifstream stream(argv[1]);
- ledger::ledger_t book;
- int count = parse_textual_ledger(stream, &book, book.master);
+ ledger::ledger_t journal;
+ int count = parse_textual_ledger(stream, &journal, journal.master);
std::cout << "Read " << count << " entries." << std::endl;
- print_textual_ledger(std::cout, &book, true);
+ print_textual_ledger(std::cout, &journal, true);
}
#endif // PARSE_TEST