Brought in the final round of 3.0 code, although it does not compile yet:
report, session, parts of xpath, main, journal, option.
This commit is contained in:
parent
59f6ffb863
commit
52fc9f2e44
22 changed files with 2730 additions and 2674 deletions
|
|
@ -51,6 +51,7 @@ libledger_la_SOURCES = \
|
|||
qif.cc \
|
||||
reconcile.cc \
|
||||
report.cc \
|
||||
session.cc \
|
||||
startup.cc \
|
||||
textual.cc \
|
||||
valexpr.cc \
|
||||
|
|
|
|||
190
binary.cc
190
binary.cc
|
|
@ -179,6 +179,28 @@ void read_string(const char *& data, string * str)
|
|||
read_guard(data, 0x3002);
|
||||
}
|
||||
|
||||
void read_string(std::istream& in, optional<string>& str)
|
||||
{
|
||||
if (read_bool(in)) {
|
||||
string temp;
|
||||
read_string(in, temp);
|
||||
str = temp;
|
||||
} else {
|
||||
str = none;
|
||||
}
|
||||
}
|
||||
|
||||
void read_string(const char *& data, optional<string>& str)
|
||||
{
|
||||
if (read_bool(data)) {
|
||||
string temp;
|
||||
read_string(data, temp);
|
||||
str = temp;
|
||||
} else {
|
||||
str = none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void write_bool(std::ostream& out, bool num)
|
||||
{
|
||||
|
|
@ -207,6 +229,16 @@ void write_string(std::ostream& out, const string& str)
|
|||
write_guard(out, 0x3002);
|
||||
}
|
||||
|
||||
void write_string(std::ostream& out, const optional<string>& str)
|
||||
{
|
||||
if (str) {
|
||||
write_bool(out, true);
|
||||
write_string(out, *str);
|
||||
} else {
|
||||
write_bool(out, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline void read_amount(const char *& data, amount_t& amt)
|
||||
{
|
||||
commodity_t::ident_t ident;
|
||||
|
|
@ -260,50 +292,53 @@ inline void read_mask(const char *& data, mask_t *& mask)
|
|||
mask->exclude = exclude;
|
||||
}
|
||||
|
||||
inline void read_value_expr(const char *& data, value_expr_t *& expr)
|
||||
inline expr::ptr_op_t read_value_expr(const char *& data)
|
||||
{
|
||||
if (! read_bool(data)) {
|
||||
expr = NULL;
|
||||
return;
|
||||
}
|
||||
if (! read_bool(data))
|
||||
return expr::ptr_op_t();
|
||||
|
||||
value_expr_t::kind_t kind;
|
||||
expr::op_t::kind_t kind;
|
||||
read_number(data, kind);
|
||||
|
||||
expr = new value_expr_t(kind);
|
||||
expr::ptr_op_t expr = new expr::op_t(kind);
|
||||
|
||||
if (kind > value_expr_t::TERMINALS) {
|
||||
read_value_expr(data, expr->left);
|
||||
if (expr->left) expr->left->acquire();
|
||||
}
|
||||
if (kind > expr::op_t::TERMINALS)
|
||||
expr->set_left(read_value_expr(data));
|
||||
|
||||
switch (expr->kind) {
|
||||
case value_expr_t::O_ARG:
|
||||
case value_expr_t::INDEX:
|
||||
read_long(data, expr->arg_index);
|
||||
case expr::op_t::O_ARG:
|
||||
case expr::op_t::INDEX: {
|
||||
long temp;
|
||||
read_long(data, temp);
|
||||
expr->set_long(temp);
|
||||
break;
|
||||
case value_expr_t::CONSTANT:
|
||||
expr->value = new value_t;
|
||||
read_value(data, *expr->value);
|
||||
}
|
||||
case expr::op_t::VALUE: {
|
||||
value_t temp;
|
||||
read_value(data, temp);
|
||||
expr->set_value(temp);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_expr_t::F_CODE_MASK:
|
||||
case value_expr_t::F_PAYEE_MASK:
|
||||
case value_expr_t::F_NOTE_MASK:
|
||||
case value_expr_t::F_ACCOUNT_MASK:
|
||||
case value_expr_t::F_SHORT_ACCOUNT_MASK:
|
||||
case value_expr_t::F_COMMODITY_MASK:
|
||||
case expr::op_t::F_CODE_MASK:
|
||||
case expr::op_t::F_PAYEE_MASK:
|
||||
case expr::op_t::F_NOTE_MASK:
|
||||
case expr::op_t::F_ACCOUNT_MASK:
|
||||
case expr::op_t::F_SHORT_ACCOUNT_MASK:
|
||||
case expr::op_t::F_COMMODITY_MASK:
|
||||
#if 0
|
||||
if (read_bool(data))
|
||||
read_mask(data, expr->mask);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
if (kind > value_expr_t::TERMINALS) {
|
||||
read_value_expr(data, expr->right);
|
||||
if (expr->right) expr->right->acquire();
|
||||
}
|
||||
if (kind > expr::op_t::TERMINALS)
|
||||
expr->set_right(read_value_expr(data));
|
||||
break;
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -322,25 +357,29 @@ inline void read_transaction(const char *& data, transaction_t * xact)
|
|||
read_string(data, xact->amount_expr.expr);
|
||||
}
|
||||
else {
|
||||
value_expr_t * ptr = NULL;
|
||||
read_value_expr(data, ptr);
|
||||
assert(ptr);
|
||||
expr::ptr_op_t ptr = read_value_expr(data);
|
||||
assert(ptr.get());
|
||||
xact->amount_expr.reset(ptr);
|
||||
read_string(data, xact->amount_expr.expr);
|
||||
}
|
||||
|
||||
if (read_bool(data)) {
|
||||
xact->cost = new amount_t;
|
||||
xact->cost = amount_t();
|
||||
read_amount(data, *xact->cost);
|
||||
read_string(data, xact->cost_expr);
|
||||
|
||||
expr::ptr_op_t ptr = read_value_expr(data);
|
||||
assert(ptr.get());
|
||||
value_expr expr;
|
||||
expr.reset(ptr);
|
||||
xact->cost_expr = expr;
|
||||
} else {
|
||||
xact->cost = NULL;
|
||||
xact->cost = none;
|
||||
}
|
||||
|
||||
read_number(data, xact->state);
|
||||
read_number(data, xact->flags);
|
||||
xact->flags |= TRANSACTION_BULK_ALLOC;
|
||||
read_string(data, &xact->note);
|
||||
xact->set_flags(read_number<transaction_t::flags_t>(data));
|
||||
xact->add_flags(TRANSACTION_BULK_ALLOC);
|
||||
read_string(data, xact->note);
|
||||
|
||||
xact->beg_pos = read_long<unsigned long>(data);
|
||||
read_long(data, xact->beg_line);
|
||||
|
|
@ -350,7 +389,7 @@ inline void read_transaction(const char *& data, transaction_t * xact)
|
|||
xact->data = NULL;
|
||||
|
||||
if (xact->amount_expr)
|
||||
compute_amount(xact->amount_expr, xact->amount, xact);
|
||||
expr::compute_amount(xact->amount_expr.get(), xact->amount, xact);
|
||||
}
|
||||
|
||||
inline void read_entry_base(const char *& data, entry_base_t * entry,
|
||||
|
|
@ -369,7 +408,7 @@ inline void read_entry_base(const char *& data, entry_base_t * entry,
|
|||
i++) {
|
||||
new(xact_pool) transaction_t;
|
||||
read_transaction(data, xact_pool);
|
||||
if (ignore_calculated && xact_pool->flags & TRANSACTION_CALCULATED)
|
||||
if (ignore_calculated && xact_pool->has_flags(TRANSACTION_CALCULATED))
|
||||
finalize = true;
|
||||
entry->add_transaction(xact_pool++);
|
||||
}
|
||||
|
|
@ -381,8 +420,8 @@ inline void read_entry(const char *& data, entry_t * entry,
|
|||
read_entry_base(data, entry, xact_pool, finalize);
|
||||
read_number(data, entry->_date);
|
||||
read_number(data, entry->_date_eff);
|
||||
read_string(data, &entry->code);
|
||||
read_string(data, &entry->payee);
|
||||
read_string(data, entry->code);
|
||||
read_string(data, entry->payee);
|
||||
}
|
||||
|
||||
inline void read_auto_entry(const char *& data, auto_entry_t * entry,
|
||||
|
|
@ -390,10 +429,7 @@ inline void read_auto_entry(const char *& data, auto_entry_t * entry,
|
|||
{
|
||||
bool ignore;
|
||||
read_entry_base(data, entry, xact_pool, ignore);
|
||||
value_expr_t * expr;
|
||||
read_value_expr(data, expr);
|
||||
// the item_predicate constructor will acquire the reference
|
||||
entry->predicate = new item_predicate<transaction_t>(expr);
|
||||
entry->predicate = item_predicate<transaction_t>(read_value_expr(data));
|
||||
}
|
||||
|
||||
inline void read_period_entry(const char *& data, period_entry_t * entry,
|
||||
|
|
@ -411,8 +447,7 @@ inline commodity_t::base_t * read_commodity_base(const char *& data)
|
|||
|
||||
read_string(data, str);
|
||||
|
||||
commodity_t::base_t * commodity = new commodity_t::base_t(str);
|
||||
*base_commodities_next++ = commodity;
|
||||
std::auto_ptr<commodity_t::base_t> commodity(new commodity_t::base_t(str));
|
||||
|
||||
read_string(data, str);
|
||||
if (! str.empty())
|
||||
|
|
@ -427,7 +462,7 @@ inline commodity_t::base_t * read_commodity_base(const char *& data)
|
|||
read_number(data, flags);
|
||||
commodity->set_flags(flags);
|
||||
|
||||
return commodity;
|
||||
return *base_commodities_next++ = commodity.release();
|
||||
}
|
||||
|
||||
inline void read_commodity_base_extra(const char *& data,
|
||||
|
|
@ -596,8 +631,13 @@ unsigned int read_journal(std::istream& in,
|
|||
// Make sure that the cache uses the same price database,
|
||||
// otherwise it means that LEDGER_PRICE_DB has been changed, and
|
||||
// we should ignore this cache file.
|
||||
if (read_string(in) != journal->price_db)
|
||||
return 0;
|
||||
if (read_bool(in)) {
|
||||
string pathname;
|
||||
read_string(in, pathname);
|
||||
if (! journal->price_db ||
|
||||
journal->price_db->string() != std::string(pathname))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Read all of the data in at once, so that we're just dealing with
|
||||
|
|
@ -812,44 +852,47 @@ void write_mask(std::ostream& out, mask_t * mask)
|
|||
write_string(out, mask->expr.str());
|
||||
}
|
||||
|
||||
void write_value_expr(std::ostream& out, const value_expr_t * expr)
|
||||
void write_value_expr(std::ostream& out, const expr::ptr_op_t expr)
|
||||
{
|
||||
if (! expr) {
|
||||
write_bool(out, false);
|
||||
return;
|
||||
}
|
||||
|
||||
write_bool(out, true);
|
||||
write_number(out, expr->kind);
|
||||
|
||||
if (expr->kind > value_expr_t::TERMINALS)
|
||||
write_value_expr(out, expr->left);
|
||||
if (expr->kind > expr::op_t::TERMINALS)
|
||||
write_value_expr(out, expr->left());
|
||||
|
||||
switch (expr->kind) {
|
||||
case value_expr_t::O_ARG:
|
||||
case value_expr_t::INDEX:
|
||||
write_long(out, expr->arg_index);
|
||||
case expr::op_t::O_ARG:
|
||||
case expr::op_t::INDEX:
|
||||
write_long(out, expr->as_long());
|
||||
break;
|
||||
case value_expr_t::CONSTANT:
|
||||
write_value(out, *expr->value);
|
||||
case expr::op_t::VALUE:
|
||||
write_value(out, expr->as_value());
|
||||
break;
|
||||
|
||||
case value_expr_t::F_CODE_MASK:
|
||||
case value_expr_t::F_PAYEE_MASK:
|
||||
case value_expr_t::F_NOTE_MASK:
|
||||
case value_expr_t::F_ACCOUNT_MASK:
|
||||
case value_expr_t::F_SHORT_ACCOUNT_MASK:
|
||||
case value_expr_t::F_COMMODITY_MASK:
|
||||
case expr::op_t::F_CODE_MASK:
|
||||
case expr::op_t::F_PAYEE_MASK:
|
||||
case expr::op_t::F_NOTE_MASK:
|
||||
case expr::op_t::F_ACCOUNT_MASK:
|
||||
case expr::op_t::F_SHORT_ACCOUNT_MASK:
|
||||
case expr::op_t::F_COMMODITY_MASK:
|
||||
#if 0
|
||||
if (expr->mask) {
|
||||
write_bool(out, true);
|
||||
write_mask(out, expr->mask);
|
||||
} else {
|
||||
write_bool(out, false);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
if (expr->kind > value_expr_t::TERMINALS)
|
||||
write_value_expr(out, expr->right);
|
||||
if (expr->kind > expr::op_t::TERMINALS)
|
||||
write_value_expr(out, expr->right());
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -862,7 +905,7 @@ void write_transaction(std::ostream& out, transaction_t * xact,
|
|||
write_number(out, xact->_date_eff);
|
||||
write_long(out, xact->account->ident);
|
||||
|
||||
if (ignore_calculated && xact->flags & TRANSACTION_CALCULATED) {
|
||||
if (ignore_calculated && xact->has_flags(TRANSACTION_CALCULATED)) {
|
||||
write_number<unsigned char>(out, 0);
|
||||
write_amount(out, amount_t());
|
||||
}
|
||||
|
|
@ -882,16 +925,16 @@ void write_transaction(std::ostream& out, transaction_t * xact,
|
|||
}
|
||||
|
||||
if (xact->cost &&
|
||||
(! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
|
||||
(! (ignore_calculated && xact->has_flags(TRANSACTION_CALCULATED)))) {
|
||||
write_bool(out, true);
|
||||
write_amount(out, *xact->cost);
|
||||
write_string(out, xact->cost_expr);
|
||||
write_string(out, xact->cost_expr->expr);
|
||||
} else {
|
||||
write_bool(out, false);
|
||||
}
|
||||
|
||||
write_number(out, xact->state);
|
||||
write_number(out, xact->flags);
|
||||
write_number(out, xact->flags());
|
||||
write_string(out, xact->note);
|
||||
|
||||
write_long(out, xact->beg_pos);
|
||||
|
|
@ -938,7 +981,7 @@ void write_entry(std::ostream& out, entry_t * entry)
|
|||
void write_auto_entry(std::ostream& out, auto_entry_t * entry)
|
||||
{
|
||||
write_entry_base(out, entry);
|
||||
write_value_expr(out, entry->predicate->predicate);
|
||||
write_value_expr(out, entry->predicate.predicate.get());
|
||||
}
|
||||
|
||||
void write_period_entry(std::ostream& out, period_entry_t * entry)
|
||||
|
|
@ -1088,7 +1131,12 @@ void write_journal(std::ostream& out, journal_t * journal)
|
|||
|
||||
// Write out the price database that relates to this data file, so
|
||||
// that if it ever changes the cache can be invalidated.
|
||||
write_string(out, journal->price_db.string());
|
||||
if (journal->price_db) {
|
||||
write_bool(out, true);
|
||||
write_string(out, journal->price_db->string());
|
||||
} else {
|
||||
write_bool(out, false);
|
||||
}
|
||||
}
|
||||
|
||||
ostream_pos_type data_val = out.tellp();
|
||||
|
|
|
|||
4
binary.h
4
binary.h
|
|
@ -205,6 +205,9 @@ inline string read_string(const char *& data) {
|
|||
return temp;
|
||||
}
|
||||
|
||||
void read_string(std::istream& in, optional<string>& str);
|
||||
void read_string(const char *& data, optional<string>& str);
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline void write_number_nocheck(std::ostream& out, T num) {
|
||||
|
|
@ -262,6 +265,7 @@ void write_long(std::ostream& out, T num)
|
|||
}
|
||||
|
||||
void write_string(std::ostream& out, const string& str);
|
||||
void write_string(std::ostream& out, const optional<string>& str);
|
||||
|
||||
template <typename T>
|
||||
inline void write_object(std::ostream& out, const T& journal) {
|
||||
|
|
|
|||
3
csv.cc
3
csv.cc
|
|
@ -87,7 +87,8 @@ void format_csv_transactions::operator()(transaction_t& xact)
|
|||
}
|
||||
out << ',';
|
||||
|
||||
write_escaped_string(out, xact.entry->code);
|
||||
if (xact.entry->code)
|
||||
write_escaped_string(out, *xact.entry->code);
|
||||
out << ',';
|
||||
|
||||
{
|
||||
|
|
|
|||
10
emacs.cc
10
emacs.cc
|
|
@ -20,10 +20,10 @@ void format_emacs_transactions::write_entry(entry_t& entry)
|
|||
|
||||
out << "(" << (date / 65536) << " " << (date % 65536) << " 0) ";
|
||||
|
||||
if (entry.code.empty())
|
||||
if (! entry.code)
|
||||
out << "nil ";
|
||||
else
|
||||
out << "\"" << entry.code << "\" ";
|
||||
out << "\"" << *entry.code << "\" ";
|
||||
|
||||
if (entry.payee.empty())
|
||||
out << "nil";
|
||||
|
|
@ -67,10 +67,8 @@ void format_emacs_transactions::operator()(transaction_t& xact)
|
|||
|
||||
if (xact.cost)
|
||||
out << " \"" << *xact.cost << "\"";
|
||||
else if (! xact.note.empty())
|
||||
out << " nil";
|
||||
if (! xact.note.empty())
|
||||
out << " \"" << xact.note << "\"";
|
||||
if (xact.note)
|
||||
out << " \"" << *xact.note << "\"";
|
||||
out << ")";
|
||||
|
||||
last_entry = xact.entry;
|
||||
|
|
|
|||
32
format.cc
32
format.cc
|
|
@ -468,8 +468,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
else
|
||||
stream << details.xact->amount.strip_annotations();
|
||||
|
||||
if (! details.xact->cost_expr.empty())
|
||||
stream << details.xact->cost_expr;
|
||||
if (details.xact->cost_expr)
|
||||
stream << details.xact->cost_expr->expr;
|
||||
else
|
||||
stream << " @ " << amount_t(*details.xact->cost /
|
||||
details.xact->amount).unround();
|
||||
|
|
@ -653,9 +653,9 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
|
||||
case element_t::CODE: {
|
||||
string temp;
|
||||
if (details.entry && ! details.entry->code.empty()) {
|
||||
if (details.entry && details.entry->code) {
|
||||
temp += "(";
|
||||
temp += details.entry->code;
|
||||
temp += *details.entry->code;
|
||||
temp += ") ";
|
||||
}
|
||||
out << temp;
|
||||
|
|
@ -670,14 +670,14 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
break;
|
||||
|
||||
case element_t::OPT_NOTE:
|
||||
if (details.xact && ! details.xact->note.empty())
|
||||
if (details.xact && details.xact->note)
|
||||
out << " ; ";
|
||||
// fall through...
|
||||
|
||||
case element_t::NOTE:
|
||||
if (details.xact)
|
||||
out << (elem->max_width == 0 ?
|
||||
details.xact->note : truncate(details.xact->note,
|
||||
details.xact->note : truncate(*details.xact->note,
|
||||
elem->max_width));
|
||||
break;
|
||||
|
||||
|
|
@ -705,11 +705,11 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
|||
details.account->fullname() :
|
||||
partial_account_name(*details.account));
|
||||
|
||||
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
|
||||
if (details.xact && details.xact->has_flags(TRANSACTION_VIRTUAL)) {
|
||||
if (elem->max_width > 2)
|
||||
name = truncate(name, elem->max_width - 2, true);
|
||||
|
||||
if (details.xact->flags & TRANSACTION_BALANCE)
|
||||
if (details.xact->has_flags(TRANSACTION_BALANCE))
|
||||
name = string("[") + name + "]";
|
||||
else
|
||||
name = string("(") + name + ")";
|
||||
|
|
@ -828,7 +828,7 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base,
|
|||
}
|
||||
else if (const auto_entry_t * entry =
|
||||
dynamic_cast<const auto_entry_t *>(&entry_base)) {
|
||||
out << "= " << entry->predicate_string << '\n';
|
||||
out << "= " << entry->predicate.predicate.expr << '\n';
|
||||
print_format = prefix + " %-34A %12o\n";
|
||||
}
|
||||
else if (const period_entry_t * entry =
|
||||
|
|
@ -850,13 +850,13 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base,
|
|||
cleaner);
|
||||
}
|
||||
|
||||
bool disp_subaccounts_p(const account_t& account,
|
||||
const item_predicate<account_t>& disp_pred,
|
||||
const account_t *& to_show)
|
||||
bool disp_subaccounts_p(const account_t& account,
|
||||
const optional<item_predicate<account_t> >& disp_pred,
|
||||
const account_t *& to_show)
|
||||
{
|
||||
bool display = false;
|
||||
unsigned int counted = 0;
|
||||
bool matches = disp_pred(account);
|
||||
bool matches = disp_pred ? (*disp_pred)(account) : true;
|
||||
value_t acct_total;
|
||||
bool computed = false;
|
||||
value_t result;
|
||||
|
|
@ -866,7 +866,7 @@ bool disp_subaccounts_p(const account_t& account,
|
|||
for (accounts_map::const_iterator i = account.accounts.begin();
|
||||
i != account.accounts.end();
|
||||
i++) {
|
||||
if (! disp_pred(*(*i).second))
|
||||
if (disp_pred && ! (*disp_pred)(*(*i).second))
|
||||
continue;
|
||||
|
||||
compute_total(result, details_t(*(*i).second));
|
||||
|
|
@ -887,7 +887,7 @@ bool disp_subaccounts_p(const account_t& account,
|
|||
}
|
||||
|
||||
bool display_account(const account_t& account,
|
||||
const item_predicate<account_t>& disp_pred)
|
||||
const optional<item_predicate<account_t> >& disp_pred)
|
||||
{
|
||||
// Never display an account that has already been displayed.
|
||||
if (account_has_xdata(account) &&
|
||||
|
|
@ -905,7 +905,7 @@ bool display_account(const account_t& account,
|
|||
if (disp_subaccounts_p(account, disp_pred, account_to_show))
|
||||
return true;
|
||||
|
||||
return ! account_to_show && disp_pred(account);
|
||||
return ! account_to_show && (! disp_pred || (*disp_pred)(account));
|
||||
}
|
||||
|
||||
void format_account::operator()(account_t& account)
|
||||
|
|
|
|||
6
format.h
6
format.h
|
|
@ -154,16 +154,16 @@ void print_entry(std::ostream& out, const entry_base_t& entry,
|
|||
const string& prefix = "");
|
||||
|
||||
bool disp_subaccounts_p(const account_t& account,
|
||||
const item_predicate<account_t>& disp_pred,
|
||||
const optional<item_predicate<account_t> >& disp_pred,
|
||||
const account_t *& to_show);
|
||||
|
||||
inline bool disp_subaccounts_p(const account_t& account) {
|
||||
const account_t * temp;
|
||||
return disp_subaccounts_p(account, item_predicate<account_t>(NULL), temp);
|
||||
return disp_subaccounts_p(account, none, temp);
|
||||
}
|
||||
|
||||
bool display_account(const account_t& account,
|
||||
const item_predicate<account_t>& disp_pred);
|
||||
const optional<item_predicate<account_t> >& disp_pred);
|
||||
|
||||
class format_account : public item_handler<account_t>
|
||||
{
|
||||
|
|
|
|||
262
journal.cc
262
journal.cc
|
|
@ -1,9 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "journal.h"
|
||||
#include "utils.h"
|
||||
#include "valexpr.h"
|
||||
#include "mask.h"
|
||||
#include "format.h"
|
||||
#include "acconf.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
|
|
@ -13,22 +42,21 @@ bool transaction_t::use_effective_date = false;
|
|||
|
||||
transaction_t::~transaction_t()
|
||||
{
|
||||
DEBUG("ledger.memory.dtors", "dtor transaction_t");
|
||||
if (cost) delete cost;
|
||||
TRACE_DTOR(transaction_t);
|
||||
}
|
||||
|
||||
datetime_t transaction_t::actual_date() const
|
||||
{
|
||||
if (! is_valid(_date) && entry)
|
||||
if (! _date && entry)
|
||||
return entry->actual_date();
|
||||
return _date;
|
||||
return *_date;
|
||||
}
|
||||
|
||||
datetime_t transaction_t::effective_date() const
|
||||
{
|
||||
if (! is_valid(_date_eff) && entry)
|
||||
if (! _date_eff && entry)
|
||||
return entry->effective_date();
|
||||
return _date_eff;
|
||||
return *_date_eff;
|
||||
}
|
||||
|
||||
bool transaction_t::valid() const
|
||||
|
|
@ -43,15 +71,10 @@ bool transaction_t::valid() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (transactions_list::const_iterator i = entry->transactions.begin();
|
||||
i != entry->transactions.end();
|
||||
i++)
|
||||
if (*i == this) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (! found) {
|
||||
transactions_list::const_iterator i =
|
||||
std::find(entry->transactions.begin(),
|
||||
entry->transactions.end(), this);
|
||||
if (i == entry->transactions.end()) {
|
||||
DEBUG("ledger.validate", "transaction_t: ! found");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -71,7 +94,7 @@ bool transaction_t::valid() const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (flags & ~0x003f) {
|
||||
if (flags() & ~0x003f) {
|
||||
DEBUG("ledger.validate", "transaction_t: flags are bad");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -99,29 +122,30 @@ bool entry_base_t::finalize()
|
|||
// and the per-unit price of unpriced commodities.
|
||||
|
||||
value_t balance;
|
||||
bool no_amounts = true;
|
||||
bool saw_null = false;
|
||||
|
||||
bool no_amounts = true;
|
||||
bool saw_null = false;
|
||||
for (transactions_list::const_iterator x = transactions.begin();
|
||||
x != transactions.end();
|
||||
x++)
|
||||
if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
|
||||
((*x)->flags & TRANSACTION_BALANCE)) {
|
||||
amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount;
|
||||
if (! p->is_null()) {
|
||||
if (! (*x)->has_flags(TRANSACTION_VIRTUAL) ||
|
||||
(*x)->has_flags(TRANSACTION_BALANCE)) {
|
||||
amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
|
||||
if (! p.is_null()) {
|
||||
if (no_amounts) {
|
||||
balance = *p;
|
||||
balance = p;
|
||||
no_amounts = false;
|
||||
} else {
|
||||
balance += *p;
|
||||
balance += p;
|
||||
}
|
||||
|
||||
assert((*x)->amount);
|
||||
if ((*x)->cost && (*x)->amount.commodity().annotated) {
|
||||
annotated_commodity_t&
|
||||
ann_comm(static_cast<annotated_commodity_t&>
|
||||
((*x)->amount.commodity()));
|
||||
if (ann_comm.details.price)
|
||||
balance += ((*ann_comm.details.price) * (*x)->amount -
|
||||
balance += (*ann_comm.details.price * (*x)->amount.number() -
|
||||
*((*x)->cost));
|
||||
}
|
||||
} else {
|
||||
|
|
@ -137,12 +161,12 @@ bool entry_base_t::finalize()
|
|||
// account if one has been set.
|
||||
|
||||
if (journal && journal->basket && transactions.size() == 1) {
|
||||
assert(balance.type() < value_t::BALANCE);
|
||||
assert(balance.is_amount());
|
||||
transaction_t * nxact = new transaction_t(journal->basket);
|
||||
// The amount doesn't need to be set because the code below will
|
||||
// balance this transaction against the other.
|
||||
add_transaction(nxact);
|
||||
nxact->flags |= TRANSACTION_CALCULATED;
|
||||
nxact->add_flags(TRANSACTION_CALCULATED);
|
||||
}
|
||||
|
||||
// If the first transaction of a two-transaction entry is of a
|
||||
|
|
@ -150,30 +174,28 @@ bool entry_base_t::finalize()
|
|||
// determine its price by dividing the unit count into the value of
|
||||
// the balance. This is done for the last eligible commodity.
|
||||
|
||||
if (! saw_null && balance && balance.is_type(value_t::BALANCE) &&
|
||||
balance.as_balance_lval().amounts.size() == 2) {
|
||||
transactions_list::const_iterator x = transactions.begin();
|
||||
commodity_t& this_comm = (*x)->amount.commodity();
|
||||
|
||||
if (! saw_null && balance && balance.is_balance()) {
|
||||
balance_t& bal(balance.as_balance_lval());
|
||||
if (bal.amounts.size() == 2) {
|
||||
transactions_list::const_iterator x = transactions.begin();
|
||||
assert((*x)->amount);
|
||||
commodity_t& this_comm = (*x)->amount.commodity();
|
||||
|
||||
balance_t::amounts_map::const_iterator this_amt =
|
||||
bal.amounts.find(&this_comm);
|
||||
balance_t::amounts_map::const_iterator other_amt =
|
||||
bal.amounts.begin();
|
||||
if (this_amt == other_amt)
|
||||
other_amt++;
|
||||
balance_t::amounts_map::const_iterator this_bal =
|
||||
bal.amounts.find(&this_comm);
|
||||
balance_t::amounts_map::const_iterator other_bal =
|
||||
bal.amounts.begin();
|
||||
if (this_bal == other_bal)
|
||||
other_bal++;
|
||||
|
||||
if (this_amt != bal.amounts.end()) {
|
||||
amount_t per_unit_cost =
|
||||
amount_t((*other_amt).second / (*this_amt).second).unround();
|
||||
amount_t((*other_bal).second / (*this_bal).second.number()).unround();
|
||||
|
||||
for (; x != transactions.end(); x++) {
|
||||
if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL) ||
|
||||
! (*x)->amount || (*x)->amount.commodity() != this_comm)
|
||||
if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
|
||||
(*x)->amount.commodity() != this_comm)
|
||||
continue;
|
||||
|
||||
assert((*x)->amount);
|
||||
balance -= (*x)->amount;
|
||||
|
||||
entry_t * entry = dynamic_cast<entry_t *>(this);
|
||||
|
|
@ -182,10 +204,10 @@ bool entry_base_t::finalize()
|
|||
! (*x)->amount.commodity().annotated)
|
||||
(*x)->amount.annotate_commodity
|
||||
(annotation_t(per_unit_cost.abs(),
|
||||
entry ? optional<datetime_t>(entry->actual_date()) : none,
|
||||
entry ? optional<string>(entry->code) : none));
|
||||
entry ? entry->actual_date() : optional<datetime_t>(),
|
||||
entry ? entry->code : optional<string>()));
|
||||
|
||||
(*x)->cost = new amount_t(- (per_unit_cost * (*x)->amount));
|
||||
(*x)->cost = - (per_unit_cost * (*x)->amount.number());
|
||||
balance += *(*x)->cost;
|
||||
}
|
||||
}
|
||||
|
|
@ -199,13 +221,14 @@ bool entry_base_t::finalize()
|
|||
for (transactions_list::const_iterator x = transactions.begin();
|
||||
x != transactions.end();
|
||||
x++) {
|
||||
if (! (*x)->amount.is_null() ||
|
||||
(((*x)->flags & TRANSACTION_VIRTUAL) &&
|
||||
! ((*x)->flags & TRANSACTION_BALANCE)))
|
||||
if ((*x)->amount ||
|
||||
((*x)->has_flags(TRANSACTION_VIRTUAL) &&
|
||||
! (*x)->has_flags(TRANSACTION_BALANCE)))
|
||||
continue;
|
||||
|
||||
if (! empty_allowed)
|
||||
throw new error("Only one transaction with null amount allowed per entry");
|
||||
throw_(std::logic_error,
|
||||
"Only one transaction with null amount allowed per entry");
|
||||
empty_allowed = false;
|
||||
|
||||
// If one transaction gives no value at all, its value will become
|
||||
|
|
@ -213,25 +236,25 @@ bool entry_base_t::finalize()
|
|||
// commodities are involved, multiple transactions will be
|
||||
// generated to balance them all.
|
||||
|
||||
balance_t * bal = NULL;
|
||||
const balance_t * bal = NULL;
|
||||
switch (balance.type()) {
|
||||
case value_t::BALANCE_PAIR:
|
||||
bal = &(balance.as_balance_pair_lval().quantity());
|
||||
bal = &balance.as_balance_pair_lval().quantity();
|
||||
// fall through...
|
||||
|
||||
case value_t::BALANCE:
|
||||
if (! bal)
|
||||
bal = &(balance.as_balance_lval());
|
||||
bal = &balance.as_balance_lval();
|
||||
|
||||
if (bal->amounts.size() < 2) {
|
||||
balance.cast(value_t::AMOUNT);
|
||||
} else {
|
||||
bool first = true;
|
||||
for (balance_t::amounts_map::const_iterator i = bal->amounts.begin();
|
||||
for (balance_t::amounts_map::const_iterator
|
||||
i = bal->amounts.begin();
|
||||
i != bal->amounts.end();
|
||||
i++) {
|
||||
amount_t amt = (*i).second;
|
||||
amt.negate();
|
||||
amount_t amt = (*i).second.negate();
|
||||
|
||||
if (first) {
|
||||
(*x)->amount = amt;
|
||||
|
|
@ -239,7 +262,7 @@ bool entry_base_t::finalize()
|
|||
} else {
|
||||
transaction_t * nxact = new transaction_t((*x)->account);
|
||||
add_transaction(nxact);
|
||||
nxact->flags |= TRANSACTION_CALCULATED;
|
||||
nxact->add_flags(TRANSACTION_CALCULATED);
|
||||
nxact->amount = amt;
|
||||
}
|
||||
|
||||
|
|
@ -250,9 +273,8 @@ bool entry_base_t::finalize()
|
|||
// fall through...
|
||||
|
||||
case value_t::AMOUNT:
|
||||
(*x)->amount = balance.as_amount_lval();
|
||||
(*x)->amount.in_place_negate();
|
||||
(*x)->flags |= TRANSACTION_CALCULATED;
|
||||
(*x)->amount = balance.as_amount().negate();
|
||||
(*x)->add_flags(TRANSACTION_CALCULATED);
|
||||
|
||||
balance += (*x)->amount;
|
||||
break;
|
||||
|
|
@ -280,8 +302,7 @@ entry_t::entry_t(const entry_t& e)
|
|||
: entry_base_t(e), _date(e._date), _date_eff(e._date_eff),
|
||||
code(e.code), payee(e.payee)
|
||||
{
|
||||
DEBUG("ledger.memory.ctors", "ctor entry_t");
|
||||
|
||||
TRACE_CTOR(entry_t, "copy");
|
||||
for (transactions_list::const_iterator i = transactions.begin();
|
||||
i != transactions.end();
|
||||
i++)
|
||||
|
|
@ -333,20 +354,6 @@ bool entry_t::valid() const
|
|||
return true;
|
||||
}
|
||||
|
||||
auto_entry_t::auto_entry_t(const string& _predicate)
|
||||
: predicate_string(_predicate)
|
||||
{
|
||||
DEBUG("ledger.memory.ctors", "ctor auto_entry_t");
|
||||
predicate = new item_predicate<transaction_t>(predicate_string);
|
||||
}
|
||||
|
||||
auto_entry_t::~auto_entry_t()
|
||||
{
|
||||
DEBUG("ledger.memory.dtors", "dtor auto_entry_t");
|
||||
if (predicate)
|
||||
delete predicate;
|
||||
}
|
||||
|
||||
void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
|
||||
{
|
||||
transactions_list initial_xacts(entry.transactions.begin(),
|
||||
|
|
@ -355,14 +362,16 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
|
|||
for (transactions_list::iterator i = initial_xacts.begin();
|
||||
i != initial_xacts.end();
|
||||
i++) {
|
||||
if ((*predicate)(**i)) {
|
||||
if (predicate(**i)) {
|
||||
for (transactions_list::iterator t = transactions.begin();
|
||||
t != transactions.end();
|
||||
t++) {
|
||||
amount_t amt;
|
||||
assert((*t)->amount);
|
||||
if (! (*t)->amount.commodity()) {
|
||||
if (! post)
|
||||
continue;
|
||||
assert((*i)->amount);
|
||||
amt = (*i)->amount * (*t)->amount;
|
||||
} else {
|
||||
if (post)
|
||||
|
|
@ -371,13 +380,13 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
|
|||
}
|
||||
|
||||
account_t * account = (*t)->account;
|
||||
string fullname = account->fullname();
|
||||
string fullname = account->fullname();
|
||||
assert(! fullname.empty());
|
||||
if (fullname == "$account" || fullname == "@account")
|
||||
account = (*i)->account;
|
||||
|
||||
transaction_t * xact
|
||||
= new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO);
|
||||
= new transaction_t(account, amt, (*t)->flags() | TRANSACTION_AUTO);
|
||||
|
||||
// Copy over details so that the resulting transaction is a mirror of
|
||||
// the automated entry's one.
|
||||
|
|
@ -398,17 +407,16 @@ void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
|
|||
|
||||
account_t::~account_t()
|
||||
{
|
||||
DEBUG("ledger.memory.dtors", "dtor account_t " << this);
|
||||
//assert(! data);
|
||||
TRACE_DTOR(account_t);
|
||||
|
||||
for (accounts_map::iterator i = accounts.begin();
|
||||
i != accounts.end();
|
||||
i++)
|
||||
delete (*i).second;
|
||||
checked_delete((*i).second);
|
||||
}
|
||||
|
||||
account_t * account_t::find_account(const string& name,
|
||||
const bool auto_create)
|
||||
const bool auto_create)
|
||||
{
|
||||
accounts_map::const_iterator i = accounts.find(name);
|
||||
if (i != accounts.end())
|
||||
|
|
@ -442,7 +450,7 @@ account_t * account_t::find_account(const string& name,
|
|||
account->journal = journal;
|
||||
|
||||
std::pair<accounts_map::iterator, bool> result
|
||||
= accounts.insert(accounts_pair(first, account));
|
||||
= accounts.insert(accounts_map::value_type(first, account));
|
||||
assert(result.second);
|
||||
} else {
|
||||
account = (*i).second;
|
||||
|
|
@ -479,8 +487,8 @@ string account_t::fullname() const
|
|||
if (! _fullname.empty()) {
|
||||
return _fullname;
|
||||
} else {
|
||||
const account_t * first = this;
|
||||
string fullname = name;
|
||||
const account_t * first = this;
|
||||
string fullname = name;
|
||||
|
||||
while (first->parent) {
|
||||
first = first->parent;
|
||||
|
|
@ -526,29 +534,33 @@ bool account_t::valid() const
|
|||
|
||||
journal_t::~journal_t()
|
||||
{
|
||||
DEBUG("ledger.memory.dtors", "dtor journal_t");
|
||||
TRACE_DTOR(journal_t);
|
||||
|
||||
assert(master);
|
||||
delete master;
|
||||
checked_delete(master);
|
||||
|
||||
// Don't bother unhooking each entry's transactions from the
|
||||
// accounts they refer to, because all accounts are about to
|
||||
// be deleted.
|
||||
for (entries_list::iterator i = entries.begin();
|
||||
i != entries.end();
|
||||
i++)
|
||||
i++) {
|
||||
if (! item_pool ||
|
||||
((char *) *i) < item_pool || ((char *) *i) >= item_pool_end)
|
||||
delete *i;
|
||||
else
|
||||
reinterpret_cast<char *>(*i) < item_pool ||
|
||||
reinterpret_cast<char *>(*i) >= item_pool_end) {
|
||||
checked_delete(*i);
|
||||
} else {
|
||||
(*i)->~entry_t();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto_entries_list::iterator i = auto_entries.begin();
|
||||
i != auto_entries.end();
|
||||
i++)
|
||||
if (! item_pool ||
|
||||
((char *) *i) < item_pool || ((char *) *i) >= item_pool_end)
|
||||
delete *i;
|
||||
reinterpret_cast<char *>(*i) < item_pool ||
|
||||
reinterpret_cast<char *>(*i) >= item_pool_end)
|
||||
checked_delete(*i);
|
||||
else
|
||||
(*i)->~auto_entry_t();
|
||||
|
||||
|
|
@ -556,13 +568,14 @@ journal_t::~journal_t()
|
|||
i != period_entries.end();
|
||||
i++)
|
||||
if (! item_pool ||
|
||||
((char *) *i) < item_pool || ((char *) *i) >= item_pool_end)
|
||||
delete *i;
|
||||
reinterpret_cast<char *>(*i) < item_pool ||
|
||||
reinterpret_cast<char *>(*i) >= item_pool_end)
|
||||
checked_delete(*i);
|
||||
else
|
||||
(*i)->~period_entry_t();
|
||||
|
||||
if (item_pool)
|
||||
delete[] item_pool;
|
||||
checked_array_delete(item_pool);
|
||||
}
|
||||
|
||||
bool journal_t::add_entry(entry_t * entry)
|
||||
|
|
@ -581,9 +594,11 @@ bool journal_t::add_entry(entry_t * entry)
|
|||
for (transactions_list::const_iterator i = entry->transactions.begin();
|
||||
i != entry->transactions.end();
|
||||
i++)
|
||||
if ((*i)->cost && (*i)->amount)
|
||||
if ((*i)->cost) {
|
||||
assert((*i)->amount);
|
||||
(*i)->amount.commodity().add_price(entry->date(),
|
||||
*(*i)->cost / (*i)->amount);
|
||||
*(*i)->cost / (*i)->amount.number());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -621,18 +636,45 @@ bool journal_t::valid() const
|
|||
return false;
|
||||
}
|
||||
|
||||
for (commodity_pool_t::commodities_by_ident::const_iterator
|
||||
i = amount_t::current_pool->commodities.begin();
|
||||
i != amount_t::current_pool->commodities.end();
|
||||
i++)
|
||||
if (! (*i)->valid()) {
|
||||
DEBUG("ledger.validate", "journal_t: commodity not valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void print_entry(std::ostream& out, const entry_base_t& entry_base,
|
||||
const string& prefix)
|
||||
{
|
||||
string print_format;
|
||||
|
||||
if (dynamic_cast<const entry_t *>(&entry_base)) {
|
||||
print_format = (prefix + "%D %X%C%P\n" +
|
||||
prefix + " %-34A %12o\n%/" +
|
||||
prefix + " %-34A %12o\n");
|
||||
}
|
||||
else if (const auto_entry_t * entry =
|
||||
dynamic_cast<const auto_entry_t *>(&entry_base)) {
|
||||
out << "= " << entry->predicate.predicate.expr << '\n';
|
||||
print_format = prefix + " %-34A %12o\n";
|
||||
}
|
||||
else if (const period_entry_t * entry =
|
||||
dynamic_cast<const period_entry_t *>(&entry_base)) {
|
||||
out << "~ " << entry->period_string << '\n';
|
||||
print_format = prefix + " %-34A %12o\n";
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#if 0
|
||||
format_entries formatter(out, print_format);
|
||||
walk_transactions(const_cast<transactions_list&>(entry_base.transactions),
|
||||
formatter);
|
||||
formatter.flush();
|
||||
|
||||
clear_transaction_xdata cleaner;
|
||||
walk_transactions(const_cast<transactions_list&>(entry_base.transactions),
|
||||
cleaner);
|
||||
#endif
|
||||
}
|
||||
|
||||
void entry_context::describe(std::ostream& out) const throw()
|
||||
{
|
||||
if (! desc.empty())
|
||||
|
|
|
|||
248
journal.h
248
journal.h
|
|
@ -1,3 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _JOURNAL_H
|
||||
#define _JOURNAL_H
|
||||
|
||||
|
|
@ -19,52 +50,64 @@ namespace ledger {
|
|||
class entry_t;
|
||||
class account_t;
|
||||
|
||||
class transaction_t
|
||||
class transaction_t : public supports_flags<>
|
||||
{
|
||||
public:
|
||||
enum state_t { UNCLEARED, CLEARED, PENDING };
|
||||
|
||||
entry_t * entry;
|
||||
datetime_t _date;
|
||||
datetime_t _date_eff;
|
||||
account_t * account;
|
||||
amount_t amount;
|
||||
value_expr amount_expr;
|
||||
amount_t * cost;
|
||||
string cost_expr;
|
||||
state_t state;
|
||||
unsigned short flags;
|
||||
string note;
|
||||
istream_pos_type beg_pos;
|
||||
unsigned long beg_line;
|
||||
istream_pos_type end_pos;
|
||||
unsigned long end_line;
|
||||
mutable void * data;
|
||||
entry_t * entry;
|
||||
state_t state;
|
||||
account_t * account;
|
||||
optional<datetime_t> _date;
|
||||
optional<datetime_t> _date_eff;
|
||||
amount_t amount;
|
||||
value_expr amount_expr;
|
||||
optional<amount_t> cost;
|
||||
optional<value_expr> cost_expr;
|
||||
optional<string> note;
|
||||
istream_pos_type beg_pos;
|
||||
unsigned long beg_line;
|
||||
istream_pos_type end_pos;
|
||||
unsigned long end_line;
|
||||
|
||||
static bool use_effective_date;
|
||||
mutable void * data;
|
||||
static bool use_effective_date;
|
||||
|
||||
transaction_t(account_t * _account = NULL)
|
||||
: entry(NULL), account(_account), cost(NULL),
|
||||
state(UNCLEARED), flags(TRANSACTION_NORMAL),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor transaction_t");
|
||||
explicit transaction_t(account_t * _account = NULL)
|
||||
: supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
|
||||
state(UNCLEARED), account(_account),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
||||
{
|
||||
TRACE_CTOR(transaction_t, "account_t *");
|
||||
}
|
||||
transaction_t(account_t * _account,
|
||||
const amount_t& _amount,
|
||||
unsigned int _flags = TRANSACTION_NORMAL,
|
||||
const string& _note = "")
|
||||
: entry(NULL), account(_account), amount(_amount), cost(NULL),
|
||||
state(UNCLEARED), flags(_flags),
|
||||
note(_note), beg_pos(0), beg_line(0), end_pos(0), end_line(0),
|
||||
data(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor transaction_t");
|
||||
explicit transaction_t(account_t * _account,
|
||||
const amount_t& _amount,
|
||||
unsigned int _flags = TRANSACTION_NORMAL,
|
||||
const optional<string> _note = none)
|
||||
: supports_flags<>(_flags), entry(NULL), state(UNCLEARED),
|
||||
account(_account), amount(_amount), note(_note),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
||||
{
|
||||
TRACE_CTOR(transaction_t,
|
||||
"account_t *, const amount_t&, unsigned int, const string&");
|
||||
}
|
||||
transaction_t(const transaction_t& xact)
|
||||
: entry(xact.entry), account(xact.account), amount(xact.amount),
|
||||
cost(xact.cost ? new amount_t(*xact.cost) : NULL),
|
||||
state(xact.state), flags(xact.flags), note(xact.note),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor transaction_t");
|
||||
explicit transaction_t(const transaction_t& xact)
|
||||
: supports_flags<>(xact),
|
||||
entry(xact.entry),
|
||||
state(xact.state),
|
||||
account(xact.account),
|
||||
_date(xact._date),
|
||||
_date_eff(xact._date_eff),
|
||||
amount(xact.amount),
|
||||
cost(xact.cost),
|
||||
note(xact.note),
|
||||
beg_pos(xact.beg_pos),
|
||||
beg_line(xact.beg_line),
|
||||
end_pos(xact.end_pos),
|
||||
end_line(xact.end_line),
|
||||
data(xact.data) // jww (2008-07-19): What are the copy semantics?
|
||||
{
|
||||
TRACE_CTOR(transaction_t, "copy");
|
||||
}
|
||||
~transaction_t();
|
||||
|
||||
|
|
@ -77,13 +120,6 @@ class transaction_t
|
|||
return actual_date();
|
||||
}
|
||||
|
||||
bool operator==(const transaction_t& xact) {
|
||||
return this == &xact;
|
||||
}
|
||||
bool operator!=(const transaction_t& xact) {
|
||||
return ! (*this == xact);
|
||||
}
|
||||
|
||||
bool valid() const;
|
||||
};
|
||||
|
||||
|
|
@ -115,24 +151,24 @@ class entry_base_t
|
|||
entry_base_t() : journal(NULL),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
|
||||
{
|
||||
DEBUG("ledger.memory.ctors", "ctor entry_base_t");
|
||||
TRACE_CTOR(entry_base_t, "");
|
||||
}
|
||||
entry_base_t(const entry_base_t& e) : journal(NULL),
|
||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
|
||||
{
|
||||
DEBUG("ledger.memory.ctors", "ctor entry_base_t");
|
||||
TRACE_CTOR(entry_base_t, "copy");
|
||||
for (transactions_list::const_iterator i = e.transactions.begin();
|
||||
i != e.transactions.end();
|
||||
i++)
|
||||
transactions.push_back(new transaction_t(**i));
|
||||
}
|
||||
virtual ~entry_base_t() {
|
||||
DEBUG("ledger.memory.dtors", "dtor entry_base_t");
|
||||
TRACE_DTOR(entry_base_t);
|
||||
for (transactions_list::iterator i = transactions.begin();
|
||||
i != transactions.end();
|
||||
i++)
|
||||
if (! ((*i)->flags & TRANSACTION_BULK_ALLOC))
|
||||
delete *i;
|
||||
if (! (*i)->has_flags(TRANSACTION_BULK_ALLOC))
|
||||
checked_delete(*i);
|
||||
else
|
||||
(*i)->~transaction_t();
|
||||
}
|
||||
|
|
@ -153,28 +189,28 @@ class entry_base_t
|
|||
|
||||
class entry_t : public entry_base_t
|
||||
{
|
||||
public:
|
||||
datetime_t _date;
|
||||
datetime_t _date_eff;
|
||||
string code;
|
||||
string payee;
|
||||
public:
|
||||
datetime_t _date;
|
||||
optional<datetime_t> _date_eff;
|
||||
optional<string> code;
|
||||
string payee;
|
||||
|
||||
entry_t() {
|
||||
DEBUG("ledger.memory.ctors", "ctor entry_t");
|
||||
TRACE_CTOR(entry_t, "");
|
||||
}
|
||||
entry_t(const entry_t& e);
|
||||
|
||||
virtual ~entry_t() {
|
||||
DEBUG("ledger.memory.dtors", "dtor entry_t");
|
||||
TRACE_DTOR(entry_t);
|
||||
}
|
||||
|
||||
datetime_t actual_date() const {
|
||||
return _date;
|
||||
}
|
||||
datetime_t effective_date() const {
|
||||
if (! is_valid(_date_eff))
|
||||
if (! _date_eff)
|
||||
return _date;
|
||||
return _date_eff;
|
||||
return *_date_eff;
|
||||
}
|
||||
datetime_t date() const {
|
||||
if (transaction_t::use_effective_date)
|
||||
|
|
@ -200,8 +236,8 @@ class entry_context : public error_context {
|
|||
const entry_base_t& entry;
|
||||
|
||||
entry_context(const entry_base_t& _entry,
|
||||
const string& desc = "") throw()
|
||||
: error_context(desc), entry(_entry) {}
|
||||
const string& _desc = "") throw()
|
||||
: error_context(_desc), entry(_entry) {}
|
||||
virtual ~entry_context() throw() {}
|
||||
|
||||
virtual void describe(std::ostream& out) const throw();
|
||||
|
|
@ -214,15 +250,18 @@ class item_predicate;
|
|||
class auto_entry_t : public entry_base_t
|
||||
{
|
||||
public:
|
||||
item_predicate<transaction_t> * predicate;
|
||||
string predicate_string;
|
||||
item_predicate<transaction_t> predicate;
|
||||
|
||||
auto_entry_t() : predicate(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor auto_entry_t");
|
||||
auto_entry_t();
|
||||
auto_entry_t(const string& _predicate)
|
||||
: predicate(_predicate)
|
||||
{
|
||||
TRACE_CTOR(auto_entry_t, "const string&");
|
||||
}
|
||||
auto_entry_t(const string& _predicate);
|
||||
|
||||
virtual ~auto_entry_t();
|
||||
virtual ~auto_entry_t() {
|
||||
TRACE_DTOR(auto_entry_t);
|
||||
}
|
||||
|
||||
virtual void extend_entry(entry_base_t& entry, bool post);
|
||||
virtual bool valid() const {
|
||||
|
|
@ -230,7 +269,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class journal_t;
|
||||
struct auto_entry_finalizer_t : public entry_finalizer_t {
|
||||
journal_t * journal;
|
||||
auto_entry_finalizer_t(journal_t * _journal) : journal(_journal) {}
|
||||
|
|
@ -245,19 +283,19 @@ class period_entry_t : public entry_base_t
|
|||
string period_string;
|
||||
|
||||
period_entry_t() {
|
||||
DEBUG("ledger.memory.ctors", "ctor period_entry_t");
|
||||
TRACE_CTOR(period_entry_t, "");
|
||||
}
|
||||
period_entry_t(const string& _period)
|
||||
: period(_period), period_string(_period) {
|
||||
DEBUG("ledger.memory.ctors", "ctor period_entry_t");
|
||||
TRACE_CTOR(period_entry_t, "const string&");
|
||||
}
|
||||
period_entry_t(const period_entry_t& e)
|
||||
: entry_base_t(e), period(e.period), period_string(e.period_string) {
|
||||
DEBUG("ledger.memory.ctors", "ctor period_entry_t");
|
||||
TRACE_CTOR(period_entry_t, "copy");
|
||||
}
|
||||
|
||||
virtual ~period_entry_t() {
|
||||
DEBUG("ledger.memory.dtors", "dtor period_entry_t");
|
||||
TRACE_DTOR(period_entry_t);
|
||||
}
|
||||
|
||||
virtual bool valid() const {
|
||||
|
|
@ -267,44 +305,39 @@ class period_entry_t : public entry_base_t
|
|||
|
||||
|
||||
typedef std::map<const string, account_t *> accounts_map;
|
||||
typedef std::pair<const string, account_t *> accounts_pair;
|
||||
|
||||
class account_t
|
||||
{
|
||||
public:
|
||||
typedef unsigned long ident_t;
|
||||
|
||||
journal_t * journal;
|
||||
account_t * parent;
|
||||
string name;
|
||||
string note;
|
||||
unsigned short depth;
|
||||
accounts_map accounts;
|
||||
journal_t * journal;
|
||||
account_t * parent;
|
||||
string name;
|
||||
optional<string> note;
|
||||
unsigned short depth;
|
||||
accounts_map accounts;
|
||||
|
||||
mutable void * data;
|
||||
mutable ident_t ident;
|
||||
mutable string _fullname;
|
||||
mutable string _fullname;
|
||||
|
||||
account_t(account_t * _parent = NULL,
|
||||
const string& _name = "",
|
||||
const string& _note = "")
|
||||
const optional<string> _note = none)
|
||||
: parent(_parent), name(_name), note(_note),
|
||||
depth(parent ? parent->depth + 1 : 0), data(NULL), ident(0) {
|
||||
DEBUG("ledger.memory.ctors", "ctor account_t " << this);
|
||||
TRACE_CTOR(account_t, "account_t *, const string&, const string&");
|
||||
}
|
||||
~account_t();
|
||||
|
||||
bool operator==(const account_t& account) {
|
||||
return this == &account;
|
||||
operator string() const {
|
||||
return fullname();
|
||||
}
|
||||
bool operator!=(const account_t& account) {
|
||||
return ! (*this == account);
|
||||
}
|
||||
|
||||
string fullname() const;
|
||||
|
||||
void add_account(account_t * acct) {
|
||||
accounts.insert(accounts_pair(acct->name, acct));
|
||||
accounts.insert(accounts_map::value_type(acct->name, acct));
|
||||
acct->journal = journal;
|
||||
}
|
||||
bool remove_account(account_t * acct) {
|
||||
|
|
@ -315,10 +348,6 @@ class account_t
|
|||
|
||||
account_t * find_account(const string& name, bool auto_create = true);
|
||||
|
||||
operator string() const {
|
||||
return fullname();
|
||||
}
|
||||
|
||||
bool valid() const;
|
||||
|
||||
friend class journal_t;
|
||||
|
|
@ -331,7 +360,8 @@ struct func_finalizer_t : public entry_finalizer_t {
|
|||
typedef bool (*func_t)(entry_t& entry, bool post);
|
||||
func_t func;
|
||||
func_finalizer_t(func_t _func) : func(_func) {}
|
||||
func_finalizer_t(const func_finalizer_t& other) : func(other.func) {}
|
||||
func_finalizer_t(const func_finalizer_t& other) :
|
||||
entry_finalizer_t(), func(other.func) {}
|
||||
virtual bool operator()(entry_t& entry, bool post) {
|
||||
return func(entry, post);
|
||||
}
|
||||
|
|
@ -364,19 +394,19 @@ bool run_hooks(std::list<T>& list, Data& item, bool post) {
|
|||
typedef std::list<entry_t *> entries_list;
|
||||
typedef std::list<auto_entry_t *> auto_entries_list;
|
||||
typedef std::list<period_entry_t *> period_entries_list;
|
||||
typedef std::list<path> paths_list;
|
||||
typedef std::list<string> strings_list;
|
||||
typedef std::list<path> paths_list;
|
||||
|
||||
class journal_t
|
||||
{
|
||||
public:
|
||||
account_t * master;
|
||||
account_t * basket;
|
||||
entries_list entries;
|
||||
paths_list sources;
|
||||
path price_db;
|
||||
char * item_pool;
|
||||
char * item_pool_end;
|
||||
account_t * master;
|
||||
account_t * basket;
|
||||
entries_list entries;
|
||||
paths_list sources;
|
||||
optional<path> price_db;
|
||||
char * item_pool;
|
||||
char * item_pool_end;
|
||||
|
||||
auto_entries_list auto_entries;
|
||||
period_entries_list period_entries;
|
||||
|
|
@ -384,21 +414,13 @@ class journal_t
|
|||
|
||||
std::list<entry_finalizer_t *> entry_finalize_hooks;
|
||||
|
||||
journal_t() : basket(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor journal_t");
|
||||
journal_t() : basket(NULL), item_pool(NULL), item_pool_end(NULL) {
|
||||
TRACE_CTOR(journal_t, "");
|
||||
master = new account_t(NULL, "");
|
||||
master->journal = this;
|
||||
item_pool = item_pool_end = NULL;
|
||||
}
|
||||
~journal_t();
|
||||
|
||||
bool operator==(const journal_t& journal) {
|
||||
return this == &journal;
|
||||
}
|
||||
bool operator!=(const journal_t& journal) {
|
||||
return ! (*this == journal);
|
||||
}
|
||||
|
||||
void add_account(account_t * acct) {
|
||||
master->add_account(acct);
|
||||
acct->journal = this;
|
||||
|
|
@ -414,7 +436,7 @@ class journal_t
|
|||
return (*c).second;
|
||||
|
||||
account_t * account = master->find_account(name, auto_create);
|
||||
accounts_cache.insert(accounts_pair(name, account));
|
||||
accounts_cache.insert(accounts_map::value_type(name, account));
|
||||
account->journal = this;
|
||||
return account;
|
||||
}
|
||||
|
|
|
|||
710
main.cc
710
main.cc
|
|
@ -1,232 +1,214 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "acconf.h"
|
||||
#include "utils.h"
|
||||
#include "option.h"
|
||||
//#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
||||
//#include "gnucash.h"
|
||||
//#endif
|
||||
//#include "qif.h"
|
||||
//#include "ofx.h"
|
||||
#include "jbuilder.h"
|
||||
#include "compile.h"
|
||||
|
||||
#include <ledger.h>
|
||||
|
||||
#ifdef HAVE_UNIX_PIPES
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "fdstream.hpp"
|
||||
#include <fdstream.hpp>
|
||||
#endif
|
||||
|
||||
#include "ledger.h"
|
||||
|
||||
using namespace ledger;
|
||||
|
||||
int parse_and_report(config_t& config, std::auto_ptr<journal_t>& journal,
|
||||
report_t& report, int argc, char * argv[], char * envp[])
|
||||
static int read_and_report(ledger::report_t& report, int argc, char * argv[],
|
||||
char * envp[])
|
||||
{
|
||||
// Configure the terminus for value expressions
|
||||
using namespace ledger;
|
||||
|
||||
ledger::terminus = current_moment;
|
||||
session_t& session(report.session);
|
||||
|
||||
// Parse command-line arguments, and those set in the environment
|
||||
// Handle the command-line arguments
|
||||
|
||||
std::list<string> args;
|
||||
process_arguments(ledger::config_options, argc - 1, argv + 1, false, args);
|
||||
strings_list args;
|
||||
process_arguments(argc - 1, argv + 1, false, report, args);
|
||||
|
||||
if (args.empty()) {
|
||||
option_help(std::cerr);
|
||||
#if 0
|
||||
help(std::cerr);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
strings_list::iterator arg = args.begin();
|
||||
|
||||
if (config.cache_file == "<none>")
|
||||
config.use_cache = false;
|
||||
if (! session.cache_file)
|
||||
session.use_cache = false;
|
||||
else
|
||||
config.use_cache = config.data_file.empty() && config.price_db.empty();
|
||||
DEBUG("ledger.config.cache", "1. use_cache = " << config.use_cache);
|
||||
session.use_cache = ! session.data_file.empty() && session.price_db;
|
||||
|
||||
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
|
||||
|
||||
// Process the environment settings
|
||||
|
||||
TRACE_START(environment, 1, "Processed environment variables");
|
||||
process_environment(const_cast<const char **>(envp), "LEDGER_", report);
|
||||
TRACE_FINISH(environment, 1);
|
||||
|
||||
optional<path> home;
|
||||
if (const char * home_var = std::getenv("HOME"))
|
||||
home = home_var;
|
||||
|
||||
if (! session.init_file)
|
||||
session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
|
||||
if (! session.price_db)
|
||||
session.price_db = home ? *home / ".pricedb" : "./.pricedb";
|
||||
|
||||
if (! session.cache_file)
|
||||
session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
|
||||
|
||||
if (session.data_file == *session.cache_file)
|
||||
session.use_cache = false;
|
||||
|
||||
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
|
||||
|
||||
INFO("Initialization file is " << session.init_file->string());
|
||||
INFO("Price database is " << session.price_db->string());
|
||||
INFO("Binary cache is " << session.cache_file->string());
|
||||
INFO("Journal file is " << session.data_file.string());
|
||||
|
||||
if (! session.use_cache)
|
||||
INFO("Binary cache mechanism will not be used");
|
||||
|
||||
// Read the command word and create a command object based on it
|
||||
|
||||
string verb = *arg++;
|
||||
|
||||
xml::xpath_t::function_t command;
|
||||
|
||||
#if 0
|
||||
TRACE(main, "Processing options and environment variables");
|
||||
if (verb == "register" || verb == "reg" || verb == "r")
|
||||
command = register_command();
|
||||
else if (verb == "balance" || verb == "bal" || verb == "b")
|
||||
command = balance_command();
|
||||
else if (verb == "print" || verb == "p")
|
||||
command = print_command();
|
||||
else if (verb == "equity")
|
||||
command = equity_command();
|
||||
else if (verb == "entry")
|
||||
command = entry_command();
|
||||
else if (verb == "dump")
|
||||
command = dump_command();
|
||||
else if (verb == "output")
|
||||
command = output_command();
|
||||
else if (verb == "prices")
|
||||
command = prices_command();
|
||||
else if (verb == "pricesdb")
|
||||
command = pricesdb_command();
|
||||
else if (verb == "csv")
|
||||
command = csv_command();
|
||||
else if (verb == "emacs" || verb == "lisp")
|
||||
command = emacs_command();
|
||||
else
|
||||
#endif
|
||||
if (verb == "xml")
|
||||
command = bind(xml_command, _1);
|
||||
else if (verb == "expr")
|
||||
;
|
||||
else if (verb == "xpath")
|
||||
;
|
||||
else if (verb == "parse") {
|
||||
xml::xpath_t expr(*arg);
|
||||
xml::document_t temp(xml::LEDGER_NODE);
|
||||
|
||||
process_environment(ledger::config_options,
|
||||
const_cast<const char **>(envp), "LEDGER_");
|
||||
xml::xpath_t::context_scope_t doc_scope(report, &temp);
|
||||
|
||||
#if 1
|
||||
// These are here for backwards compatability, but are deprecated.
|
||||
|
||||
if (const char * p = std::getenv("LEDGER"))
|
||||
process_option(ledger::config_options, "file", p);
|
||||
if (const char * p = std::getenv("LEDGER_INIT"))
|
||||
process_option(ledger::config_options, "init-file", p);
|
||||
if (const char * p = std::getenv("PRICE_HIST"))
|
||||
process_option(ledger::config_options, "price-db", p);
|
||||
if (const char * p = std::getenv("PRICE_EXP"))
|
||||
process_option(ledger::config_options, "price-exp", p);
|
||||
#endif
|
||||
|
||||
const char * p = std::getenv("HOME");
|
||||
string home = p ? p : "";
|
||||
|
||||
if (config.init_file.empty())
|
||||
config.init_file = home + "/.ledgerrc";
|
||||
if (config.price_db.empty())
|
||||
config.price_db = home + "/.pricedb";
|
||||
|
||||
if (config.cache_file.empty())
|
||||
config.cache_file = home + "/.ledger-cache";
|
||||
|
||||
if (config.data_file == config.cache_file)
|
||||
config.use_cache = false;
|
||||
DEBUG("ledger.config.cache", "2. use_cache = " << config.use_cache);
|
||||
|
||||
#if 0
|
||||
TRACE(main, string("Initialization file is ") + config.init_file);
|
||||
TRACE(main, string("Price database is ") + config.price_db);
|
||||
TRACE(main, string("Binary cache is ") + config.cache_file);
|
||||
TRACE(main, string("Main journal is ") + config.data_file);
|
||||
|
||||
TRACE(main, string("Based on option settings, binary cache ") +
|
||||
(config.use_cache ? "WILL " : "will NOT ") + "be used");
|
||||
#endif
|
||||
|
||||
// Read the command word, canonicalize it to its one letter form,
|
||||
// then configure the system based on the kind of report to be
|
||||
// generated
|
||||
|
||||
string command = *arg++;
|
||||
|
||||
if (command == "balance" || command == "bal" || command == "b")
|
||||
command = "b";
|
||||
else if (command == "register" || command == "reg" || command == "r")
|
||||
command = "r";
|
||||
else if (command == "print" || command == "p")
|
||||
command = "p";
|
||||
else if (command == "output")
|
||||
command = "w";
|
||||
else if (command == "dump")
|
||||
command = "W";
|
||||
else if (command == "emacs" || command == "lisp")
|
||||
command = "x";
|
||||
else if (command == "xml")
|
||||
command = "X";
|
||||
else if (command == "entry")
|
||||
command = "e";
|
||||
else if (command == "equity")
|
||||
command = "E";
|
||||
else if (command == "prices")
|
||||
command = "P";
|
||||
else if (command == "pricesdb")
|
||||
command = "D";
|
||||
else if (command == "csv")
|
||||
command = "c";
|
||||
else if (command == "parse") {
|
||||
value_expr expr(ledger::parse_value_expr(*arg));
|
||||
|
||||
if (config.verbose_mode) {
|
||||
IF_INFO() {
|
||||
std::cout << "Value expression tree:" << std::endl;
|
||||
ledger::dump_value_expr(std::cout, expr.get());
|
||||
expr.dump(std::cout);
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "Value expression parsed was:" << std::endl;
|
||||
ledger::print_value_expr(std::cout, expr.get());
|
||||
expr.print(std::cout, doc_scope);
|
||||
std::cout << std::endl << std::endl;
|
||||
std::cout << "Result of computation: ";
|
||||
|
||||
expr.compile(doc_scope);
|
||||
|
||||
std::cout << "Value expression after compiling:" << std::endl;
|
||||
expr.dump(std::cout);
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "Value expression is now:" << std::endl;
|
||||
expr.print(std::cout, doc_scope);
|
||||
std::cout << std::endl << std::endl;
|
||||
|
||||
std::cout << "Result of calculation: ";
|
||||
}
|
||||
|
||||
value_t result = guarded_compute(expr.get());
|
||||
std::cout << result.strip_annotations() << std::endl;
|
||||
std::cout << expr.calc(doc_scope).strip_annotations() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (command == "expr") {
|
||||
// this gets done below...
|
||||
}
|
||||
else {
|
||||
throw new error(string("Unrecognized command '") + command + "'");
|
||||
char buf[128];
|
||||
std::strcpy(buf, "command_");
|
||||
std::strcat(buf, verb.c_str());
|
||||
|
||||
if (xml::xpath_t::ptr_op_t def = report.lookup(buf))
|
||||
command = def->as_function();
|
||||
|
||||
if (! command)
|
||||
throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
|
||||
}
|
||||
|
||||
// Parse initialization files, ledger data, price database, etc.
|
||||
// Parse the initialization file, which can only be textual; then
|
||||
// parse the journal data.
|
||||
|
||||
journal.reset(new journal_t);
|
||||
session.read_init();
|
||||
|
||||
#if 0
|
||||
{ TRACE_PUSH(parser, "Parsing journal file");
|
||||
#endif
|
||||
INFO_START(journal, "Read journal file");
|
||||
|
||||
if (parse_ledger_data(config, journal.get()) == 0)
|
||||
throw new error("Please specify ledger file using -f"
|
||||
" or LEDGER_FILE environment variable.");
|
||||
xml::document_t xml_document(xml::LEDGER_NODE);
|
||||
journal_t * journal = session.create_journal();
|
||||
xml::journal_builder_t builder(xml_document, journal);
|
||||
|
||||
#if 0
|
||||
TRACE_POP(parser, "Finished parsing"); }
|
||||
#endif
|
||||
if (! session.read_data(builder, journal, report.account))
|
||||
throw_(parse_error, "Failed to locate any journal entries; "
|
||||
"did you specify a valid file with -f?");
|
||||
|
||||
// process the command word and its following arguments
|
||||
INFO_FINISH(journal);
|
||||
|
||||
string first_arg;
|
||||
if (command == "w") {
|
||||
if (arg != args.end())
|
||||
first_arg = *arg++;
|
||||
}
|
||||
else if (command == "W") {
|
||||
if (report.output_file.empty())
|
||||
throw new error("The 'dump' command requires use of the --output option");
|
||||
}
|
||||
|
||||
#if 0
|
||||
TRACE(options, string("Post-processing options ") +
|
||||
"for command \"" + command + "\"");
|
||||
#endif
|
||||
|
||||
report.process_options(command, arg, args.end());
|
||||
|
||||
#if 0
|
||||
// jww (2008-05-08): Is this disabled now?
|
||||
// If downloading is to be supported, configure the updater
|
||||
|
||||
if (! commodity_base_t::updater && config.download_quotes)
|
||||
commodity_base_t::updater =
|
||||
new quotes_by_script(config.price_db, config.pricing_leeway,
|
||||
config.cache_dirty);
|
||||
#endif
|
||||
|
||||
std::auto_ptr<entry_t> new_entry;
|
||||
if (command == "e") {
|
||||
if (arg == args.end()) {
|
||||
std::cout << "\
|
||||
The entry command requires at least one argument, so Ledger can intelligently\n\
|
||||
create a new entry for you. The possible arguments are:\n\
|
||||
DATE PAYEE [ACCOUNT] [AMOUNT] [DRAW ACCOUNT]\n\n\
|
||||
Some things to note:\n\
|
||||
- The ACCOUNT is optional; if no account is given, the last account affected\n\
|
||||
by PAYEE is used. If no payee can be found, the generic account 'Expenses'\n\
|
||||
is used.\n\
|
||||
- The AMOUNT is optional; if not specified, the same amount is used as the\n\
|
||||
last time PAYEE was seen, or 0 if not applicable.\n\
|
||||
- The AMOUNT does not require a commodity; if none is given, the commodity\n\
|
||||
currently contained within ACCOUNT is used, or no commodity at all if\n\
|
||||
either: the ACCOUNT was not found, or it contains more than one commodity.\n\
|
||||
- Lastly, the DRAW ACCOUNT is optional; if not present, the last account\n\
|
||||
drawn from by PAYEE is used, or the 'basket' account (specified with\n\
|
||||
'A ACCOUNT' in your Ledger file) if that does not apply, or the generic\n\
|
||||
account 'Equity' is used.\n\n\
|
||||
Here are a few examples, all of which may be equivalent depending on your\n\
|
||||
Ledger data:\n\
|
||||
ledger entry 3/25 chevron\n\
|
||||
ledger entry 3/25 chevron 20\n\
|
||||
ledger entry 3/25 chevron \\$20\n\
|
||||
ledger entry 3/25 chevron gas 20\n\
|
||||
ledger entry 3/25 chevron gas \\$20 checking\n\n\
|
||||
A final note: Ledger never modifies your data! You are responsible for\n\
|
||||
appending the output of this command to your Ledger file if you so choose."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
new_entry.reset(derive_new_entry(*journal, arg, args.end()));
|
||||
if (! new_entry.get())
|
||||
return 1;
|
||||
}
|
||||
TRACE_FINISH(entry_text, 1);
|
||||
TRACE_FINISH(entry_date, 1);
|
||||
TRACE_FINISH(entry_details, 1);
|
||||
TRACE_FINISH(entry_xacts, 1);
|
||||
TRACE_FINISH(entries, 1);
|
||||
TRACE_FINISH(parsing_total, 1);
|
||||
|
||||
// Configure the output stream
|
||||
|
||||
|
|
@ -235,22 +217,20 @@ appending the output of this command to your Ledger file if you so choose."
|
|||
#endif
|
||||
std::ostream * out = &std::cout;
|
||||
|
||||
if (! report.output_file.empty()) {
|
||||
out = new ofstream(report.output_file);
|
||||
if (report.output_file) {
|
||||
out = new ofstream(*report.output_file);
|
||||
}
|
||||
#ifdef HAVE_UNIX_PIPES
|
||||
else if (! config.pager.empty()) {
|
||||
else if (report.pager) {
|
||||
status = pipe(pfd);
|
||||
if (status == -1)
|
||||
throw new error("Failed to create pipe");
|
||||
throw_(std::logic_error, "Failed to create pipe");
|
||||
|
||||
status = fork();
|
||||
if (status < 0) {
|
||||
throw new error("Failed to fork child process");
|
||||
throw_(std::logic_error, "Failed to fork child process");
|
||||
}
|
||||
else if (status == 0) { // child
|
||||
const char *arg0;
|
||||
|
||||
// Duplicate pipe's reading end into stdin
|
||||
status = dup2(pfd[0], STDIN_FILENO);
|
||||
if (status == -1)
|
||||
|
|
@ -265,13 +245,8 @@ appending the output of this command to your Ledger file if you so choose."
|
|||
// Find command name: its the substring starting right of the
|
||||
// rightmost '/' character in the pager pathname. See manpage
|
||||
// for strrchr.
|
||||
arg0 = std::strrchr(config.pager.c_str(), '/');
|
||||
if (arg0)
|
||||
arg0++;
|
||||
else
|
||||
arg0 = config.pager.c_str(); // No slashes in pager.
|
||||
|
||||
execlp(config.pager.c_str(), arg0, (char *)0);
|
||||
execlp(report.pager->native_file_string().c_str(),
|
||||
basename(*report.pager).c_str(), (char *)0);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -282,236 +257,188 @@ appending the output of this command to your Ledger file if you so choose."
|
|||
}
|
||||
#endif
|
||||
|
||||
// Are we handling the parse or expr commands? Do so now.
|
||||
report.define("ostream", value_t(out));
|
||||
|
||||
if (command == "expr") {
|
||||
value_expr expr(ledger::parse_value_expr(*arg));
|
||||
// Are we handling the expr commands? Do so now.
|
||||
|
||||
if (config.verbose_mode) {
|
||||
std::cout << "Value expression tree:" << std::endl;
|
||||
ledger::dump_value_expr(std::cout, expr.get());
|
||||
std::cout << std::endl;
|
||||
std::cout << "Value expression parsed was:" << std::endl;
|
||||
ledger::print_value_expr(std::cout, expr.get());
|
||||
std::cout << std::endl << std::endl;
|
||||
std::cout << "Result of computation: ";
|
||||
xml::xpath_t::context_scope_t doc_scope(report, &xml_document);
|
||||
|
||||
if (verb == "expr") {
|
||||
xml::xpath_t expr(*arg);
|
||||
|
||||
IF_INFO() {
|
||||
*out << "Value expression tree:" << std::endl;
|
||||
expr.dump(*out);
|
||||
*out << std::endl;
|
||||
*out << "Value expression parsed was:" << std::endl;
|
||||
expr.print(*out, doc_scope);
|
||||
*out << std::endl << std::endl;
|
||||
*out << "Result of calculation: ";
|
||||
}
|
||||
|
||||
value_t result = guarded_compute(expr.get());
|
||||
std::cout << result.strip_annotations() << std::endl;
|
||||
*out << expr.calc(doc_scope).strip_annotations() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (verb == "xpath") {
|
||||
std::cout << "XPath parsed: ";
|
||||
|
||||
// Compile the format strings
|
||||
xml::xpath_t xpath(*arg);
|
||||
xpath.print(*out, doc_scope);
|
||||
*out << std::endl;
|
||||
|
||||
const string * format;
|
||||
IF_INFO() {
|
||||
*out << "Raw results:" << std::endl;
|
||||
|
||||
if (! report.format_string.empty())
|
||||
format = &report.format_string;
|
||||
else if (command == "b")
|
||||
format = &config.balance_format;
|
||||
else if (command == "r")
|
||||
format = &config.register_format;
|
||||
else if (command == "E")
|
||||
format = &config.equity_format;
|
||||
else if (command == "P")
|
||||
format = &config.prices_format;
|
||||
else if (command == "D")
|
||||
format = &config.pricesdb_format;
|
||||
else if (command == "w")
|
||||
format = &config.write_xact_format;
|
||||
else
|
||||
format = &config.print_format;
|
||||
|
||||
// Walk the entries based on the report type and the options
|
||||
|
||||
item_handler<transaction_t> * formatter;
|
||||
std::list<item_handler<transaction_t> *> formatter_ptrs;
|
||||
|
||||
if (command == "b" || command == "E")
|
||||
formatter = new set_account_value;
|
||||
else if (command == "p" || command == "e")
|
||||
formatter = new format_entries(*out, *format);
|
||||
else if (command == "x")
|
||||
formatter = new format_emacs_transactions(*out);
|
||||
else if (command == "X")
|
||||
formatter = new format_xml_entries(*out, report.show_totals);
|
||||
else if (command == "c")
|
||||
formatter = new format_csv_transactions(*out);
|
||||
else
|
||||
formatter = new format_transactions(*out, *format);
|
||||
|
||||
if (command == "w") {
|
||||
#if 0
|
||||
TRACE_PUSH(text_writer, "Writing journal file");
|
||||
#endif
|
||||
write_textual_journal(*journal, first_arg, *formatter,
|
||||
config.write_hdr_format, *out);
|
||||
#if 0
|
||||
TRACE_POP(text_writer, "Finished writing");
|
||||
#endif
|
||||
}
|
||||
else if (command == "W") {
|
||||
#if 0
|
||||
TRACE_PUSH(binary_writer, "Writing binary file");
|
||||
#endif
|
||||
ofstream stream(report.output_file);
|
||||
binary::write_journal(stream, journal.get());
|
||||
#if 0
|
||||
TRACE_POP(binary_writer, "Finished writing");
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#if 0
|
||||
TRACE_PUSH(main, "Walking journal entries");
|
||||
#endif
|
||||
|
||||
formatter = report.chain_xact_handlers(command, formatter, journal.get(),
|
||||
journal->master, formatter_ptrs);
|
||||
if (command == "e")
|
||||
walk_transactions(new_entry->transactions, *formatter);
|
||||
else if (command == "P" || command == "D")
|
||||
walk_commodities(amount_t::current_pool->commodities, *formatter);
|
||||
else
|
||||
walk_entries(journal->entries, *formatter);
|
||||
|
||||
if (command != "P" && command != "D")
|
||||
formatter->flush();
|
||||
|
||||
#if 0
|
||||
TRACE_POP(main, "Finished entry walk");
|
||||
#endif
|
||||
}
|
||||
|
||||
// For the balance and equity reports, output the sum totals.
|
||||
|
||||
if (command == "b") {
|
||||
#if 0
|
||||
TRACE_PUSH(main, "Walking journal accounts");
|
||||
#endif
|
||||
|
||||
format_account acct_formatter(*out, *format, report.display_predicate);
|
||||
sum_accounts(*journal->master);
|
||||
walk_accounts(*journal->master, acct_formatter, report.sort_string);
|
||||
acct_formatter.flush();
|
||||
|
||||
if (account_has_xdata(*journal->master)) {
|
||||
account_xdata_t& xdata = account_xdata(*journal->master);
|
||||
if (! report.show_collapsed && xdata.total) {
|
||||
*out << "--------------------\n";
|
||||
xdata.value = xdata.total;
|
||||
acct_formatter.format.format(*out, details_t(*journal->master));
|
||||
foreach (const value_t& value, xpath.find_all(doc_scope)) {
|
||||
if (value.is_xml_node())
|
||||
value.as_xml_node()->print(std::cout);
|
||||
else
|
||||
std::cout << value;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
*out << "Compiled results:" << std::endl;
|
||||
}
|
||||
#if 0
|
||||
TRACE_POP(main, "Finished account walk");
|
||||
#endif
|
||||
}
|
||||
else if (command == "E") {
|
||||
#if 0
|
||||
TRACE_PUSH(main, "Walking journal accounts");
|
||||
#endif
|
||||
|
||||
format_equity acct_formatter(*out, *format, report.display_predicate);
|
||||
sum_accounts(*journal->master);
|
||||
walk_accounts(*journal->master, acct_formatter, report.sort_string);
|
||||
acct_formatter.flush();
|
||||
xml::compile_node(xml_document, report);
|
||||
|
||||
#if 0
|
||||
TRACE_POP(main, "Finished account walk");
|
||||
#endif
|
||||
foreach (const value_t& value, xpath.find_all(doc_scope)) {
|
||||
if (value.is_xml_node())
|
||||
value.as_xml_node()->print(std::cout);
|
||||
else
|
||||
std::cout << value;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if DEBUG_LEVEL >= BETA
|
||||
#if 0
|
||||
{ TRACE_PUSH(cleanup, "Cleaning up allocated memory");
|
||||
#endif
|
||||
// Apply transforms to the hierarchical document structure
|
||||
|
||||
clear_transaction_xdata xact_cleaner;
|
||||
walk_entries(journal->entries, xact_cleaner);
|
||||
INFO_START(transforms, "Applied transforms");
|
||||
report.apply_transforms(doc_scope);
|
||||
INFO_FINISH(transforms);
|
||||
|
||||
clear_account_xdata acct_cleaner;
|
||||
walk_accounts(*journal->master, acct_cleaner);
|
||||
// Create an argument scope containing the report command's
|
||||
// arguments, and then invoke the command.
|
||||
|
||||
if (! report.output_file.empty())
|
||||
delete out;
|
||||
xml::xpath_t::call_scope_t command_args(doc_scope);
|
||||
|
||||
for (std::list<item_handler<transaction_t> *>::iterator i
|
||||
= formatter_ptrs.begin();
|
||||
i != formatter_ptrs.end();
|
||||
i++)
|
||||
delete *i;
|
||||
for (strings_list::iterator i = arg; i != args.end(); i++)
|
||||
command_args.push_back(value_t(*i, true));
|
||||
|
||||
#if 0
|
||||
TRACE_POP(cleanup, "Finished cleaning"); }
|
||||
#endif
|
||||
#endif
|
||||
INFO_START(command, "Did user command '" << verb << "'");
|
||||
|
||||
command(command_args);
|
||||
|
||||
INFO_FINISH(command);
|
||||
|
||||
// Write out the binary cache, if need be
|
||||
|
||||
if (config.use_cache && config.cache_dirty &&
|
||||
! config.cache_file.empty()) {
|
||||
#if 0
|
||||
TRACE_PUSH(binary_cache, "Writing journal file");
|
||||
#endif
|
||||
|
||||
ofstream stream(config.cache_file);
|
||||
binary::write_journal(stream, journal.get());
|
||||
if (session.use_cache && session.cache_dirty && session.cache_file) {
|
||||
TRACE_START(binary_cache, 1, "Wrote binary journal file");
|
||||
|
||||
#if 0
|
||||
TRACE_POP(binary_cache, "Finished writing");
|
||||
ofstream stream(*session.cache_file);
|
||||
write_binary_journal(stream, journal);
|
||||
#endif
|
||||
|
||||
TRACE_FINISH(binary_cache, 1);
|
||||
}
|
||||
|
||||
// If the user specified a pager, wait for it to exit now
|
||||
|
||||
#ifdef HAVE_UNIX_PIPES
|
||||
if (! config.pager.empty()) {
|
||||
delete out;
|
||||
if (! report.output_file && report.pager) {
|
||||
checked_delete(out);
|
||||
close(pfd[1]);
|
||||
|
||||
// Wait for child to finish
|
||||
wait(&status);
|
||||
if (status & 0xffff != 0)
|
||||
throw new error("Something went wrong in the pager");
|
||||
throw_(std::logic_error, "Something went wrong in the pager");
|
||||
}
|
||||
#endif
|
||||
else if (DO_VERIFY() && report.output_file) {
|
||||
checked_delete(out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[], char * envp[])
|
||||
{
|
||||
// This variable must be defined here so that any memory it holds is still
|
||||
// available should the subsequent exception handlers catch an inner
|
||||
// exception and need to report something on the invalid state of the
|
||||
// journal (such as an unbalanced entry).
|
||||
std::auto_ptr<journal_t> journal;
|
||||
int status = 1;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
if (argv[i][0] == '-') {
|
||||
if (std::strcmp(argv[i], "--verify") == 0) {
|
||||
#if defined(VERIFY_ON)
|
||||
ledger::verify_enabled = true;
|
||||
#endif
|
||||
}
|
||||
else if (std::strcmp(argv[i], "--verbose") == 0 ||
|
||||
std::strcmp(argv[i], "-v") == 0) {
|
||||
#if defined(LOGGING_ON)
|
||||
ledger::_log_level = ledger::LOG_INFO;
|
||||
#endif
|
||||
}
|
||||
else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
|
||||
#if defined(DEBUG_ON)
|
||||
ledger::_log_level = ledger::LOG_DEBUG;
|
||||
ledger::_log_category = argv[i + 1];
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
|
||||
#if defined(TRACING_ON)
|
||||
ledger::_log_level = ledger::LOG_TRACE;
|
||||
ledger::_trace_level = boost::lexical_cast<int>(argv[i + 1]);
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
IF_VERIFY()
|
||||
ledger::initialize_memory_tracing();
|
||||
|
||||
try {
|
||||
#if DEBUG_LEVEL < BETA
|
||||
ledger::do_cleanup = false;
|
||||
#endif
|
||||
config_t config;
|
||||
report_t report;
|
||||
ledger::config = &config;
|
||||
ledger::report = &report;
|
||||
std::ios::sync_with_stdio(false);
|
||||
|
||||
amount_t::initialize();
|
||||
value_t::initialize();
|
||||
boost::filesystem::path::default_name_check
|
||||
(boost::filesystem::portable_posix_name);
|
||||
|
||||
INFO("Ledger starting");
|
||||
|
||||
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
|
||||
|
||||
ledger::set_session_context(session.get());
|
||||
|
||||
#if 0
|
||||
TRACE_PUSH(main, "Ledger starting");
|
||||
session->register_parser(new binary_parser_t);
|
||||
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
||||
session->register_parser(new xml::xml_parser_t);
|
||||
session->register_parser(new gnucash_parser_t);
|
||||
#endif
|
||||
int status = parse_and_report(config, journal, report, argc, argv, envp);
|
||||
|
||||
value_t::shutdown();
|
||||
amount_t::shutdown();
|
||||
|
||||
#if 0
|
||||
TRACE_POP(main, "Ledger done");
|
||||
#ifdef HAVE_LIBOFX
|
||||
session->register_parser(new ofx_parser_t);
|
||||
#endif
|
||||
return status;
|
||||
session->register_parser(new qif_parser_t);
|
||||
#endif
|
||||
session->register_parser(new ledger::textual_parser_t);
|
||||
|
||||
std::auto_ptr<ledger::report_t> report(new ledger::report_t(*session.get()));
|
||||
|
||||
status = read_and_report(*report.get(), argc, argv, envp);
|
||||
|
||||
if (DO_VERIFY()) {
|
||||
ledger::set_session_context();
|
||||
} else {
|
||||
report.release();
|
||||
session.release();
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
catch (error * err) {
|
||||
std::cout.flush();
|
||||
// Push a null here since there's no file context
|
||||
|
|
@ -520,8 +447,7 @@ int main(int argc, char * argv[], char * envp[])
|
|||
err->context.push_front(new error_context(""));
|
||||
err->reveal_context(std::cerr, "Error");
|
||||
std::cerr << err->what() << std::endl;
|
||||
delete err;
|
||||
return 1;
|
||||
checked_delete(err);
|
||||
}
|
||||
catch (fatal * err) {
|
||||
std::cout.flush();
|
||||
|
|
@ -531,17 +457,25 @@ int main(int argc, char * argv[], char * envp[])
|
|||
err->context.push_front(new error_context(""));
|
||||
err->reveal_context(std::cerr, "Fatal");
|
||||
std::cerr << err->what() << std::endl;
|
||||
delete err;
|
||||
return 1;
|
||||
checked_delete(err);
|
||||
}
|
||||
#endif
|
||||
catch (const std::exception& err) {
|
||||
std::cout.flush();
|
||||
std::cerr << "Error: " << err.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
catch (int status) {
|
||||
return status;
|
||||
catch (int _status) {
|
||||
status = _status;
|
||||
}
|
||||
|
||||
IF_VERIFY() {
|
||||
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
|
||||
ledger::shutdown_memory_tracing();
|
||||
} else {
|
||||
INFO("Ledger ended");
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// main.cc ends here.
|
||||
|
|
|
|||
72
option.h
72
option.h
|
|
@ -1,45 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _OPTION_H
|
||||
#define _OPTION_H
|
||||
|
||||
#include "utils.h"
|
||||
#include "valexpr.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
typedef void (*handler_t)(const char * arg);
|
||||
void process_option(const string& name, expr::scope_t& scope,
|
||||
const char * arg = NULL);
|
||||
|
||||
struct option_t {
|
||||
const char * long_opt;
|
||||
char short_opt;
|
||||
bool wants_arg;
|
||||
handler_t handler;
|
||||
bool handled;
|
||||
};
|
||||
void process_environment(const char ** envp, const string& tag,
|
||||
expr::scope_t& scope);
|
||||
|
||||
void process_arguments(int argc, char ** argv, const bool anywhere,
|
||||
expr::scope_t& scope,
|
||||
std::list<string>& args);
|
||||
|
||||
DECLARE_EXCEPTION(error, option_error);
|
||||
|
||||
bool process_option(option_t * options, const string& opt,
|
||||
const char * arg = NULL);
|
||||
void process_arguments(option_t * options, int argc, char ** argv,
|
||||
const bool anywhere, std::list<string>& args);
|
||||
void process_environment(option_t * options, const char ** envp,
|
||||
const string& tag);
|
||||
|
||||
class config_t;
|
||||
class report_t;
|
||||
|
||||
extern config_t * config;
|
||||
extern report_t * report;
|
||||
|
||||
#define CONFIG_OPTIONS_SIZE 97
|
||||
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
||||
|
||||
void option_help(std::ostream& out);
|
||||
|
||||
#define OPT_BEGIN(tag, chars) \
|
||||
void opt_ ## tag(const char * optarg)
|
||||
|
||||
#define OPT_END(tag)
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _OPTION_H
|
||||
|
|
|
|||
14
parser.cc
14
parser.cc
|
|
@ -127,7 +127,7 @@ unsigned int parse_ledger_data(config_t& config,
|
|||
if (boost::filesystem::exists(config.cache_file)) {
|
||||
boost::filesystem::ifstream stream(config.cache_file);
|
||||
if (cache_parser && cache_parser->test(stream)) {
|
||||
path price_db_orig = journal->price_db;
|
||||
optional<path> price_db_orig = journal->price_db;
|
||||
journal->price_db = config.price_db;
|
||||
entry_count += cache_parser->parse(stream, config, journal,
|
||||
NULL, &config.data_file);
|
||||
|
|
@ -145,13 +145,13 @@ unsigned int parse_ledger_data(config_t& config,
|
|||
acct = journal->find_account(config.account);
|
||||
|
||||
journal->price_db = config.price_db;
|
||||
if (! journal->price_db.empty() &&
|
||||
boost::filesystem::exists(journal->price_db)) {
|
||||
if (parse_journal_file(journal->price_db, config, journal)) {
|
||||
if (journal->price_db &&
|
||||
boost::filesystem::exists(*journal->price_db)) {
|
||||
if (parse_journal_file(*journal->price_db, config, journal)) {
|
||||
throw new error("Entries not allowed in price history file");
|
||||
} else {
|
||||
DEBUG("ledger.config.cache",
|
||||
"read price database " << journal->price_db);
|
||||
"read price database " << *journal->price_db);
|
||||
journal->sources.pop_back();
|
||||
}
|
||||
}
|
||||
|
|
@ -172,8 +172,8 @@ unsigned int parse_ledger_data(config_t& config,
|
|||
else if (boost::filesystem::exists(config.data_file)) {
|
||||
entry_count += parse_journal_file(config.data_file, config, journal,
|
||||
acct);
|
||||
if (! journal->price_db.empty())
|
||||
journal->sources.push_back(journal->price_db);
|
||||
if (journal->price_db)
|
||||
journal->sources.push_back(*journal->price_db);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
589
report.cc
589
report.cc
|
|
@ -1,413 +1,232 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "report.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
report_t::report_t()
|
||||
report_t::~report_t()
|
||||
{
|
||||
ledger::amount_expr = "@a";
|
||||
ledger::total_expr = "@O";
|
||||
|
||||
predicate = "";
|
||||
secondary_predicate = "";
|
||||
display_predicate = "";
|
||||
descend_expr = "";
|
||||
|
||||
budget_flags = BUDGET_NO_BUDGET;
|
||||
|
||||
head_entries = 0;
|
||||
tail_entries = 0;
|
||||
|
||||
show_collapsed = false;
|
||||
show_subtotal = false;
|
||||
show_totals = false;
|
||||
show_related = false;
|
||||
show_all_related = false;
|
||||
show_inverted = false;
|
||||
show_empty = false;
|
||||
days_of_the_week = false;
|
||||
by_payee = false;
|
||||
comm_as_payee = false;
|
||||
code_as_payee = false;
|
||||
show_revalued = false;
|
||||
show_revalued_only = false;
|
||||
keep_price = false;
|
||||
keep_date = false;
|
||||
keep_tag = false;
|
||||
entry_sort = false;
|
||||
sort_all = false;
|
||||
TRACE_DTOR(report_t);
|
||||
}
|
||||
|
||||
void
|
||||
report_t::regexps_to_predicate(const string& command,
|
||||
std::list<string>::const_iterator begin,
|
||||
std::list<string>::const_iterator end,
|
||||
const bool account_regexp,
|
||||
const bool add_account_short_masks,
|
||||
const bool logical_and)
|
||||
void report_t::apply_transforms(expr::scope_t& scope)
|
||||
{
|
||||
string regexps[2];
|
||||
#if 0
|
||||
typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple;
|
||||
|
||||
assert(begin != end);
|
||||
|
||||
// Treat the remaining command-line arguments as regular
|
||||
// expressions, used for refining report results.
|
||||
|
||||
for (std::list<string>::const_iterator i = begin;
|
||||
i != end;
|
||||
i++)
|
||||
if ((*i)[0] == '-') {
|
||||
if (! regexps[1].empty())
|
||||
regexps[1] += "|";
|
||||
regexps[1] += (*i).substr(1);
|
||||
}
|
||||
else if ((*i)[0] == '+') {
|
||||
if (! regexps[0].empty())
|
||||
regexps[0] += "|";
|
||||
regexps[0] += (*i).substr(1);
|
||||
}
|
||||
else {
|
||||
if (! regexps[0].empty())
|
||||
regexps[0] += "|";
|
||||
regexps[0] += *i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (regexps[i].empty())
|
||||
continue;
|
||||
|
||||
if (! predicate.empty())
|
||||
predicate += logical_and ? "&" : "|";
|
||||
|
||||
int add_predicate = 0; // 1 adds /.../, 2 adds ///.../
|
||||
if (i == 1) {
|
||||
predicate += "!";
|
||||
}
|
||||
else if (add_account_short_masks) {
|
||||
if (regexps[i].find(':') != string::npos ||
|
||||
regexps[i].find('.') != string::npos ||
|
||||
regexps[i].find('*') != string::npos ||
|
||||
regexps[i].find('+') != string::npos ||
|
||||
regexps[i].find('[') != string::npos ||
|
||||
regexps[i].find('(') != string::npos) {
|
||||
show_subtotal = true;
|
||||
add_predicate = 1;
|
||||
} else {
|
||||
add_predicate = 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
add_predicate = 1;
|
||||
}
|
||||
|
||||
if (i != 1 && command == "b" && account_regexp) {
|
||||
if (! show_related && ! show_all_related) {
|
||||
if (! display_predicate.empty())
|
||||
display_predicate += "&";
|
||||
if (! show_empty)
|
||||
display_predicate += "T&";
|
||||
|
||||
if (add_predicate == 2)
|
||||
display_predicate += "//";
|
||||
display_predicate += "/(?:";
|
||||
display_predicate += regexps[i];
|
||||
display_predicate += ")/";
|
||||
}
|
||||
else if (! show_empty) {
|
||||
if (! display_predicate.empty())
|
||||
display_predicate += "&";
|
||||
display_predicate += "T";
|
||||
}
|
||||
}
|
||||
|
||||
if (! account_regexp)
|
||||
predicate += "/";
|
||||
predicate += "/(?:";
|
||||
predicate += regexps[i];
|
||||
predicate += ")/";
|
||||
foreach (transform_details_tuple& transform_details, transforms) {
|
||||
expr::call_scope_t call_args(scope);
|
||||
call_args.set_args(transform_details.get<1>());
|
||||
(*transform_details.get<0>())(call_args);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void report_t::process_options(const string& command,
|
||||
strings_list::iterator arg,
|
||||
strings_list::iterator args_end)
|
||||
value_t report_t::abbrev(expr::call_scope_t& args)
|
||||
{
|
||||
// Configure some other options depending on report type
|
||||
if (args.size() < 2)
|
||||
throw_(std::logic_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])");
|
||||
|
||||
if (command == "p" || command == "e" || command == "w") {
|
||||
show_related =
|
||||
show_all_related = true;
|
||||
}
|
||||
else if (command == "E") {
|
||||
show_subtotal = true;
|
||||
}
|
||||
else if (show_related) {
|
||||
if (command == "r") {
|
||||
show_inverted = true;
|
||||
} else {
|
||||
show_subtotal = true;
|
||||
show_all_related = true;
|
||||
string str = args[0].as_string();
|
||||
long wid = args[1];
|
||||
|
||||
#if 0
|
||||
elision_style_t style = session.elision_style;
|
||||
if (args.size() == 3)
|
||||
style = static_cast<elision_style_t>(args[2].as_long());
|
||||
#endif
|
||||
|
||||
long abbrev_len = session.abbrev_length;
|
||||
if (args.size() == 4)
|
||||
abbrev_len = args[3].as_long();
|
||||
|
||||
#if 0
|
||||
return value_t(abbreviate(str, wid, style, true,
|
||||
static_cast<int>(abbrev_len)), true);
|
||||
#else
|
||||
return value_t();
|
||||
#endif
|
||||
}
|
||||
|
||||
value_t report_t::ftime(expr::call_scope_t& args)
|
||||
{
|
||||
if (args.size() < 1)
|
||||
throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])");
|
||||
|
||||
datetime_t date = args[0].as_datetime();
|
||||
|
||||
string date_format;
|
||||
if (args.size() == 2)
|
||||
date_format = args[1].as_string();
|
||||
#if 0
|
||||
// jww (2007-04-18): Need to setup an output facet here
|
||||
else
|
||||
date_format = moment_t::output_format;
|
||||
|
||||
return value_t(date.as_string(date_format), true);
|
||||
#else
|
||||
return NULL_VALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
optional<value_t>
|
||||
report_t::resolve(const string& name, expr::call_scope_t& args)
|
||||
{
|
||||
const char * p = name.c_str();
|
||||
switch (*p) {
|
||||
case 'a':
|
||||
if (name == "abbrev") {
|
||||
return abbrev(args);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if (name == "ftime") {
|
||||
return ftime(args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return expr::scope_t::resolve(name, args);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (command != "b" && command != "r")
|
||||
amount_t::keep_base = true;
|
||||
|
||||
// Process remaining command-line arguments
|
||||
|
||||
if (command != "e") {
|
||||
// Treat the remaining command-line arguments as regular
|
||||
// expressions, used for refining report results.
|
||||
|
||||
std::list<string>::iterator i = arg;
|
||||
for (; i != args_end; i++)
|
||||
if (*i == "--")
|
||||
expr::ptr_op_t report_t::lookup(const string& name)
|
||||
{
|
||||
const char * p = name.c_str();
|
||||
switch (*p) {
|
||||
case 'o':
|
||||
if (std::strncmp(p, "option_", 7) == 0) {
|
||||
p = p + 7;
|
||||
switch (*p) {
|
||||
case 'a':
|
||||
#if 0
|
||||
if (std::strcmp(p, "accounts") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_accounts);
|
||||
else
|
||||
#endif
|
||||
if (std::strcmp(p, "amount") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_amount);
|
||||
break;
|
||||
|
||||
if (i != arg)
|
||||
regexps_to_predicate(command, arg, i, true,
|
||||
(command == "b" && ! show_subtotal &&
|
||||
display_predicate.empty()));
|
||||
if (i != args_end && ++i != args_end)
|
||||
regexps_to_predicate(command, i, args_end);
|
||||
}
|
||||
case 'b':
|
||||
if (std::strcmp(p, "bar") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_bar);
|
||||
break;
|
||||
|
||||
// Setup the default value for the display predicate
|
||||
#if 0
|
||||
case 'c':
|
||||
if (std::strcmp(p, "clean") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_clean);
|
||||
else if (std::strcmp(p, "compact") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_compact);
|
||||
break;
|
||||
#endif
|
||||
|
||||
if (display_predicate.empty()) {
|
||||
if (command == "b") {
|
||||
if (! show_empty)
|
||||
display_predicate = "T";
|
||||
if (! show_subtotal) {
|
||||
if (! display_predicate.empty())
|
||||
display_predicate += "&";
|
||||
display_predicate += "l<=1";
|
||||
case 'e':
|
||||
#if 0
|
||||
if (std::strcmp(p, "entries") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_entries);
|
||||
else if (std::strcmp(p, "eval") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_eval);
|
||||
else if (std::strcmp(p, "exclude") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_remove);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
#if 0
|
||||
if (std::strcmp(p, "foo") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_foo);
|
||||
else
|
||||
#endif
|
||||
if (std::strcmp(p, "format") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_format);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
#if 0
|
||||
if (std::strcmp(p, "include") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_select);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
#if 0
|
||||
if (! *(p + 1) || std::strcmp(p, "limit") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_limit);
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case 'm':
|
||||
if (std::strcmp(p, "merge") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_merge);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'r':
|
||||
#if 0
|
||||
if (std::strcmp(p, "remove") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_remove);
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case 's':
|
||||
if (std::strcmp(p, "select") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_select);
|
||||
else if (std::strcmp(p, "split") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_split);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 't':
|
||||
if (! *(p + 1))
|
||||
return MAKE_FUNCTOR(report_t::option_amount);
|
||||
else if (std::strcmp(p, "total") == 0)
|
||||
return MAKE_FUNCTOR(report_t::option_total);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if (! *(p + 1))
|
||||
return MAKE_FUNCTOR(report_t::option_total);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (command == "E") {
|
||||
display_predicate = "t";
|
||||
}
|
||||
else if (command == "r" && ! show_empty) {
|
||||
display_predicate = "a";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG("ledger.config.predicates", "Predicate: " << predicate);
|
||||
DEBUG("ledger.config.predicates", "Display P: " << display_predicate);
|
||||
|
||||
// Setup the values of %t and %T, used in format strings
|
||||
|
||||
if (! amount_expr.empty())
|
||||
ledger::amount_expr = amount_expr;
|
||||
if (! total_expr.empty())
|
||||
ledger::total_expr = total_expr;
|
||||
|
||||
// Now setup the various formatting strings
|
||||
|
||||
if (! date_output_format.empty())
|
||||
output_time_format = date_output_format;
|
||||
|
||||
amount_t::keep_price = keep_price;
|
||||
amount_t::keep_date = keep_date;
|
||||
amount_t::keep_tag = keep_tag;
|
||||
|
||||
if (! report_period.empty() && ! sort_all)
|
||||
entry_sort = true;
|
||||
}
|
||||
|
||||
item_handler<transaction_t> *
|
||||
report_t::chain_xact_handlers(const string& command,
|
||||
item_handler<transaction_t> * base_formatter,
|
||||
journal_t * journal,
|
||||
account_t * master,
|
||||
std::list<item_handler<transaction_t> *>& ptrs)
|
||||
{
|
||||
bool remember_components = false;
|
||||
|
||||
item_handler<transaction_t> * formatter = NULL;
|
||||
|
||||
ptrs.push_back(formatter = base_formatter);
|
||||
|
||||
// format_transactions write each transaction received to the
|
||||
// output stream.
|
||||
if (! (command == "b" || command == "E")) {
|
||||
// truncate_entries cuts off a certain number of _entries_ from
|
||||
// being displayed. It does not affect calculation.
|
||||
if (head_entries || tail_entries)
|
||||
ptrs.push_back(formatter =
|
||||
new truncate_entries(formatter,
|
||||
head_entries, tail_entries));
|
||||
|
||||
// filter_transactions will only pass through transactions
|
||||
// matching the `display_predicate'.
|
||||
if (! display_predicate.empty())
|
||||
ptrs.push_back(formatter =
|
||||
new filter_transactions(formatter,
|
||||
display_predicate));
|
||||
|
||||
// calc_transactions computes the running total. When this
|
||||
// appears will determine, for example, whether filtered
|
||||
// transactions are included or excluded from the running total.
|
||||
ptrs.push_back(formatter = new calc_transactions(formatter));
|
||||
|
||||
// component_transactions looks for reported transaction that
|
||||
// match the given `descend_expr', and then reports the
|
||||
// transactions which made up the total for that reported
|
||||
// transaction.
|
||||
if (! descend_expr.empty()) {
|
||||
std::list<string> descend_exprs;
|
||||
|
||||
string::size_type beg = 0;
|
||||
for (string::size_type pos = descend_expr.find(';');
|
||||
pos != string::npos;
|
||||
beg = pos + 1, pos = descend_expr.find(';', beg))
|
||||
descend_exprs.push_back(string(descend_expr, beg, pos - beg));
|
||||
descend_exprs.push_back(string(descend_expr, beg));
|
||||
|
||||
for (std::list<string>::reverse_iterator i =
|
||||
descend_exprs.rbegin();
|
||||
i != descend_exprs.rend();
|
||||
i++)
|
||||
ptrs.push_back(formatter =
|
||||
new component_transactions(formatter, *i));
|
||||
|
||||
remember_components = true;
|
||||
}
|
||||
|
||||
// reconcile_transactions will pass through only those
|
||||
// transactions which can be reconciled to a given balance
|
||||
// (calculated against the transactions which it receives).
|
||||
if (! reconcile_balance.empty()) {
|
||||
datetime_t cutoff = current_moment;
|
||||
if (! reconcile_date.empty())
|
||||
cutoff = parse_datetime(reconcile_date);
|
||||
ptrs.push_back(formatter =
|
||||
new reconcile_transactions
|
||||
(formatter, value_t(reconcile_balance), cutoff));
|
||||
}
|
||||
|
||||
// filter_transactions will only pass through transactions
|
||||
// matching the `secondary_predicate'.
|
||||
if (! secondary_predicate.empty())
|
||||
ptrs.push_back(formatter =
|
||||
new filter_transactions(formatter,
|
||||
secondary_predicate));
|
||||
|
||||
// sort_transactions will sort all the transactions it sees, based
|
||||
// on the `sort_order' value expression.
|
||||
if (! sort_string.empty()) {
|
||||
if (entry_sort)
|
||||
ptrs.push_back(formatter =
|
||||
new sort_entries(formatter, sort_string));
|
||||
else
|
||||
ptrs.push_back(formatter =
|
||||
new sort_transactions(formatter, sort_string));
|
||||
}
|
||||
|
||||
// changed_value_transactions adds virtual transactions to the
|
||||
// list to account for changes in market value of commodities,
|
||||
// which otherwise would affect the running total unpredictably.
|
||||
if (show_revalued)
|
||||
ptrs.push_back(formatter =
|
||||
new changed_value_transactions(formatter,
|
||||
show_revalued_only));
|
||||
|
||||
// collapse_transactions causes entries with multiple transactions
|
||||
// to appear as entries with a subtotaled transaction for each
|
||||
// commodity used.
|
||||
if (show_collapsed)
|
||||
ptrs.push_back(formatter = new collapse_transactions(formatter));
|
||||
|
||||
// subtotal_transactions combines all the transactions it receives
|
||||
// into one subtotal entry, which has one transaction for each
|
||||
// commodity in each account.
|
||||
//
|
||||
// period_transactions is like subtotal_transactions, but it
|
||||
// subtotals according to time periods rather than totalling
|
||||
// everything.
|
||||
//
|
||||
// dow_transactions is like period_transactions, except that it
|
||||
// reports all the transactions that fall on each subsequent day
|
||||
// of the week.
|
||||
if (show_subtotal)
|
||||
ptrs.push_back(formatter =
|
||||
new subtotal_transactions(formatter, remember_components));
|
||||
|
||||
if (days_of_the_week)
|
||||
ptrs.push_back(formatter =
|
||||
new dow_transactions(formatter, remember_components));
|
||||
else if (by_payee)
|
||||
ptrs.push_back(formatter =
|
||||
new by_payee_transactions(formatter, remember_components));
|
||||
|
||||
// interval_transactions groups transactions together based on a
|
||||
// time period, such as weekly or monthly.
|
||||
if (! report_period.empty()) {
|
||||
ptrs.push_back(formatter =
|
||||
new interval_transactions(formatter, report_period,
|
||||
remember_components));
|
||||
ptrs.push_back(formatter = new sort_transactions(formatter, "d"));
|
||||
}
|
||||
}
|
||||
|
||||
// invert_transactions inverts the value of the transactions it
|
||||
// receives.
|
||||
if (show_inverted)
|
||||
ptrs.push_back(formatter = new invert_transactions(formatter));
|
||||
|
||||
// related_transactions will pass along all transactions related
|
||||
// to the transaction received. If `show_all_related' is true,
|
||||
// then all the entry's transactions are passed; meaning that if
|
||||
// one transaction of an entry is to be printed, all the
|
||||
// transaction for that entry will be printed.
|
||||
if (show_related)
|
||||
ptrs.push_back(formatter =
|
||||
new related_transactions(formatter,
|
||||
show_all_related));
|
||||
|
||||
// This filter_transactions will only pass through transactions
|
||||
// matching the `predicate'.
|
||||
if (! predicate.empty())
|
||||
ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
|
||||
|
||||
// budget_transactions takes a set of transactions from a data
|
||||
// file and uses them to generate "budget transactions" which
|
||||
// balance against the reported transactions.
|
||||
//
|
||||
// forecast_transactions is a lot like budget_transactions, except
|
||||
// that it adds entries only for the future, and does not balance
|
||||
// them against anything but the future balance.
|
||||
|
||||
if (budget_flags) {
|
||||
budget_transactions * handler
|
||||
= new budget_transactions(formatter, budget_flags);
|
||||
handler->add_period_entries(journal->period_entries);
|
||||
ptrs.push_back(formatter = handler);
|
||||
|
||||
// Apply this before the budget handler, so that only matching
|
||||
// transactions are calculated toward the budget. The use of
|
||||
// filter_transactions above will further clean the results so
|
||||
// that no automated transactions that don't match the filter get
|
||||
// reported.
|
||||
if (! predicate.empty())
|
||||
ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
|
||||
}
|
||||
else if (! forecast_limit.empty()) {
|
||||
forecast_transactions * handler
|
||||
= new forecast_transactions(formatter, forecast_limit);
|
||||
handler->add_period_entries(journal->period_entries);
|
||||
ptrs.push_back(formatter = handler);
|
||||
|
||||
// See above, under budget_transactions.
|
||||
if (! predicate.empty())
|
||||
ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
|
||||
}
|
||||
|
||||
if (comm_as_payee)
|
||||
ptrs.push_back(formatter = new set_comm_as_payee(formatter));
|
||||
else if (code_as_payee)
|
||||
ptrs.push_back(formatter = new set_code_as_payee(formatter));
|
||||
|
||||
return formatter;
|
||||
return expr::symbol_scope_t::lookup(name);
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
|
|
|
|||
233
report.h
233
report.h
|
|
@ -1,79 +1,194 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _REPORT_H
|
||||
#define _REPORT_H
|
||||
|
||||
#include "ledger.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include "session.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
class report_t
|
||||
typedef std::list<string> strings_list;
|
||||
|
||||
class report_t : public expr::symbol_scope_t
|
||||
{
|
||||
public:
|
||||
path output_file;
|
||||
public:
|
||||
optional<path> output_file;
|
||||
string format_string;
|
||||
string amount_expr;
|
||||
string total_expr;
|
||||
string date_output_format;
|
||||
|
||||
string predicate;
|
||||
string secondary_predicate;
|
||||
string display_predicate;
|
||||
string report_period;
|
||||
string report_period_sort;
|
||||
string format_string;
|
||||
string sort_string;
|
||||
string amount_expr;
|
||||
string total_expr;
|
||||
string descend_expr;
|
||||
string forecast_limit;
|
||||
string reconcile_balance;
|
||||
string reconcile_date;
|
||||
string date_output_format;
|
||||
unsigned long budget_flags;
|
||||
|
||||
unsigned long budget_flags;
|
||||
string account;
|
||||
optional<path> pager;
|
||||
|
||||
int head_entries;
|
||||
int tail_entries;
|
||||
bool show_totals;
|
||||
bool raw_mode;
|
||||
|
||||
bool show_collapsed;
|
||||
bool show_subtotal;
|
||||
bool show_totals;
|
||||
bool show_related;
|
||||
bool show_all_related;
|
||||
bool show_inverted;
|
||||
bool show_empty;
|
||||
bool days_of_the_week;
|
||||
bool by_payee;
|
||||
bool comm_as_payee;
|
||||
bool code_as_payee;
|
||||
bool show_revalued;
|
||||
bool show_revalued_only;
|
||||
bool keep_price;
|
||||
bool keep_date;
|
||||
bool keep_tag;
|
||||
bool entry_sort;
|
||||
bool sort_all;
|
||||
session_t& session;
|
||||
#if 0
|
||||
transform_t * last_transform;
|
||||
|
||||
report_t();
|
||||
std::list<tuple<shared_ptr<transform_t>, value_t> > transforms;
|
||||
#endif
|
||||
|
||||
void regexps_to_predicate(const string& command,
|
||||
std::list<string>::const_iterator begin,
|
||||
std::list<string>::const_iterator end,
|
||||
const bool account_regexp = false,
|
||||
const bool add_account_short_masks = false,
|
||||
const bool logical_and = true);
|
||||
explicit report_t(session_t& _session)
|
||||
: expr::symbol_scope_t(downcast<expr::scope_t>(_session)),
|
||||
show_totals(false),
|
||||
raw_mode(false),
|
||||
session(_session)
|
||||
#if 0
|
||||
,
|
||||
last_transform(NULL)
|
||||
#endif
|
||||
{
|
||||
TRACE_CTOR(report_t, "session_t&");
|
||||
#if 0
|
||||
eval("t=total,TOT=0,T()=(TOT=TOT+t,TOT)");
|
||||
#endif
|
||||
}
|
||||
|
||||
void process_options(const string& command,
|
||||
strings_list::iterator arg,
|
||||
strings_list::iterator args_end);
|
||||
virtual ~report_t();
|
||||
|
||||
item_handler<transaction_t> *
|
||||
chain_xact_handlers(const string& command,
|
||||
item_handler<transaction_t> * base_formatter,
|
||||
journal_t * journal,
|
||||
account_t * master,
|
||||
std::list<item_handler<transaction_t> *>& ptrs);
|
||||
void apply_transforms(expr::scope_t& scope);
|
||||
|
||||
//
|
||||
// Utility functions for value expressions
|
||||
//
|
||||
|
||||
value_t ftime(expr::call_scope_t& args);
|
||||
value_t abbrev(expr::call_scope_t& args);
|
||||
|
||||
//
|
||||
// Config options
|
||||
//
|
||||
|
||||
void eval(const string& expr) {
|
||||
#if 0
|
||||
expr(expr).compile((xml::document_t *)NULL, this);
|
||||
#endif
|
||||
}
|
||||
value_t option_eval(expr::call_scope_t& args) {
|
||||
eval(args[0].as_string());
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_amount(expr::call_scope_t& args) {
|
||||
eval(string("t=") + args[0].as_string());
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_total(expr::call_scope_t& args) {
|
||||
eval(string("T()=") + args[0].as_string());
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_format(expr::call_scope_t& args) {
|
||||
format_string = args[0].as_string();
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_raw(expr::call_scope_t& args) {
|
||||
raw_mode = true;
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_foo(expr::call_scope_t& args) {
|
||||
std::cout << "This is foo" << std::endl;
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_bar(expr::call_scope_t& args) {
|
||||
std::cout << "This is bar: " << args[0] << std::endl;
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
//
|
||||
// Transform options
|
||||
//
|
||||
|
||||
#if 0
|
||||
value_t option_select(expr::call_scope_t& args) {
|
||||
transforms.push_back(new select_transform(args[0].as_string()));
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_limit(expr::call_scope_t& args) {
|
||||
string expr = (string("//xact[") +
|
||||
args[0].as_string() + "]");
|
||||
transforms.push_back(new select_transform(expr));
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_remove(expr::call_scope_t& args) {
|
||||
transforms.push_back(new remove_transform(args[0].as_string()));
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_accounts(expr::call_scope_t& args) {
|
||||
transforms.push_back(new accounts_transform);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_compact(expr::call_scope_t& args) {
|
||||
transforms.push_back(new compact_transform);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_clean(expr::call_scope_t& args) {
|
||||
transforms.push_back(new clean_transform);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_entries(expr::call_scope_t& args) {
|
||||
transforms.push_back(new entries_transform);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_split(expr::call_scope_t& args) {
|
||||
transforms.push_back(new split_transform);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_merge(expr::call_scope_t& args) {
|
||||
transforms.push_back(new merge_transform);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Scope members
|
||||
//
|
||||
|
||||
virtual expr::ptr_op_t lookup(const string& name);
|
||||
};
|
||||
|
||||
string abbrev(const string& str, unsigned int width,
|
||||
const bool is_account);
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _REPORT_H
|
||||
|
|
|
|||
313
session.cc
Normal file
313
session.cc
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "session.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
session_t * session_t::current = NULL;
|
||||
|
||||
#if 0
|
||||
boost::mutex session_t::session_mutex;
|
||||
#endif
|
||||
|
||||
static void initialize();
|
||||
static void shutdown();
|
||||
|
||||
void set_session_context(session_t * session)
|
||||
{
|
||||
#if 0
|
||||
session_t::session_mutex.lock();
|
||||
#endif
|
||||
|
||||
if (session && ! session_t::current) {
|
||||
initialize();
|
||||
}
|
||||
else if (! session && session_t::current) {
|
||||
shutdown();
|
||||
#if 0
|
||||
session_t::session_mutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
session_t::current = session;
|
||||
}
|
||||
|
||||
void release_session_context()
|
||||
{
|
||||
#if 0
|
||||
session_t::session_mutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
session_t::session_t()
|
||||
: symbol_scope_t(),
|
||||
|
||||
register_format
|
||||
("%((//entry)%{date} %-.20{payee}"
|
||||
"%((./xact)%32|%-22{abbrev(account, 22)} %12.67t %12.80T\n))"),
|
||||
wide_register_format
|
||||
("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
|
||||
"%48|%-.38A %22.108t %!22.132T\n"),
|
||||
print_format
|
||||
#if 1
|
||||
("%(/%(/%{date} %-.20{payee}\n%(: %-34{account} %12t\n)\n))"),
|
||||
#else
|
||||
("\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"),
|
||||
#endif
|
||||
balance_format
|
||||
("%(/%(//%20t %{\" \" * rdepth}%{rname}\n))--------------------\n%20t\n"),
|
||||
equity_format
|
||||
|
||||
("%((/)%{ftime(now, date_format)} %-.20{\"Opening Balance\"}\n%((.//account[value != 0]) %-34{fullname} %12{value}\n)\n)"),
|
||||
plot_amount_format
|
||||
("%D %(@S(@t))\n"),
|
||||
plot_total_format
|
||||
("%D %(@S(@T))\n"),
|
||||
write_hdr_format
|
||||
("%d %Y%C%P\n"),
|
||||
write_xact_format
|
||||
(" %-34W %12o%n\n"),
|
||||
prices_format
|
||||
("%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"),
|
||||
pricesdb_format
|
||||
("P %[%Y/%m/%d %H:%M:%S] %A %t\n"),
|
||||
|
||||
pricing_leeway(24 * 3600),
|
||||
|
||||
download_quotes(false),
|
||||
use_cache(false),
|
||||
cache_dirty(false),
|
||||
|
||||
now(now),
|
||||
|
||||
elision_style(ABBREVIATE),
|
||||
abbrev_length(2),
|
||||
|
||||
ansi_codes(false),
|
||||
ansi_invert(false)
|
||||
{
|
||||
TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
|
||||
}
|
||||
|
||||
std::size_t session_t::read_journal(std::istream& in,
|
||||
const path& pathname,
|
||||
xml::builder_t& builder)
|
||||
{
|
||||
#if 0
|
||||
if (! master)
|
||||
master = journal->master;
|
||||
#endif
|
||||
|
||||
foreach (parser_t& parser, parsers)
|
||||
if (parser.test(in))
|
||||
return parser.parse(in, pathname, builder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t session_t::read_journal(const path& pathname,
|
||||
xml::builder_t& builder)
|
||||
{
|
||||
#if 0
|
||||
journal->sources.push_back(pathname);
|
||||
#endif
|
||||
|
||||
if (! exists(pathname))
|
||||
throw_(std::logic_error, "Cannot read file" << pathname);
|
||||
|
||||
ifstream stream(pathname);
|
||||
return read_journal(stream, pathname, builder);
|
||||
}
|
||||
|
||||
void session_t::read_init()
|
||||
{
|
||||
if (! init_file)
|
||||
return;
|
||||
|
||||
if (! exists(*init_file))
|
||||
throw_(std::logic_error, "Cannot read init file" << *init_file);
|
||||
|
||||
ifstream init(*init_file);
|
||||
|
||||
// jww (2006-09-15): Read initialization options here!
|
||||
}
|
||||
|
||||
std::size_t session_t::read_data(xml::builder_t& builder,
|
||||
journal_t * journal,
|
||||
const string& master_account)
|
||||
{
|
||||
if (data_file.empty())
|
||||
throw_(parse_error, "No journal file was specified (please use -f)");
|
||||
|
||||
TRACE_START(parser, 1, "Parsing journal file");
|
||||
|
||||
std::size_t entry_count = 0;
|
||||
|
||||
DEBUG("ledger.cache", "3. use_cache = " << use_cache);
|
||||
|
||||
if (use_cache && cache_file) {
|
||||
DEBUG("ledger.cache", "using_cache " << cache_file->string());
|
||||
cache_dirty = true;
|
||||
if (exists(*cache_file)) {
|
||||
push_variable<optional<path> >
|
||||
save_price_db(journal->price_db, price_db);
|
||||
|
||||
entry_count += read_journal(*cache_file, builder);
|
||||
if (entry_count > 0)
|
||||
cache_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry_count == 0) {
|
||||
account_t * acct = NULL;
|
||||
if (! master_account.empty())
|
||||
acct = journal->find_account(master_account);
|
||||
|
||||
journal->price_db = price_db;
|
||||
if (journal->price_db && exists(*journal->price_db)) {
|
||||
if (read_journal(*journal->price_db, builder)) {
|
||||
throw_(parse_error, "Entries not allowed in price history file");
|
||||
} else {
|
||||
DEBUG("ledger.cache",
|
||||
"read price database " << journal->price_db->string());
|
||||
journal->sources.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("ledger.cache", "rejected cache, parsing " << data_file.string());
|
||||
if (data_file == "-") {
|
||||
use_cache = false;
|
||||
journal->sources.push_back("<stdin>");
|
||||
entry_count += read_journal(std::cin, "<stdin>", builder);
|
||||
}
|
||||
else if (exists(data_file)) {
|
||||
entry_count += read_journal(data_file, builder);
|
||||
if (journal->price_db)
|
||||
journal->sources.push_back(*journal->price_db);
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY(journal->valid());
|
||||
|
||||
TRACE_STOP(parser, 1);
|
||||
|
||||
return entry_count;
|
||||
}
|
||||
|
||||
#if 0
|
||||
optional<value_t>
|
||||
session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
|
||||
{
|
||||
const char * p = name.c_str();
|
||||
switch (*p) {
|
||||
case 'd':
|
||||
#if 0
|
||||
if (name == "date_format") {
|
||||
// jww (2007-04-18): What to do here?
|
||||
return value_t(moment_t::output_format, true);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
switch (*++p) {
|
||||
case 'o':
|
||||
if (name == "now")
|
||||
return value_t(now);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
if (name == "register_format")
|
||||
return value_t(register_format, true);
|
||||
break;
|
||||
}
|
||||
return xml::xpath_t::scope_t::resolve(name, locals);
|
||||
}
|
||||
#endif
|
||||
|
||||
xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
|
||||
{
|
||||
const char * p = name.c_str();
|
||||
switch (*p) {
|
||||
case 'o':
|
||||
if (std::strncmp(p, "option_", 7) == 0) {
|
||||
p = p + 7;
|
||||
switch (*p) {
|
||||
case 'd':
|
||||
if (std::strcmp(p, "debug_") == 0)
|
||||
return MAKE_FUNCTOR(session_t::option_debug_);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if ((*(p + 1) == '_' && ! *(p + 2)) ||
|
||||
std::strcmp(p, "file_") == 0)
|
||||
return MAKE_FUNCTOR(session_t::option_file_);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if (std::strcmp(p, "trace_") == 0)
|
||||
return MAKE_FUNCTOR(session_t::option_trace_);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
if (! *(p + 1) || std::strcmp(p, "verbose") == 0)
|
||||
return MAKE_FUNCTOR(session_t::option_verbose);
|
||||
else if (std::strcmp(p, "verify") == 0)
|
||||
return MAKE_FUNCTOR(session_t::option_verify);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return xml::xpath_t::symbol_scope_t::lookup(name);
|
||||
}
|
||||
|
||||
// jww (2007-04-26): All of Ledger should be accessed through a
|
||||
// session_t object
|
||||
static void initialize()
|
||||
{
|
||||
amount_t::initialize();
|
||||
value_t::initialize();
|
||||
xml::xpath_t::initialize();
|
||||
}
|
||||
|
||||
static void shutdown()
|
||||
{
|
||||
xml::xpath_t::shutdown();
|
||||
value_t::shutdown();
|
||||
amount_t::shutdown();
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
198
session.h
Normal file
198
session.h
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SESSION_H
|
||||
#define _SESSION_H
|
||||
|
||||
#include "valexpr.h"
|
||||
#include "journal.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
class session_t : public expr::symbol_scope_t
|
||||
{
|
||||
public:
|
||||
static session_t * current;
|
||||
|
||||
path data_file;
|
||||
optional<path> init_file;
|
||||
optional<path> cache_file;
|
||||
optional<path> price_db;
|
||||
|
||||
string register_format;
|
||||
string wide_register_format;
|
||||
string print_format;
|
||||
string balance_format;
|
||||
string equity_format;
|
||||
string plot_amount_format;
|
||||
string plot_total_format;
|
||||
string write_hdr_format;
|
||||
string write_xact_format;
|
||||
string prices_format;
|
||||
string pricesdb_format;
|
||||
|
||||
unsigned long pricing_leeway;
|
||||
|
||||
bool download_quotes;
|
||||
bool use_cache;
|
||||
bool cache_dirty;
|
||||
|
||||
datetime_t now;
|
||||
|
||||
#if 0
|
||||
elision_style_t elision_style;
|
||||
#endif
|
||||
int abbrev_length;
|
||||
|
||||
bool ansi_codes;
|
||||
bool ansi_invert;
|
||||
|
||||
ptr_list<journal_t> journals;
|
||||
ptr_list<parser_t> parsers;
|
||||
|
||||
session_t();
|
||||
virtual ~session_t() {
|
||||
TRACE_DTOR(session_t);
|
||||
}
|
||||
|
||||
journal_t * create_journal() {
|
||||
journal_t * journal = new journal_t;
|
||||
journals.push_back(journal);
|
||||
return journal;
|
||||
}
|
||||
void close_journal(journal_t * journal) {
|
||||
for (ptr_list<journal_t>::iterator i = journals.begin();
|
||||
i != journals.end();
|
||||
i++)
|
||||
if (&*i == journal) {
|
||||
journals.erase(i);
|
||||
return;
|
||||
}
|
||||
assert(false);
|
||||
checked_delete(journal);
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::size_t read_journal(std::istream& in,
|
||||
const path& pathname,
|
||||
xml::builder_t& builder);
|
||||
std::size_t read_journal(const path& pathname,
|
||||
xml::builder_t& builder);
|
||||
|
||||
void read_init();
|
||||
|
||||
std::size_t read_data(xml::builder_t& builder,
|
||||
journal_t * journal,
|
||||
const string& master_account = "");
|
||||
#endif
|
||||
|
||||
void register_parser(parser_t * parser) {
|
||||
parsers.push_back(parser);
|
||||
}
|
||||
void unregister_parser(parser_t * parser) {
|
||||
for (ptr_list<parser_t>::iterator i = parsers.begin();
|
||||
i != parsers.end();
|
||||
i++)
|
||||
if (&*i == parser) {
|
||||
parsers.erase(i);
|
||||
return;
|
||||
}
|
||||
assert(false);
|
||||
checked_delete(parser);
|
||||
}
|
||||
|
||||
//
|
||||
// Scope members
|
||||
//
|
||||
|
||||
virtual expr::ptr_op_t lookup(const string& name);
|
||||
|
||||
//
|
||||
// Debug options
|
||||
//
|
||||
|
||||
value_t option_trace_(expr::scope_t& locals) {
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_debug_(expr::scope_t& locals) {
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
value_t option_verify(expr::scope_t&) {
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_verbose(expr::scope_t&) {
|
||||
#if defined(LOGGING_ON)
|
||||
if (_log_level < LOG_INFO)
|
||||
_log_level = LOG_INFO;
|
||||
#endif
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
//
|
||||
// Option handlers
|
||||
//
|
||||
|
||||
value_t option_file_(expr::call_scope_t& args) {
|
||||
assert(args.size() == 1);
|
||||
data_file = args[0].as_string();
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if defined(USE_BOOST_PYTHON)
|
||||
value_t option_import_(expr::call_scope_t& args) {
|
||||
python_import(optarg);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
value_t option_import_stdin(expr::call_scope_t& args) {
|
||||
python_eval(std::cin, PY_EVAL_MULTI);
|
||||
return NULL_VALUE;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* This sets the current session context, transferring all static
|
||||
* globals to point at the data structures related to this session.
|
||||
* Although Ledger itself is not thread-safe, by locking, switching
|
||||
* session context, then unlocking after the operation is done,
|
||||
* multiple threads can sequentially make use of the library. Thus, a
|
||||
* session_t maintains all of the information relating to a single
|
||||
* usage of the Ledger library.
|
||||
*/
|
||||
void set_session_context(session_t * session = NULL);
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _SESSION_H
|
||||
|
|
@ -64,7 +64,7 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount,
|
|||
throw new parse_error("Amount expression failed to compute");
|
||||
|
||||
#if 0
|
||||
if (expr->kind == value_expr_t::CONSTANT) {
|
||||
if (expr->kind == expr::node_t::CONSTANT) {
|
||||
expr = NULL;
|
||||
} else {
|
||||
DEBUG_IF("ledger.textual.parse") {
|
||||
|
|
|
|||
606
valexpr.cc
606
valexpr.cc
File diff suppressed because it is too large
Load diff
698
valexpr.h
698
valexpr.h
|
|
@ -13,9 +13,59 @@ class entry_t;
|
|||
class transaction_t;
|
||||
class account_t;
|
||||
|
||||
namespace expr {
|
||||
|
||||
#if 0
|
||||
struct context_t
|
||||
{
|
||||
const entry_t * entry() {
|
||||
return NULL;
|
||||
}
|
||||
const transaction_t * xact() {
|
||||
return NULL;
|
||||
}
|
||||
const account_t * account() {
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
struct entry_context_t : public context_t
|
||||
{
|
||||
const entry_t * entry_;
|
||||
|
||||
const entry_t * entry() {
|
||||
return entry_;
|
||||
}
|
||||
};
|
||||
|
||||
struct xact_context_t : public context_t
|
||||
{
|
||||
const transaction_t * xact_;
|
||||
|
||||
const entry_t * entry() {
|
||||
return xact_->entry;
|
||||
}
|
||||
const transaction_t * xact() {
|
||||
return xact_;
|
||||
}
|
||||
const account_t * account() {
|
||||
return xact_->account;
|
||||
}
|
||||
};
|
||||
|
||||
struct account_context_t : public context_t
|
||||
{
|
||||
const account_t * account_;
|
||||
|
||||
const account_t * account() {
|
||||
return account_;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
struct details_t
|
||||
{
|
||||
const entry_t * entry;
|
||||
const entry_t * entry;
|
||||
const transaction_t * xact;
|
||||
const account_t * account;
|
||||
|
||||
|
|
@ -36,11 +86,217 @@ struct details_t
|
|||
#endif
|
||||
};
|
||||
|
||||
struct value_expr_t
|
||||
struct op_t;
|
||||
typedef intrusive_ptr<op_t> ptr_op_t;
|
||||
|
||||
class call_scope_t;
|
||||
|
||||
typedef function<value_t (call_scope_t&)> function_t;
|
||||
|
||||
#define MAKE_FUNCTOR(x) expr::op_t::wrap_functor(bind(&x, this, _1))
|
||||
#define WRAP_FUNCTOR(x) expr::op_t::wrap_functor(x)
|
||||
|
||||
class scope_t : public noncopyable
|
||||
{
|
||||
public:
|
||||
enum type_t {
|
||||
CHILD_SCOPE,
|
||||
SYMBOL_SCOPE,
|
||||
CALL_SCOPE,
|
||||
CONTEXT_SCOPE
|
||||
} type_;
|
||||
|
||||
explicit scope_t(type_t _type) : type_(_type) {
|
||||
TRACE_CTOR(expr::scope_t, "type_t");
|
||||
}
|
||||
virtual ~scope_t() {
|
||||
TRACE_DTOR(expr::scope_t);
|
||||
}
|
||||
|
||||
const type_t type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
virtual void define(const string& name, ptr_op_t def) = 0;
|
||||
void define(const string& name, const value_t& val);
|
||||
virtual ptr_op_t lookup(const string& name) = 0;
|
||||
value_t resolve(const string& name) {
|
||||
#if 0
|
||||
return lookup(name)->calc(*this);
|
||||
#else
|
||||
return value_t();
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual optional<scope_t&> find_scope(const type_t _type,
|
||||
bool skip_this = false) = 0;
|
||||
virtual optional<scope_t&> find_first_scope(const type_t _type1,
|
||||
const type_t _type2,
|
||||
bool skip_this = false) = 0;
|
||||
|
||||
template <typename T>
|
||||
T& find_scope(bool skip_this = false) {
|
||||
assert(false);
|
||||
}
|
||||
template <typename T>
|
||||
optional<T&> maybe_find_scope(bool skip_this = false) {
|
||||
assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
class child_scope_t : public scope_t
|
||||
{
|
||||
scope_t * parent;
|
||||
|
||||
public:
|
||||
explicit child_scope_t(type_t _type = CHILD_SCOPE)
|
||||
: scope_t(_type), parent(NULL) {
|
||||
TRACE_CTOR(expr::child_scope_t, "type_t");
|
||||
}
|
||||
explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE)
|
||||
: scope_t(_type), parent(&_parent) {
|
||||
TRACE_CTOR(expr::child_scope_t, "scope_t&, type_t");
|
||||
}
|
||||
virtual ~child_scope_t() {
|
||||
TRACE_DTOR(expr::child_scope_t);
|
||||
}
|
||||
public:
|
||||
virtual void define(const string& name, ptr_op_t def) {
|
||||
if (parent)
|
||||
parent->define(name, def);
|
||||
}
|
||||
virtual ptr_op_t lookup(const string& name) {
|
||||
if (parent)
|
||||
return parent->lookup(name);
|
||||
return ptr_op_t();
|
||||
}
|
||||
|
||||
virtual optional<scope_t&> find_scope(type_t _type,
|
||||
bool skip_this = false) {
|
||||
for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
|
||||
if (ptr->type() == _type)
|
||||
return *ptr;
|
||||
|
||||
ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
|
||||
}
|
||||
return none;
|
||||
}
|
||||
|
||||
virtual optional<scope_t&> find_first_scope(const type_t _type1,
|
||||
const type_t _type2,
|
||||
bool skip_this = false) {
|
||||
for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
|
||||
if (ptr->type() == _type1 || ptr->type() == _type2)
|
||||
return *ptr;
|
||||
|
||||
ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
|
||||
}
|
||||
return none;
|
||||
}
|
||||
};
|
||||
|
||||
class symbol_scope_t : public child_scope_t
|
||||
{
|
||||
typedef std::map<const string, ptr_op_t> symbol_map;
|
||||
symbol_map symbols;
|
||||
|
||||
public:
|
||||
explicit symbol_scope_t()
|
||||
: child_scope_t(SYMBOL_SCOPE) {
|
||||
TRACE_CTOR(expr::symbol_scope_t, "");
|
||||
}
|
||||
explicit symbol_scope_t(scope_t& _parent)
|
||||
: child_scope_t(_parent, SYMBOL_SCOPE) {
|
||||
TRACE_CTOR(expr::symbol_scope_t, "scope_t&");
|
||||
}
|
||||
virtual ~symbol_scope_t() {
|
||||
TRACE_DTOR(expr::symbol_scope_t);
|
||||
}
|
||||
|
||||
virtual void define(const string& name, ptr_op_t def);
|
||||
void define(const string& name, const value_t& val) {
|
||||
scope_t::define(name, val);
|
||||
}
|
||||
virtual ptr_op_t lookup(const string& name);
|
||||
};
|
||||
|
||||
class call_scope_t : public child_scope_t
|
||||
{
|
||||
value_t args;
|
||||
|
||||
public:
|
||||
explicit call_scope_t(scope_t& _parent)
|
||||
: child_scope_t(_parent, CALL_SCOPE) {
|
||||
TRACE_CTOR(expr::call_scope_t, "scope_t&");
|
||||
}
|
||||
virtual ~call_scope_t() {
|
||||
TRACE_DTOR(expr::call_scope_t);
|
||||
}
|
||||
|
||||
void set_args(const value_t& _args) {
|
||||
args = _args;
|
||||
}
|
||||
|
||||
value_t& value() {
|
||||
return args;
|
||||
}
|
||||
|
||||
value_t& operator[](const int index) {
|
||||
return args[index];
|
||||
}
|
||||
const value_t& operator[](const int index) const {
|
||||
return args[index];
|
||||
}
|
||||
|
||||
void push_back(const value_t& val) {
|
||||
args.push_back(val);
|
||||
}
|
||||
void pop_back() {
|
||||
args.pop_back();
|
||||
}
|
||||
|
||||
const std::size_t size() const {
|
||||
return args.size();
|
||||
}
|
||||
};
|
||||
|
||||
class context_scope_t : public child_scope_t
|
||||
{
|
||||
public:
|
||||
value_t current_element;
|
||||
std::size_t element_index;
|
||||
std::size_t sequence_size;
|
||||
|
||||
explicit context_scope_t(scope_t& _parent,
|
||||
const value_t& _element = NULL_VALUE,
|
||||
const std::size_t _element_index = 0,
|
||||
const std::size_t _sequence_size = 0)
|
||||
: child_scope_t(_parent, CONTEXT_SCOPE), current_element(_element),
|
||||
element_index(_element_index), sequence_size(_sequence_size)
|
||||
{
|
||||
TRACE_CTOR(expr::context_scope_t, "scope_t&, const value_t&, ...");
|
||||
}
|
||||
virtual ~context_scope_t() {
|
||||
TRACE_DTOR(expr::context_scope_t);
|
||||
}
|
||||
|
||||
const std::size_t index() const {
|
||||
return element_index;
|
||||
}
|
||||
const std::size_t size() const {
|
||||
return sequence_size;
|
||||
}
|
||||
|
||||
value_t& value() {
|
||||
return current_element;
|
||||
}
|
||||
};
|
||||
|
||||
struct op_t : public noncopyable
|
||||
{
|
||||
enum kind_t {
|
||||
// Constants
|
||||
CONSTANT,
|
||||
VALUE,
|
||||
ARG_INDEX,
|
||||
|
||||
CONSTANTS,
|
||||
|
|
@ -70,6 +326,8 @@ struct value_expr_t
|
|||
TOTAL_EXPR,
|
||||
|
||||
// Functions
|
||||
FUNCTION,
|
||||
|
||||
F_NOW,
|
||||
F_ARITH_MEAN,
|
||||
F_QUANTITY,
|
||||
|
|
@ -121,82 +379,231 @@ struct value_expr_t
|
|||
LAST
|
||||
};
|
||||
|
||||
kind_t kind;
|
||||
mutable short refc;
|
||||
value_expr_t * left;
|
||||
kind_t kind;
|
||||
mutable short refc;
|
||||
ptr_op_t left_;
|
||||
|
||||
union {
|
||||
value_t * value;
|
||||
mask_t * mask;
|
||||
unsigned int arg_index; // used by ARG_INDEX and O_ARG
|
||||
value_expr_t * right;
|
||||
};
|
||||
variant<unsigned int, // used by ARG_INDEX and O_ARG
|
||||
value_t, // used by constant VALUE
|
||||
mask_t, // used by constant MASK
|
||||
function_t, // used by terminal FUNCTION
|
||||
#if 0
|
||||
node_t::nameid_t, // used by NODE_ID and ATTR_ID
|
||||
#endif
|
||||
ptr_op_t> // used by all binary operators
|
||||
data;
|
||||
|
||||
value_expr_t(const kind_t _kind)
|
||||
: kind(_kind), refc(0), left(NULL), right(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor value_expr_t " << this);
|
||||
explicit op_t(const kind_t _kind) : kind(_kind), refc(0){
|
||||
TRACE_CTOR(expr::op_t, "const kind_t");
|
||||
}
|
||||
~value_expr_t();
|
||||
~op_t() {
|
||||
TRACE_DTOR(expr::op_t);
|
||||
|
||||
void release() const {
|
||||
DEBUG("ledger.valexpr.memory",
|
||||
"Releasing " << this << ", refc now " << refc - 1);
|
||||
assert(refc > 0);
|
||||
if (--refc == 0)
|
||||
delete this;
|
||||
DEBUG("ledger.xpath.memory", "Destroying " << this);
|
||||
assert(refc == 0);
|
||||
}
|
||||
value_expr_t * acquire() {
|
||||
DEBUG("ledger.valexpr.memory",
|
||||
"Acquiring " << this << ", refc now " << refc + 1);
|
||||
|
||||
bool is_long() const {
|
||||
return data.type() == typeid(unsigned int);
|
||||
}
|
||||
unsigned int& as_long() {
|
||||
assert(kind == ARG_INDEX || kind == O_ARG);
|
||||
return boost::get<unsigned int>(data);
|
||||
}
|
||||
const unsigned int& as_long() const {
|
||||
return const_cast<op_t *>(this)->as_long();
|
||||
}
|
||||
void set_long(unsigned int val) {
|
||||
data = val;
|
||||
}
|
||||
|
||||
bool is_value() const {
|
||||
if (kind == VALUE) {
|
||||
assert(data.type() == typeid(value_t));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
value_t& as_value() {
|
||||
assert(is_value());
|
||||
return boost::get<value_t>(data);
|
||||
}
|
||||
const value_t& as_value() const {
|
||||
return const_cast<op_t *>(this)->as_value();
|
||||
}
|
||||
void set_value(const value_t& val) {
|
||||
data = val;
|
||||
}
|
||||
|
||||
bool is_string() const {
|
||||
if (kind == VALUE) {
|
||||
assert(data.type() == typeid(value_t));
|
||||
return boost::get<value_t>(data).is_string();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
string& as_string() {
|
||||
assert(is_string());
|
||||
return boost::get<value_t>(data).as_string_lval();
|
||||
}
|
||||
const string& as_string() const {
|
||||
return const_cast<op_t *>(this)->as_string();
|
||||
}
|
||||
void set_string(const string& val) {
|
||||
data = value_t(val);
|
||||
}
|
||||
|
||||
bool is_function() const {
|
||||
return kind == FUNCTION;
|
||||
}
|
||||
function_t& as_function() {
|
||||
assert(kind == FUNCTION);
|
||||
return boost::get<function_t>(data);
|
||||
}
|
||||
const function_t& as_function() const {
|
||||
return const_cast<op_t *>(this)->as_function();
|
||||
}
|
||||
void set_function(const function_t& val) {
|
||||
data = val;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool is_name() const {
|
||||
return data.type() == typeid(node_t::nameid_t);
|
||||
}
|
||||
node_t::nameid_t& as_name() {
|
||||
assert(kind == NODE_ID || kind == ATTR_ID);
|
||||
return boost::get<node_t::nameid_t>(data);
|
||||
}
|
||||
const node_t::nameid_t& as_name() const {
|
||||
return const_cast<op_t *>(this)->as_name();
|
||||
}
|
||||
void set_name(const node_t::nameid_t& val) {
|
||||
data = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
ptr_op_t& as_op() {
|
||||
assert(kind > TERMINALS);
|
||||
return boost::get<ptr_op_t>(data);
|
||||
}
|
||||
const ptr_op_t& as_op() const {
|
||||
return const_cast<op_t *>(this)->as_op();
|
||||
}
|
||||
|
||||
void acquire() const {
|
||||
DEBUG("ledger.xpath.memory",
|
||||
"Acquiring " << this << ", refc now " << refc + 1);
|
||||
assert(refc >= 0);
|
||||
refc++;
|
||||
return this;
|
||||
}
|
||||
const value_expr_t * acquire() const {
|
||||
DEBUG("ledger.valexpr.memory",
|
||||
"Acquiring " << this << ", refc now " << refc + 1);
|
||||
refc++;
|
||||
return this;
|
||||
void release() const {
|
||||
DEBUG("ledger.xpath.memory",
|
||||
"Releasing " << this << ", refc now " << refc - 1);
|
||||
assert(refc > 0);
|
||||
if (--refc == 0)
|
||||
checked_delete(this);
|
||||
}
|
||||
|
||||
void set_left(value_expr_t * expr) {
|
||||
ptr_op_t& left() {
|
||||
return left_;
|
||||
}
|
||||
const ptr_op_t& left() const {
|
||||
assert(kind > TERMINALS);
|
||||
if (left)
|
||||
left->release();
|
||||
left = expr ? expr->acquire() : NULL;
|
||||
return left_;
|
||||
}
|
||||
void set_left(const ptr_op_t& expr) {
|
||||
assert(kind > TERMINALS);
|
||||
left_ = expr;
|
||||
}
|
||||
|
||||
void set_right(value_expr_t * expr) {
|
||||
ptr_op_t& right() {
|
||||
assert(kind > TERMINALS);
|
||||
if (right)
|
||||
right->release();
|
||||
right = expr ? expr->acquire() : NULL;
|
||||
return as_op();
|
||||
}
|
||||
const ptr_op_t& right() const {
|
||||
assert(kind > TERMINALS);
|
||||
return as_op();
|
||||
}
|
||||
void set_right(const ptr_op_t& expr) {
|
||||
assert(kind > TERMINALS);
|
||||
data = expr;
|
||||
}
|
||||
|
||||
static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
|
||||
ptr_op_t _right = NULL);
|
||||
ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const {
|
||||
return new_node(kind, _left, _right);
|
||||
}
|
||||
|
||||
static ptr_op_t wrap_value(const value_t& val);
|
||||
static ptr_op_t wrap_functor(const function_t& fobj);
|
||||
|
||||
ptr_op_t compile(scope_t& scope);
|
||||
value_t current_value(scope_t& scope);
|
||||
#if 0
|
||||
node_t& current_xml_node(scope_t& scope);
|
||||
#endif
|
||||
value_t calc(scope_t& scope);
|
||||
|
||||
void compute(value_t& result,
|
||||
const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) const;
|
||||
ptr_op_t context = NULL) const;
|
||||
|
||||
value_t compute(const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) const {
|
||||
ptr_op_t context = NULL) const {
|
||||
value_t temp;
|
||||
compute(temp, details, context);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
value_expr_t(const value_expr_t&) {
|
||||
DEBUG("ledger.memory.ctors", "ctor value_expr_t (copy) " << this);
|
||||
struct print_context_t
|
||||
{
|
||||
scope_t& scope;
|
||||
const bool relaxed;
|
||||
const ptr_op_t& op_to_find;
|
||||
unsigned long * start_pos;
|
||||
unsigned long * end_pos;
|
||||
|
||||
print_context_t(scope_t& _scope,
|
||||
const bool _relaxed = false,
|
||||
const ptr_op_t& _op_to_find = ptr_op_t(),
|
||||
unsigned long * _start_pos = NULL,
|
||||
unsigned long * _end_pos = NULL)
|
||||
: scope(_scope), relaxed(_relaxed), op_to_find(_op_to_find),
|
||||
start_pos(_start_pos), end_pos(_end_pos) {}
|
||||
};
|
||||
|
||||
bool print(std::ostream& out, print_context_t& context) const;
|
||||
void dump(std::ostream& out, const int depth) const;
|
||||
|
||||
friend inline void intrusive_ptr_add_ref(op_t * op) {
|
||||
op->acquire();
|
||||
}
|
||||
friend inline void intrusive_ptr_release(op_t * op) {
|
||||
op->release();
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
class op_predicate {
|
||||
ptr_op_t op;
|
||||
|
||||
public:
|
||||
explicit op_predicate(ptr_op_t _op) : op(_op) {}
|
||||
bool operator()(scope_t& scope) {
|
||||
return op->calc(scope).to_boolean();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class valexpr_context : public error_context {
|
||||
public:
|
||||
const ledger::value_expr_t * expr;
|
||||
const ledger::value_expr_t * error_node;
|
||||
ptr_op_t expr;
|
||||
ptr_op_t error_node;
|
||||
|
||||
valexpr_context(const ledger::value_expr_t * _expr,
|
||||
const string& desc = "") throw();
|
||||
valexpr_context(const ptr_op_t _expr,
|
||||
const string& desc = "") throw();
|
||||
virtual ~valexpr_context() throw();
|
||||
|
||||
virtual void describe(std::ostream& out) const throw();
|
||||
|
|
@ -217,53 +624,6 @@ class value_expr_error : public error {
|
|||
virtual ~value_expr_error() throw() {}
|
||||
};
|
||||
|
||||
struct scope_t
|
||||
{
|
||||
scope_t * parent;
|
||||
|
||||
typedef std::map<const string, value_expr_t *> symbol_map;
|
||||
typedef std::pair<const string, value_expr_t *> symbol_pair;
|
||||
|
||||
symbol_map symbols;
|
||||
|
||||
scope_t(scope_t * _parent = NULL) : parent(_parent) {
|
||||
DEBUG("ledger.memory.ctors", "ctor scope_t");
|
||||
}
|
||||
~scope_t() {
|
||||
DEBUG("ledger.memory.dtors", "dtor scope_t");
|
||||
for (symbol_map::iterator i = symbols.begin();
|
||||
i != symbols.end();
|
||||
i++)
|
||||
(*i).second->release();
|
||||
}
|
||||
|
||||
void define(const string& name, value_expr_t * def) {
|
||||
DEBUG("ledger.valexpr.syms",
|
||||
"Defining '" << name << "' = " << def);
|
||||
std::pair<symbol_map::iterator, bool> result
|
||||
= symbols.insert(symbol_pair(name, def));
|
||||
if (! result.second) {
|
||||
symbols.erase(name);
|
||||
std::pair<symbol_map::iterator, bool> result
|
||||
= symbols.insert(symbol_pair(name, def));
|
||||
if (! result.second) {
|
||||
def->release();
|
||||
throw new compute_error(string("Redefinition of '") +
|
||||
name + "' in same scope");
|
||||
}
|
||||
}
|
||||
def->acquire();
|
||||
}
|
||||
value_expr_t * lookup(const string& name) {
|
||||
symbol_map::const_iterator i = symbols.find(name);
|
||||
if (i != symbols.end())
|
||||
return (*i).second;
|
||||
else if (parent)
|
||||
return parent->lookup(name);
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
extern std::auto_ptr<scope_t> global_scope;
|
||||
|
||||
extern datetime_t terminus;
|
||||
|
|
@ -271,9 +631,9 @@ extern bool initialized;
|
|||
|
||||
void init_value_expr();
|
||||
|
||||
bool compute_amount(value_expr_t * expr, amount_t& amt,
|
||||
bool compute_amount(const ptr_op_t expr, amount_t& amt,
|
||||
const transaction_t * xact,
|
||||
value_expr_t * context = NULL);
|
||||
const ptr_op_t context = NULL);
|
||||
|
||||
#define PARSE_VALEXPR_NORMAL 0x00
|
||||
#define PARSE_VALEXPR_PARTIAL 0x01
|
||||
|
|
@ -281,11 +641,11 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
|
|||
#define PARSE_VALEXPR_NO_MIGRATE 0x04
|
||||
#define PARSE_VALEXPR_NO_REDUCE 0x08
|
||||
|
||||
value_expr_t * parse_value_expr(std::istream& in,
|
||||
ptr_op_t parse_value_expr(std::istream& in,
|
||||
scope_t * scope = NULL,
|
||||
const short flags = PARSE_VALEXPR_RELAXED);
|
||||
|
||||
inline value_expr_t *
|
||||
inline ptr_op_t
|
||||
parse_value_expr(const string& str,
|
||||
scope_t * scope = NULL,
|
||||
const short flags = PARSE_VALEXPR_RELAXED) {
|
||||
|
|
@ -301,29 +661,29 @@ parse_value_expr(const string& str,
|
|||
}
|
||||
}
|
||||
|
||||
inline value_expr_t *
|
||||
inline ptr_op_t
|
||||
parse_value_expr(const char * p,
|
||||
scope_t * scope = NULL,
|
||||
const short flags = PARSE_VALEXPR_RELAXED) {
|
||||
return parse_value_expr(string(p), scope, flags);
|
||||
}
|
||||
|
||||
void dump_value_expr(std::ostream& out, const value_expr_t * node,
|
||||
void dump_value_expr(std::ostream& out, const ptr_op_t node,
|
||||
const int depth = 0);
|
||||
|
||||
bool print_value_expr(std::ostream& out,
|
||||
const value_expr_t * node,
|
||||
const ptr_op_t node,
|
||||
const bool relaxed = true,
|
||||
const value_expr_t * node_to_find = NULL,
|
||||
const ptr_op_t node_to_find = NULL,
|
||||
unsigned long * start_pos = NULL,
|
||||
unsigned long * end_pos = NULL);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline void guarded_compute(const value_expr_t * expr,
|
||||
value_t& result,
|
||||
const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) {
|
||||
inline void guarded_compute(const ptr_op_t expr,
|
||||
value_t& result,
|
||||
const details_t& details = details_t(),
|
||||
const ptr_op_t context = NULL) {
|
||||
try {
|
||||
expr->compute(result, details);
|
||||
}
|
||||
|
|
@ -333,45 +693,81 @@ inline void guarded_compute(const value_expr_t * expr,
|
|||
err->context.push_back(new valexpr_context(expr));
|
||||
error_context * last = err->context.back();
|
||||
if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) {
|
||||
ctxt->expr = expr->acquire();
|
||||
ctxt->expr = expr;
|
||||
ctxt->desc = "While computing value expression:";
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
inline value_t guarded_compute(const value_expr_t * expr,
|
||||
inline value_t guarded_compute(const ptr_op_t expr,
|
||||
const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) {
|
||||
ptr_op_t context = NULL) {
|
||||
value_t temp;
|
||||
guarded_compute(expr, temp, details, context);
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline symbol_scope_t&
|
||||
scope_t::find_scope<symbol_scope_t>(bool skip_this) {
|
||||
optional<scope_t&> scope = find_scope(SYMBOL_SCOPE, skip_this);
|
||||
assert(scope);
|
||||
return downcast<symbol_scope_t>(*scope);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline call_scope_t&
|
||||
scope_t::find_scope<call_scope_t>(bool skip_this) {
|
||||
optional<scope_t&> scope = find_scope(CALL_SCOPE, skip_this);
|
||||
assert(scope);
|
||||
return downcast<call_scope_t>(*scope);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline context_scope_t&
|
||||
scope_t::find_scope<context_scope_t>(bool skip_this) {
|
||||
optional<scope_t&> scope = find_scope(CONTEXT_SCOPE, skip_this);
|
||||
assert(scope);
|
||||
return downcast<context_scope_t>(*scope);
|
||||
}
|
||||
|
||||
#define FIND_SCOPE(scope_type, scope_ref) \
|
||||
downcast<scope_t>(scope_ref).find_scope<scope_type>()
|
||||
|
||||
#define CALL_SCOPE(scope_ref) \
|
||||
FIND_SCOPE(call_scope_t, scope_ref)
|
||||
#define SYMBOL_SCOPE(scope_ref) \
|
||||
FIND_SCOPE(symbol_scope_t, scope_ref)
|
||||
#define CONTEXT_SCOPE(scope_ref) \
|
||||
FIND_SCOPE(context_scope_t, scope_ref)
|
||||
|
||||
} // namespace expr
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class value_expr
|
||||
{
|
||||
value_expr_t * ptr;
|
||||
expr::ptr_op_t ptr;
|
||||
|
||||
public:
|
||||
string expr;
|
||||
|
||||
typedef expr::details_t details_t;
|
||||
|
||||
value_expr() : ptr(NULL) {}
|
||||
|
||||
value_expr(const string& _expr) : expr(_expr) {
|
||||
DEBUG("ledger.memory.ctors", "ctor value_expr");
|
||||
if (! _expr.empty())
|
||||
ptr = parse_value_expr(expr)->acquire();
|
||||
ptr = expr::parse_value_expr(expr);
|
||||
else
|
||||
ptr = NULL;
|
||||
ptr = expr::ptr_op_t();
|
||||
}
|
||||
value_expr(value_expr_t * _ptr)
|
||||
: ptr(_ptr ? _ptr->acquire(): NULL) {
|
||||
value_expr(const expr::ptr_op_t _ptr) : ptr(_ptr) {
|
||||
DEBUG("ledger.memory.ctors", "ctor value_expr");
|
||||
}
|
||||
value_expr(const value_expr& other)
|
||||
: ptr(other.ptr ? other.ptr->acquire() : NULL),
|
||||
expr(other.expr) {
|
||||
value_expr(const value_expr& other) : ptr(other.ptr), expr(other.expr) {
|
||||
DEBUG("ledger.memory.ctors", "ctor value_expr");
|
||||
}
|
||||
virtual ~value_expr() {
|
||||
|
|
@ -382,10 +778,10 @@ public:
|
|||
|
||||
value_expr& operator=(const string& _expr) {
|
||||
expr = _expr;
|
||||
reset(parse_value_expr(expr));
|
||||
reset(expr::parse_value_expr(expr));
|
||||
return *this;
|
||||
}
|
||||
value_expr& operator=(value_expr_t * _expr) {
|
||||
value_expr& operator=(expr::ptr_op_t _expr) {
|
||||
expr = "";
|
||||
reset(_expr);
|
||||
return *this;
|
||||
|
|
@ -402,50 +798,48 @@ public:
|
|||
operator string() const throw() {
|
||||
return expr;
|
||||
}
|
||||
operator value_expr_t *() const throw() {
|
||||
operator const expr::ptr_op_t() const throw() {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
value_expr_t& operator*() const throw() {
|
||||
const expr::op_t& operator*() const throw() {
|
||||
return *ptr;
|
||||
}
|
||||
value_expr_t * operator->() const throw() {
|
||||
const expr::ptr_op_t operator->() const throw() {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
value_expr_t * get() const throw() { return ptr; }
|
||||
value_expr_t * release() throw() {
|
||||
value_expr_t * tmp = ptr;
|
||||
ptr = 0;
|
||||
const expr::ptr_op_t get() const throw() { return ptr; }
|
||||
const expr::ptr_op_t release() throw() {
|
||||
const expr::ptr_op_t tmp = ptr;
|
||||
ptr = expr::ptr_op_t();
|
||||
return tmp;
|
||||
}
|
||||
void reset(value_expr_t * p = 0) throw() {
|
||||
if (p != ptr) {
|
||||
if (ptr)
|
||||
ptr->release();
|
||||
ptr = p ? p->acquire() : NULL;
|
||||
}
|
||||
void reset(const expr::ptr_op_t p = expr::ptr_op_t()) throw() {
|
||||
ptr = p;
|
||||
}
|
||||
|
||||
virtual void compute(value_t& result,
|
||||
const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) {
|
||||
expr::ptr_op_t context = NULL) {
|
||||
guarded_compute(ptr, result, details, context);
|
||||
}
|
||||
virtual value_t compute(const details_t& details = details_t(),
|
||||
value_expr_t * context = NULL) {
|
||||
expr::ptr_op_t context = NULL) {
|
||||
value_t temp;
|
||||
guarded_compute(ptr, temp, details, context);
|
||||
return temp;
|
||||
}
|
||||
|
||||
friend bool print_value_expr(std::ostream& out,
|
||||
const value_expr_t * node,
|
||||
const value_expr_t * node_to_find,
|
||||
const expr::ptr_op_t node,
|
||||
const expr::ptr_op_t node_to_find,
|
||||
unsigned long * start_pos,
|
||||
unsigned long * end_pos);
|
||||
};
|
||||
|
||||
typedef value_expr::details_t details_t; // jww (2008-07-20): remove
|
||||
|
||||
extern value_expr amount_expr;
|
||||
extern value_expr total_expr;
|
||||
|
||||
|
|
@ -471,14 +865,14 @@ inline value_t compute_total(const details_t& details = details_t()) {
|
|||
return total_expr->compute(details);
|
||||
}
|
||||
|
||||
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope,
|
||||
expr::ptr_op_t parse_boolean_expr(std::istream& in, expr::scope_t * scope,
|
||||
const short flags);
|
||||
|
||||
inline void parse_value_definition(const string& str,
|
||||
scope_t * scope = NULL) {
|
||||
expr::scope_t * scope = NULL) {
|
||||
std::istringstream def(str);
|
||||
value_expr expr
|
||||
(parse_boolean_expr(def, scope ? scope : global_scope.get(),
|
||||
(parse_boolean_expr(def, scope ? scope : expr::global_scope.get(),
|
||||
PARSE_VALEXPR_RELAXED));
|
||||
}
|
||||
|
||||
|
|
@ -487,28 +881,26 @@ inline void parse_value_definition(const string& str,
|
|||
template <typename T>
|
||||
class item_predicate
|
||||
{
|
||||
public:
|
||||
const value_expr_t * predicate;
|
||||
public:
|
||||
value_expr predicate;
|
||||
|
||||
item_predicate(const string& _predicate) : predicate(NULL) {
|
||||
DEBUG("ledger.memory.ctors", "ctor item_predicate<T>");
|
||||
if (! _predicate.empty())
|
||||
predicate = parse_value_expr(_predicate)->acquire();
|
||||
item_predicate() {
|
||||
TRACE_CTOR(item_predicate, "ctor item_predicate<T>()");
|
||||
}
|
||||
item_predicate(const value_expr_t * _predicate = NULL)
|
||||
: predicate(_predicate->acquire()) {
|
||||
DEBUG("ledger.memory.ctors", "ctor item_predicate<T>");
|
||||
item_predicate(const value_expr& _predicate) : predicate(_predicate) {
|
||||
TRACE_CTOR(item_predicate, "ctor item_predicate<T>(const value_expr&)");
|
||||
}
|
||||
item_predicate(const string& _predicate) : predicate(_predicate) {
|
||||
TRACE_CTOR(item_predicate, "ctor item_predicate<T>(const string&)");
|
||||
}
|
||||
|
||||
~item_predicate() {
|
||||
DEBUG("ledger.memory.dtors", "dtor item_predicate<T>");
|
||||
if (predicate)
|
||||
predicate->release();
|
||||
TRACE_DTOR(item_predicate);
|
||||
}
|
||||
|
||||
bool operator()(const T& item) const {
|
||||
return (! predicate ||
|
||||
predicate->compute(details_t(item)).strip_annotations());
|
||||
predicate->compute(value_expr::details_t(item)).strip_annotations());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
24
walk.cc
24
walk.cc
|
|
@ -16,14 +16,14 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
|
|||
|
||||
transaction_xdata_t& lxdata(transaction_xdata(*left));
|
||||
if (! (lxdata.dflags & TRANSACTION_SORT_CALC)) {
|
||||
guarded_compute(sort_order, lxdata.sort_value, details_t(*left));
|
||||
sort_order.compute(lxdata.sort_value, details_t(*left));
|
||||
lxdata.sort_value.reduce();
|
||||
lxdata.dflags |= TRANSACTION_SORT_CALC;
|
||||
}
|
||||
|
||||
transaction_xdata_t& rxdata(transaction_xdata(*right));
|
||||
if (! (rxdata.dflags & TRANSACTION_SORT_CALC)) {
|
||||
guarded_compute(sort_order, rxdata.sort_value, details_t(*right));
|
||||
sort_order.compute(rxdata.sort_value, details_t(*right));
|
||||
rxdata.sort_value.reduce();
|
||||
rxdata.dflags |= TRANSACTION_SORT_CALC;
|
||||
}
|
||||
|
|
@ -799,13 +799,13 @@ bool compare_items<account_t>::operator()(const account_t * left,
|
|||
|
||||
account_xdata_t& lxdata(account_xdata(*left));
|
||||
if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) {
|
||||
guarded_compute(sort_order, lxdata.sort_value, details_t(*left));
|
||||
sort_order.compute(lxdata.sort_value, details_t(*left));
|
||||
lxdata.dflags |= ACCOUNT_SORT_CALC;
|
||||
}
|
||||
|
||||
account_xdata_t& rxdata(account_xdata(*right));
|
||||
if (! (rxdata.dflags & ACCOUNT_SORT_CALC)) {
|
||||
guarded_compute(sort_order, rxdata.sort_value, details_t(*right));
|
||||
sort_order.compute(rxdata.sort_value, details_t(*right));
|
||||
rxdata.dflags |= ACCOUNT_SORT_CALC;
|
||||
}
|
||||
|
||||
|
|
@ -841,9 +841,9 @@ void sum_accounts(account_t& account)
|
|||
xdata.total_count += xdata.count;
|
||||
}
|
||||
|
||||
void sort_accounts(account_t& account,
|
||||
const value_expr_t * sort_order,
|
||||
accounts_deque& accounts)
|
||||
void sort_accounts(account_t& account,
|
||||
const value_expr& sort_order,
|
||||
accounts_deque& accounts)
|
||||
{
|
||||
for (accounts_map::iterator i = account.accounts.begin();
|
||||
i != account.accounts.end();
|
||||
|
|
@ -854,15 +854,15 @@ void sort_accounts(account_t& account,
|
|||
compare_items<account_t>(sort_order));
|
||||
}
|
||||
|
||||
void walk_accounts(account_t& account,
|
||||
item_handler<account_t>& handler,
|
||||
const value_expr_t * sort_order)
|
||||
void walk_accounts(account_t& account,
|
||||
item_handler<account_t>& handler,
|
||||
const optional<value_expr>& sort_order)
|
||||
{
|
||||
handler(account);
|
||||
|
||||
if (sort_order) {
|
||||
accounts_deque accounts;
|
||||
sort_accounts(account, sort_order, accounts);
|
||||
sort_accounts(account, *sort_order, accounts);
|
||||
for (accounts_deque::const_iterator i = accounts.begin();
|
||||
i != accounts.end();
|
||||
i++) {
|
||||
|
|
@ -884,7 +884,7 @@ void walk_accounts(account_t& account,
|
|||
if (! sort_string.empty()) {
|
||||
value_expr sort_order;
|
||||
sort_order.reset(parse_value_expr(sort_string));
|
||||
walk_accounts(account, handler, sort_order.get());
|
||||
walk_accounts(account, handler, sort_order);
|
||||
} else {
|
||||
walk_accounts(account, handler);
|
||||
}
|
||||
|
|
|
|||
50
walk.h
50
walk.h
|
|
@ -38,12 +38,9 @@ struct item_handler {
|
|||
|
||||
template <typename T>
|
||||
class compare_items {
|
||||
const value_expr_t * sort_order;
|
||||
value_expr sort_order;
|
||||
public:
|
||||
compare_items(const value_expr_t * _sort_order)
|
||||
: sort_order(_sort_order) {
|
||||
assert(sort_order);
|
||||
}
|
||||
compare_items(value_expr _sort_order) : sort_order(_sort_order) {}
|
||||
bool operator()(const T * left, const T * right);
|
||||
};
|
||||
|
||||
|
|
@ -55,8 +52,8 @@ bool compare_items<T>::operator()(const T * left, const T * right)
|
|||
|
||||
value_t left_result;
|
||||
value_t right_result;
|
||||
guarded_compute(sort_order, left_result, details_t(*left));
|
||||
guarded_compute(sort_order, right_result, details_t(*right));
|
||||
sort_order.compute(left_result, details_t(*left));
|
||||
sort_order.compute(right_result, details_t(*right));
|
||||
|
||||
return left_result < right_result;
|
||||
}
|
||||
|
|
@ -244,26 +241,19 @@ class sort_transactions : public item_handler<transaction_t>
|
|||
{
|
||||
typedef std::deque<transaction_t *> transactions_deque;
|
||||
|
||||
transactions_deque transactions;
|
||||
const value_expr_t * sort_order;
|
||||
transactions_deque transactions;
|
||||
const value_expr sort_order;
|
||||
|
||||
public:
|
||||
sort_transactions(item_handler<transaction_t> * handler,
|
||||
const value_expr_t * _sort_order)
|
||||
const value_expr& _sort_order)
|
||||
: item_handler<transaction_t>(handler),
|
||||
sort_order(_sort_order->acquire()) {}
|
||||
sort_order(_sort_order) {}
|
||||
|
||||
sort_transactions(item_handler<transaction_t> * handler,
|
||||
const string& _sort_order)
|
||||
: item_handler<transaction_t>(handler) {
|
||||
assert(! _sort_order.empty());
|
||||
sort_order = parse_value_expr(_sort_order)->acquire();
|
||||
}
|
||||
|
||||
virtual ~sort_transactions() {
|
||||
assert(sort_order);
|
||||
sort_order->release();
|
||||
}
|
||||
: item_handler<transaction_t>(handler),
|
||||
sort_order(_sort_order) {}
|
||||
|
||||
virtual void post_accumulated_xacts();
|
||||
|
||||
|
|
@ -284,7 +274,7 @@ class sort_entries : public item_handler<transaction_t>
|
|||
|
||||
public:
|
||||
sort_entries(item_handler<transaction_t> * handler,
|
||||
const value_expr_t * _sort_order)
|
||||
const value_expr& _sort_order)
|
||||
: sorter(handler, _sort_order) {}
|
||||
|
||||
sort_entries(item_handler<transaction_t> * handler,
|
||||
|
|
@ -312,7 +302,7 @@ class filter_transactions : public item_handler<transaction_t>
|
|||
|
||||
public:
|
||||
filter_transactions(item_handler<transaction_t> * handler,
|
||||
const value_expr_t * predicate)
|
||||
const value_expr& predicate)
|
||||
: item_handler<transaction_t>(handler), pred(predicate) {}
|
||||
|
||||
filter_transactions(item_handler<transaction_t> * handler,
|
||||
|
|
@ -392,7 +382,7 @@ class component_transactions : public item_handler<transaction_t>
|
|||
|
||||
public:
|
||||
component_transactions(item_handler<transaction_t> * handler,
|
||||
const value_expr_t * predicate)
|
||||
const value_expr& predicate)
|
||||
: item_handler<transaction_t>(handler), pred(predicate) {}
|
||||
|
||||
component_transactions(item_handler<transaction_t> * handler,
|
||||
|
|
@ -656,7 +646,7 @@ class forecast_transactions : public generate_transactions
|
|||
|
||||
public:
|
||||
forecast_transactions(item_handler<transaction_t> * handler,
|
||||
const value_expr_t * predicate)
|
||||
const value_expr& predicate)
|
||||
: generate_transactions(handler), pred(predicate) {}
|
||||
|
||||
forecast_transactions(item_handler<transaction_t> * handler,
|
||||
|
|
@ -720,12 +710,12 @@ void sum_accounts(account_t& account);
|
|||
|
||||
typedef std::deque<account_t *> accounts_deque;
|
||||
|
||||
void sort_accounts(account_t& account,
|
||||
const value_expr_t * sort_order,
|
||||
accounts_deque& accounts);
|
||||
void walk_accounts(account_t& account,
|
||||
item_handler<account_t>& handler,
|
||||
const value_expr_t * sort_order = NULL);
|
||||
void sort_accounts(account_t& account,
|
||||
const value_expr& sort_order,
|
||||
accounts_deque& accounts);
|
||||
void walk_accounts(account_t& account,
|
||||
item_handler<account_t>& handler,
|
||||
const optional<value_expr>& sort_order = none);
|
||||
void walk_accounts(account_t& account,
|
||||
item_handler<account_t>& handler,
|
||||
const string& sort_string);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue