summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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