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.
This commit is contained in:
John Wiegley 2004-07-30 21:57:02 -04:00
parent 5087a60dee
commit 94e76ae87e
16 changed files with 651 additions and 575 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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) {

View file

@ -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;

View file

@ -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;

228
expr.cc
View file

@ -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 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 F_ARITH_MEAN:
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;

106
expr.h
View file

@ -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;

277
format.cc
View file

@ -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
{
std::string result;
node_t * format_t::value_expr = NULL;
node_t * format_t::total_expr = NULL;
for (const char * p = format_string.c_str(); *p; p++) {
element_t * format_t::parse_elements(const std::string& fmt)
{
element_t * result = NULL;
element_t * current = NULL;
std::string str;
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 (*p != ']')
throw format_error("Missing ']'");
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 << " ";
}
current->type = element_t::DATE_STRING;
current->chars = num;
break;
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;
}
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 << " ";
}
break;
}
case 'p':
out << (strict_width == 0 ?
item->payee : truncated(item->payee, strict_width));
break;
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;
case 'N':
if (item->account)
out << (strict_width == 0 ?
item->account->fullname() :
truncated(item->account->fullname(), strict_width));
else
out << " ";
break;
case 't':
if (value_style) {
balance_t value = compute_value(item);
value.write(out, width, strict_width > 0 ? strict_width : 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 '_': {
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 (width > 0 || strict_width > 0)
out.width(width > strict_width ? width : strict_width);
out << " ";
}
break;
}
}
result += out.str();
} else {
result += *p;
str += *p;
}
}
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;
}
return result;
}
void format_t::format_elements(std::ostream& out, const item_t * item,
const item_t * displayed_parent) const
{
std::string result;
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 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 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;
}
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;
}
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;
}
default:
assert(0);
break;
}
}
}
} // namespace ledger

View file

@ -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 element_t
{
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;
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
{
std::string format_string;
node_t * value_style;
node_t * total_style;
element_t * elements;
format_t() {
value_style = NULL;
total_style = NULL;
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

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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

275
main.cc
View file

@ -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;

View file

@ -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