diff options
author | John Wiegley <johnw@newartisans.com> | 2004-07-30 21:57:02 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2004-07-30 21:57:02 -0400 |
commit | 94e76ae87e883291d13320738fe165c7a2a2415b (patch) | |
tree | b90eff2ee3737ecdfea96dbee52ecd239fcb2578 | |
parent | 5087a60deef7c618a07562511e9a1fbf2414776c (diff) | |
download | fork-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.cc | 45 | ||||
-rw-r--r-- | binary.cc | 10 | ||||
-rw-r--r-- | binary.h | 2 | ||||
-rw-r--r-- | constraint.cc | 4 | ||||
-rw-r--r-- | constraint.h | 10 | ||||
-rw-r--r-- | error.h | 7 | ||||
-rw-r--r-- | expr.cc | 230 | ||||
-rw-r--r-- | expr.h | 106 | ||||
-rw-r--r-- | format.cc | 259 | ||||
-rw-r--r-- | format.h | 85 | ||||
-rw-r--r-- | gnucash.cc | 10 | ||||
-rw-r--r-- | item.cc | 4 | ||||
-rw-r--r-- | ledger.cc | 33 | ||||
-rw-r--r-- | ledger.h | 57 | ||||
-rw-r--r-- | main.cc | 275 | ||||
-rw-r--r-- | textual.cc | 73 |
16 files changed, 643 insertions, 567 deletions
@@ -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; @@ -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); @@ -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; @@ -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; @@ -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; @@ -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; @@ -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 @@ -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 @@ -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; @@ -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) @@ -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 @@ -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 @@ -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; @@ -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 |