predicate are now used instead of constraints

This commit is contained in:
John Wiegley 2004-07-30 23:42:59 -04:00
parent 94e76ae87e
commit 493694f848
10 changed files with 343 additions and 446 deletions

View file

@ -1,6 +1,5 @@
CODE = amount.cc balance.cc account.cc ledger.cc \
constraint.cc item.cc expr.cc format.cc \
textual.cc binary.cc
item.cc expr.cc format.cc textual.cc binary.cc
OBJS = $(patsubst %.cc,%.o,$(CODE))
#CXX = cc
CXX = g++

2
NEWS
View file

@ -70,7 +70,7 @@
i index (within the report)
o item age = date - report begin date
w item newness = report end date - date
b report begin date
e report end date

View file

@ -1,229 +0,0 @@
#include "constraint.h"
#include "expr.h"
#include <pcre.h>
namespace ledger {
constraints_t::~constraints_t()
{
if (predicate) delete predicate;
if (sort_order) delete sort_order;
}
mask_t::mask_t(const std::string& pat) : exclude(false)
{
const char * p = pat.c_str();
if (*p == '-') {
exclude = true;
p++;
while (std::isspace(*p))
p++;
}
else if (*p == '+') {
p++;
while (std::isspace(*p))
p++;
}
pattern = p;
const char *error;
int erroffset;
regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
&error, &erroffset, NULL);
if (! regexp)
std::cerr << "Warning: Failed to compile regexp: " << pattern
<< std::endl;
}
mask_t::mask_t(const mask_t& m) : exclude(m.exclude), pattern(m.pattern)
{
const char *error;
int erroffset;
regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
&error, &erroffset, NULL);
assert(regexp);
}
bool mask_t::match(const std::string& str) const
{
static int ovec[30];
int result = pcre_exec((pcre *)regexp, NULL,
str.c_str(), str.length(), 0, 0, ovec, 30);
return result >= 0 && ! exclude;
}
mask_t::~mask_t() {
pcre_free((pcre *)regexp);
}
bool matches(const masks_list& regexps, const std::string& str,
bool * by_exclusion)
{
if (regexps.empty())
return false;
bool match = false;
bool definite = false;
for (masks_list::const_iterator r = regexps.begin();
r != regexps.end();
r++) {
static int ovec[30];
int result = pcre_exec((pcre *)(*r).regexp, NULL,
str.c_str(), str.length(), 0, 0, ovec, 30);
if (result >= 0) {
match = ! (*r).exclude;
definite = true;
}
else if ((*r).exclude) {
if (! match)
match = ! definite;
}
else {
definite = true;
}
}
if (by_exclusion)
*by_exclusion = match && ! definite && by_exclusion;
return match;
}
bool constraints_t::matches_date_range(const std::time_t date) const
{
if (begin_date != -1 && difftime(date, begin_date) < 0)
return false;
if (end_date != -1 && difftime(date, end_date) >= 0)
return false;
if (have_date_mask) {
struct std::tm * then = std::gmtime(&date);
if (date_mask.tm_mon != -1 &&
date_mask.tm_mon != then->tm_mon)
return false;
if (date_mask.tm_mday != -1 &&
date_mask.tm_mday != then->tm_mday)
return false;
#if 0
// jww (2003-10-10): This causes only certain days of the week to
// print, even when it was not included in the mask.
if (date_mask.tm_wday != -1 &&
date_mask.tm_wday != then->tm_wday)
return false;
#endif
if (date_mask.tm_year != -1 &&
date_mask.tm_year != then->tm_year)
return false;
}
return true;
}
bool constraints_t::operator ()(const transaction_t * xact) const
{
if ((cleared_only && xact->entry->state != entry_t::CLEARED) ||
(uncleared_only && xact->entry->state == entry_t::CLEARED) ||
! matches_date_range(xact->entry->date))
return false;
if (! payee_masks.empty() &&
(! (matches(payee_masks, xact->entry->payee)
//|| matches(payee_masks, xact->entry->code))
)))
return false;
if (real_only && xact->flags & TRANSACTION_VIRTUAL)
return false;
if (! account_masks.empty() &&
! (matches(account_masks, std::string(*(xact->account)))
//|| matches(account_masks, (*i)->note)
))
return false;
return true;
}
bool constraints_t::operator ()(const entry_t * entry) const
{
if ((cleared_only && entry->state != entry_t::CLEARED) ||
(uncleared_only && entry->state == entry_t::CLEARED) ||
! matches_date_range(entry->date))
return false;
if (! payee_masks.empty() &&
(! (matches(payee_masks, entry->payee)
//|| matches(payee_masks, entry->code)
)))
return false;
if (! account_masks.empty()) {
bool match = false;
for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++) {
if (real_only && (*i)->flags & TRANSACTION_VIRTUAL)
continue;
if (matches(account_masks, std::string(*((*i)->account)))
//|| matches(account_masks, (*i)->note)
) {
match = true;
break;
}
}
if (! match)
return false;
}
return true;
}
bool constraints_t::operator ()(const item_t * item) const
{
if (predicate && ! predicate->compute(item, begin(), end()))
return false;
if (! matches_date_range(item->date))
return false;
if (! payee_masks.empty() && ! matches(payee_masks, item->payee))
return false;
#if 0
// jww (2004-07-26): It shouldn't be necessary to check against the
// account here, since this is always done during initial compiling
// of the item_t tree.
if (! account_masks.empty()) {
bool match = false;
for (amounts_map::const_iterator i = item->value.quantity.amounts.begin();
i != item->value.quantity.amounts.end();
i++) {
if (matches(account_masks, std::string(*((*i)->account)))
//|| matches(account_masks, (*i)->note)
) {
match = true;
break;
}
}
if (! match)
return false;
}
#endif
return true;
}
} // namespace ledger

View file

@ -2,6 +2,7 @@
#define _CONSTRAINT_H
#include "ledger.h"
#include "expr.h"
#include "item.h"
template <typename ForwardIterator, typename ValueType, typename Constraint>
@ -60,96 +61,71 @@ class constrained_iterator
namespace ledger {
class mask_t
{
public:
bool exclude;
std::string pattern;
void * regexp;
explicit mask_t(const std::string& pattern);
mask_t(const mask_t&);
~mask_t();
bool match(const std::string& str) const;
};
typedef std::list<mask_t> masks_list;
bool matches(const masks_list& regexps, const std::string& str,
bool * by_exclusion = NULL);
struct node_t;
enum periodicity_t {
PERIOD_NONE,
PERIOD_MONTHLY,
PERIOD_WEEKLY_SUN,
PERIOD_WEEKLY_MON
};
class constraints_t
{
public:
bool real_only;
bool cleared_only;
bool uncleared_only;
bool show_expanded;
bool show_related;
bool show_inverted;
bool show_subtotals;
bool show_empty;
std::time_t begin_date;
std::time_t end_date;
struct std::tm date_mask;
bool have_date_mask;
masks_list payee_masks;
masks_list account_masks;
periodicity_t period;
node_t * predicate;
node_t * sort_order;
node_t * predicate;
explicit constraints_t() {
real_only = false;
cleared_only = false;
uncleared_only = false;
show_expanded = false;
show_related = false;
show_inverted = false;
show_subtotals = true;
show_empty = false;
begin_date = -1;
end_date = -1;
have_date_mask = false;
period = PERIOD_NONE;
predicate = NULL;
sort_order = NULL;
}
~constraints_t();
std::time_t begin() const {
return begin_date == -1 ? 0 : begin_date;
~constraints_t() {
if (predicate) delete predicate;
}
std::time_t end() const {
return end_date == -1 ? std::time(NULL) : end_date;
bool operator ()(const transaction_t * xact) const {
if (! predicate) {
return true;
} else {
item_t temp;
temp.date = xact->entry->date;
temp.payee = xact->entry->payee;
temp.account = xact->account;
return predicate->compute(&temp);
}
}
bool matches_date_range(const std::time_t date) const;
bool operator ()(const entry_t * entry) const {
if (! predicate) {
return true;
} else {
item_t temp;
temp.date = entry->date;
temp.payee = entry->payee;
bool operator ()(const transaction_t * xact) const;
bool operator ()(const entry_t * entry) const;
bool operator ()(const item_t * item) const;
// Although there may be conflicting account masks for the whole
// set of transactions -- for example, /rent/&!/expenses/, which
// might match one by not another transactions -- we let the
// entry through if at least one of the transactions meets the
// criterion
for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end();
i++) {
temp.account = (*i)->account;
if (predicate->compute(&temp))
return true;
}
return false;
}
}
bool operator ()(const item_t * item) const {
return ! predicate || predicate->compute(item);
}
};
typedef constrained_iterator<transactions_list::const_iterator, transaction_t *,

169
expr.cc
View file

@ -2,11 +2,95 @@
#include "error.h"
#include "textual.h"
#include <pcre.h>
namespace ledger {
balance_t node_t::compute(const item_t * item,
const std::time_t begin,
const std::time_t end) const
mask_t::mask_t(const std::string& pat) : exclude(false)
{
const char * p = pat.c_str();
if (*p == '-') {
exclude = true;
p++;
while (std::isspace(*p))
p++;
}
else if (*p == '+') {
p++;
while (std::isspace(*p))
p++;
}
pattern = p;
const char *error;
int erroffset;
regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
&error, &erroffset, NULL);
if (! regexp)
std::cerr << "Warning: Failed to compile regexp: " << pattern
<< std::endl;
}
mask_t::mask_t(const mask_t& m) : exclude(m.exclude), pattern(m.pattern)
{
const char *error;
int erroffset;
regexp = pcre_compile(pattern.c_str(), PCRE_CASELESS,
&error, &erroffset, NULL);
assert(regexp);
}
bool mask_t::match(const std::string& str) const
{
static int ovec[30];
int result = pcre_exec((pcre *)regexp, NULL,
str.c_str(), str.length(), 0, 0, ovec, 30);
return result >= 0 && ! exclude;
}
mask_t::~mask_t() {
pcre_free((pcre *)regexp);
}
#if 1
bool matches(const masks_list& regexps, const std::string& str,
bool * by_exclusion)
{
if (regexps.empty())
return false;
bool match = false;
bool definite = false;
for (masks_list::const_iterator r = regexps.begin();
r != regexps.end();
r++) {
static int ovec[30];
int result = pcre_exec((pcre *)(*r).regexp, NULL,
str.c_str(), str.length(), 0, 0, ovec, 30);
if (result >= 0) {
match = ! (*r).exclude;
definite = true;
}
else if ((*r).exclude) {
if (! match)
match = ! definite;
}
else {
definite = true;
}
}
if (by_exclusion)
*by_exclusion = match && ! definite && by_exclusion;
return match;
}
#endif
balance_t node_t::compute(const item_t * item) const
{
balance_t temp;
@ -44,35 +128,44 @@ balance_t node_t::compute(const item_t * item,
temp = amount_t((unsigned int) item->date);
break;
case CLEARED:
#if 0
temp = amount_t(item->state == CLEARED ? 1 : 0);
#endif
break;
case REAL:
#if 0
temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1);
#endif
break;
case INDEX:
temp = amount_t(item->index + 1);
break;
case BEGIN_DATE:
temp = amount_t((unsigned int) begin);
break;
case END_DATE:
temp = amount_t((unsigned int) end);
break;
case F_ARITH_MEAN:
assert(left);
temp = left->compute(item, begin, end);
temp = left->compute(item);
temp /= amount_t(item->index + 1);
break;
case F_NEG:
assert(left);
temp = left->compute(item, begin, end).negated();
temp = left->compute(item).negated();
break;
case F_ABS:
assert(left);
temp = abs(left->compute(item, begin, end));
temp = abs(left->compute(item));
break;
case F_REGEXP:
case F_PAYEE_MASK:
assert(mask);
temp = mask->match(item->payee);
break;
case F_ACCOUNT_MASK:
assert(mask);
temp = (item->account &&
mask->match(item->account->fullname())) ? 1 : 0;
@ -80,14 +173,12 @@ balance_t node_t::compute(const item_t * item,
case F_VALUE: {
assert(left);
temp = left->compute(item, begin, end);
temp = left->compute(item);
std::time_t moment = -1;
if (right) {
switch (right->type) {
case DATE: moment = item->date; break;
case BEGIN_DATE: moment = begin; break;
case END_DATE: moment = end; break;
case DATE: moment = item->date; break;
default:
throw compute_error("Invalid date passed to P(v,d)");
}
@ -97,15 +188,15 @@ balance_t node_t::compute(const item_t * item,
}
case O_NOT:
temp = left->compute(item, begin, end) ? 0 : 1;
temp = left->compute(item) ? 0 : 1;
break;
case O_QUES:
temp = left->compute(item, begin, end);
temp = left->compute(item);
if (temp)
temp = right->left->compute(item, begin, end);
temp = right->left->compute(item);
else
temp = right->right->compute(item, begin, end);
temp = right->right->compute(item);
break;
case O_AND:
@ -121,8 +212,8 @@ balance_t node_t::compute(const item_t * item,
case O_DIV: {
assert(left);
assert(right);
balance_t left_bal = left->compute(item, begin, end);
balance_t right_bal = right->compute(item, begin, end);
balance_t left_bal = left->compute(item);
balance_t right_bal = right->compute(item);
switch (type) {
case O_AND: temp = (left_bal && right_bal) ? 1 : 0; break;
case O_OR: temp = (left_bal || right_bal) ? 1 : 0; break;
@ -197,8 +288,8 @@ node_t * parse_term(std::istream& in)
case 'a': node = new node_t(node_t::AMOUNT); break;
case 'c': node = new node_t(node_t::COST); break;
case 'd': node = new node_t(node_t::DATE); break;
case 'b': node = new node_t(node_t::BEGIN_DATE); break;
case 'e': node = new node_t(node_t::END_DATE); break;
case 'X': node = new node_t(node_t::CLEARED); break;
case 'R': node = new node_t(node_t::REAL); break;
case 'i': node = new node_t(node_t::INDEX); break;
case 'B': node = new node_t(node_t::BALANCE); break;
case 'T': node = new node_t(node_t::TOTAL); break;
@ -256,8 +347,15 @@ node_t * parse_term(std::istream& in)
// Other
case '/': {
std::string ident;
bool payee_mask = false;
c = in.peek();
if (c == '/') {
payee_mask = true;
in.get(c);
c = in.peek();
}
while (! in.eof() && c != '/') {
in.get(c);
if (c == '\\')
@ -265,9 +363,11 @@ node_t * parse_term(std::istream& in)
ident += c;
c = in.peek();
}
if (c == '/') {
in.get(c);
node = new node_t(node_t::F_REGEXP);
node = new node_t(payee_mask ?
node_t::F_PAYEE_MASK : node_t::F_ACCOUNT_MASK);
node->mask = new mask_t(ident);
} else {
throw expr_error("Missing closing '/'");
@ -517,13 +617,13 @@ static void dump_tree(std::ostream& out, node_t * node)
case node_t::AMOUNT: out << "AMOUNT"; break;
case node_t::COST: out << "COST"; break;
case node_t::DATE: out << "DATE"; break;
case node_t::CLEARED: out << "CLEARED"; break;
case node_t::REAL: out << "REAL"; break;
case node_t::INDEX: out << "INDEX"; break;
case node_t::BALANCE: out << "BALANCE"; break;
case node_t::COST_BALANCE: out << "COST_BALANCE"; break;
case node_t::TOTAL: out << "TOTAL"; break;
case node_t::COST_TOTAL: out << "COST_TOTAL"; break;
case node_t::BEGIN_DATE: out << "BEGIN"; break;
case node_t::END_DATE: out << "END"; break;
case node_t::F_ARITH_MEAN:
out << "MEAN(";
@ -543,9 +643,14 @@ static void dump_tree(std::ostream& out, node_t * node)
out << ")";
break;
case node_t::F_REGEXP:
case node_t::F_PAYEE_MASK:
assert(node->mask);
out << "RE(" << node->mask->pattern << ")";
out << "P_MASK(" << node->mask->pattern << ")";
break;
case node_t::F_ACCOUNT_MASK:
assert(node->mask);
out << "A_MASK(" << node->mask->pattern << ")";
break;
case node_t::F_VALUE:

38
expr.h
View file

@ -3,10 +3,33 @@
#include "ledger.h"
#include "balance.h"
#include "constraint.h"
#include "item.h"
namespace ledger {
class mask_t
{
public:
bool exclude;
std::string pattern;
void * regexp;
explicit mask_t(const std::string& pattern);
mask_t(const mask_t&);
~mask_t();
bool match(const std::string& str) const;
};
#if 1
typedef std::list<mask_t> masks_list;
bool matches(const masks_list& regexps, const std::string& str,
bool * by_exclusion = NULL);
#endif
struct node_t
{
enum kind_t {
@ -18,6 +41,8 @@ struct node_t
AMOUNT,
COST,
DATE,
CLEARED,
REAL,
INDEX,
// Item totals
@ -26,16 +51,13 @@ struct node_t
TOTAL,
COST_TOTAL,
// Constraint details
BEGIN_DATE,
END_DATE,
// Functions
F_ARITH_MEAN,
F_VALUE,
F_NEG,
F_ABS,
F_REGEXP,
F_PAYEE_MASK,
F_ACCOUNT_MASK,
// Binary operators
O_ADD,
@ -73,9 +95,7 @@ struct node_t
if (right) delete right;
}
balance_t compute(const item_t * item,
const std::time_t begin = -1,
const std::time_t end = -1) const;
balance_t compute(const item_t * item) const;
};
node_t * parse_expr(std::istream& in);

View file

@ -14,8 +14,7 @@ std::string truncated(const std::string& str, unsigned int width)
return buf;
}
std::string maximal_account_name(const item_t * item,
const item_t * parent)
std::string maximal_account_name(const item_t * item, const item_t * parent)
{
std::string name = item->account->name;
for (const item_t * i = item->parent;

View file

@ -63,37 +63,12 @@ struct format_t
void format_elements(std::ostream& out, const item_t * item,
const item_t * displayed_parent = NULL) const;
#if 1
static balance_t compute_value(const item_t * item) {
if (value_expr)
return value_expr->compute(item);
else
return balance_t();
return value_expr ? value_expr->compute(item) : balance_t();
}
static balance_t compute_total(const item_t * item) {
if (total_expr)
return total_expr->compute(item);
else
return balance_t();
return total_expr ? total_expr->compute(item) : balance_t();
}
#else
static balance_t compute_value(const item_t * item,
const constraints_t& constraints) {
if (value_expr)
return value_expr->compute(item, constraints.begin(), constraints.end());
else
return balance_t();
}
static balance_t compute_total(const item_t * item,
const constraints_t& constraints) {
if (total_expr)
return total_expr->compute(item, constraints.begin(), constraints.end());
else
return balance_t();
}
#endif
};
} // namespace ledger

View file

@ -12,7 +12,6 @@ item_t * walk_accounts(const account_t * account,
{
item_t * item = new item_t;
item->account = account;
item->date = constraints.end() - 1;
for (constrained_transactions_list_const_iterator
i(account->transactions.begin(),

209
main.cc
View file

@ -23,53 +23,54 @@ namespace ledger {
static const std::string bal_fmt = "%20T%2_%-n\n";
void show_balances(std::ostream& out,
items_deque& items,
const constraints_t& constraints,
const format_t& format,
const item_t * displayed_parent)
unsigned int show_balances(std::ostream& out,
items_deque& items,
const constraints_t& constraints,
const node_t * sort_order,
const format_t& format,
const item_t * displayed_parent)
{
unsigned int headlines = 0;
for (items_deque::const_iterator i = items.begin();
i != items.end();
i++) {
const item_t * parent = displayed_parent;
bool by_exclusion = false;
std::string name = maximal_account_name(*i, parent);
const bool match = (constraints.show_expanded ||
(! constraints.account_masks.empty() &&
matches(constraints.account_masks, name,
&by_exclusion) &&
(! by_exclusion ||
displayed_parent->parent == NULL)) ||
(constraints.account_masks.empty() &&
displayed_parent->parent == NULL));
if (match && constraints(*i) &&
if (constraints(*i) &&
((*i)->subitems.size() != 1 ||
(*i)->total != (*i)->subitems[0]->total)) {
format.format_elements(out, *i, parent);
parent = *i;
if (! displayed_parent->parent)
headlines++;
}
if (constraints.sort_order)
(*i)->sort(constraints.sort_order);
if (sort_order)
(*i)->sort(sort_order);
show_balances(out, (*i)->subitems, constraints, format, parent);
if (constraints.show_expanded)
headlines += show_balances(out, (*i)->subitems, constraints,
sort_order, format, parent);
}
return headlines;
}
void balance_report(std::ostream& out,
item_t * top,
const constraints_t& constraints,
const format_t& format)
void balance_report(std::ostream& out,
item_t * top,
const constraints_t& constraints,
const node_t * sort_order,
const format_t& format)
{
if (constraints.sort_order)
top->sort(constraints.sort_order);
if (sort_order)
top->sort(sort_order);
show_balances(out, top->subitems, constraints, format, top);
unsigned int headlines = show_balances(out, top->subitems, constraints,
sort_order, format, top);
if (constraints.show_subtotals && top->subitems.size() > 1 && top->total) {
if (constraints.show_subtotals && headlines > 1 && top->total) {
std::cout << "--------------------\n";
format.format_elements(std::cout, top);
}
@ -127,11 +128,12 @@ static void report_value_change(std::ostream& out,
void register_report(std::ostream& out,
item_t * top,
const constraints_t& constraints,
const node_t * sort_order,
const format_t& first_line_format,
const format_t& next_lines_format)
{
if (constraints.sort_order)
top->sort(constraints.sort_order);
if (sort_order)
top->sort(sort_order);
balance_pair_t balance;
balance_pair_t last_reported;
@ -203,8 +205,8 @@ void register_report(std::ostream& out,
}
if (show_commodities_revalued)
report_value_change(out, constraints.end(), balance, last_reported,
constraints, first_line_format, next_lines_format);
report_value_change(out, -1, balance, last_reported, constraints,
first_line_format, next_lines_format);
}
@ -471,11 +473,17 @@ int main(int argc, char * argv[])
std::list<std::string> files;
ledger::ledger_t * journal = new ledger::ledger_t;
ledger::constraints_t constraints;
std::string predicate;
std::string format_string;
std::string sort_order;
std::string sort_str;
ledger::node_t * sort_order = NULL;
std::string value_expr = "a";
std::string total_expr = "T";
#ifdef DEBUG
bool debug = false;
#endif
// Initialize some variables based on environment variable settings
if (char * p = std::getenv("PRICE_HIST"))
@ -518,13 +526,19 @@ int main(int argc, char * argv[])
int c, index;
while (-1 !=
(c = getopt(argc, argv,
"+a:ABb:Ccd:DEe:F:f:Ghi:L:l:MN:noOP:p:QRS:st:T:UVvWXZ"))) {
"+ABb:Ccd:DEe:F:f:Ghi:L:l:MnoOP:p:QRS:st:T:UVvWXZz"))) {
switch (char(c)) {
// Basic options
case 'h':
show_help(std::cout);
break;
#ifdef DEBUG
case 'z':
debug = 1;
break;
#endif
case 'v':
std::cout
<< "Ledger " << ledger::version
@ -547,47 +561,49 @@ int main(int argc, char * argv[])
ledger::set_price_conversion(optarg);
break;
// Constraint options
case 'a':
constraints.account_masks.push_back(ledger::mask_t(optarg));
break;
case 'b':
if (! ledger::parse_date(optarg, &constraints.begin_date)) {
std::cerr << "Error: Bad begin date: " << optarg << std::endl;
return 1;
}
if (! predicate.empty())
predicate += "&";
predicate += "(d>=[";
predicate += optarg;
predicate += "])";
break;
case 'e':
if (! ledger::parse_date(optarg, &constraints.end_date)) {
std::cerr << "Error: Bad end date: " << optarg << std::endl;
return 1;
}
if (! predicate.empty())
predicate += "&";
predicate += "(d<[";
predicate += optarg;
predicate += "])";
break;
case 'c':
constraints.end_date = std::time(NULL);
break;
case 'd':
constraints.have_date_mask = true;
if (! ledger::parse_date_mask(optarg, &constraints.date_mask)) {
std::cerr << "Error: Bad date mask: " << optarg << std::endl;
return 1;
}
case 'c': {
if (! predicate.empty())
predicate += "&";
predicate += "(d<";
std::ostringstream now;
now << std::time(NULL);
predicate += now.str();
predicate += ")";
break;
}
case 'C':
constraints.cleared_only = true;
if (! predicate.empty())
predicate += "&";
predicate += "X";
break;
case 'U':
constraints.uncleared_only = true;
if (! predicate.empty())
predicate += "&";
predicate += "!X";
break;
case 'R':
constraints.real_only = true;
if (! predicate.empty())
predicate += "&";
predicate += "R";
break;
// Customizing output
@ -595,10 +611,6 @@ int main(int argc, char * argv[])
format_string = optarg;
break;
case 'M':
constraints.period = ledger::PERIOD_MONTHLY;
break;
case 'E':
constraints.show_empty = true;
break;
@ -612,7 +624,7 @@ int main(int argc, char * argv[])
break;
case 'S':
sort_order = optarg;
sort_str = optarg;
break;
case 'o':
@ -620,7 +632,11 @@ int main(int argc, char * argv[])
break;
case 'l':
constraints.predicate = ledger::parse_expr(optarg);
if (! predicate.empty())
predicate += "&";
predicate += "(";
predicate += optarg;
predicate += ")";
break;
// Commodity reporting
@ -761,17 +777,51 @@ int main(int argc, char * argv[])
index++;
break;
}
constraints.account_masks.push_back(ledger::mask_t(argv[index]));
constraints.show_expanded = true;
if (! predicate.empty())
predicate += "&";
if (argv[index][0] == '-') {
predicate += "(!/";
predicate += argv[index] + 1;
} else {
predicate += "(/";
predicate += argv[index];
}
predicate += "/)";
}
for (; index < argc; index++)
constraints.payee_masks.push_back(ledger::mask_t(argv[index]));
for (; index < argc; index++) {
constraints.show_expanded = true;
if (! predicate.empty())
predicate += "&";
if (argv[index][0] == '-') {
predicate += "(!//";
predicate += argv[index] + 1;
} else {
predicate += "(//";
predicate += argv[index];
}
predicate += "/)";
}
// Copy the constraints to the format object, and compile the value
// and total style strings
if (! sort_order.empty())
constraints.sort_order = ledger::parse_expr(sort_order);
if (! predicate.empty()) {
#ifdef DEBUG
if (debug)
std::cerr << "predicate = " << predicate << std::endl;
#endif
constraints.predicate = ledger::parse_expr(predicate);
}
if (! sort_str.empty())
sort_order = ledger::parse_expr(sort_str);
// Setup the meaning of %t and %T encountered in format strings
@ -784,8 +834,7 @@ int main(int argc, char * argv[])
#if 0
if (ledger::item_t * top
= ledger::walk_entries(journal->entries.begin(),
journal->entries.end(),
constraints)) {
journal->entries.end(), constraints)) {
ledger::format_t * format = new ledger::format_t(format_string);
ledger::entry_report(std::cout, top, *format);
#ifdef DEBUG
@ -808,15 +857,14 @@ int main(int argc, char * argv[])
}
#endif
}
else if (constraints.period == ledger::PERIOD_NONE &&
! constraints.sort_order && ! constraints.show_related &&
else if (! sort_order && ! constraints.show_related &&
(command == "balance" || command == "bal")) {
if (ledger::item_t * top
= ledger::walk_accounts(journal->master, constraints)) {
ledger::format_t * format
= new ledger::format_t(format_string.empty() ?
ledger::bal_fmt : format_string);
ledger::balance_report(std::cout, top, constraints, *format);
ledger::balance_report(std::cout, top, constraints, sort_order, *format);
#ifdef DEBUG
delete format;
delete top;
@ -832,7 +880,8 @@ int main(int argc, char * argv[])
ledger::format_t * format
= new ledger::format_t(format_string.empty() ?
ledger::bal_fmt : format_string);
ledger::balance_report(std::cout, top, constraints, *format);
ledger::balance_report(std::cout, top, constraints, sort_order,
*format);
#ifdef DEBUG
delete format;
delete top;
@ -862,7 +911,8 @@ int main(int argc, char * argv[])
ledger::format_t * format = new ledger::format_t(first_line_format);
ledger::format_t * nformat = new ledger::format_t(next_lines_format);
ledger::register_report(std::cout, top, constraints, *format, *nformat);
ledger::register_report(std::cout, top, constraints, sort_order,
*format, *nformat);
#ifdef DEBUG
delete format;
delete top;
@ -887,6 +937,9 @@ int main(int argc, char * argv[])
#ifdef DEBUG
delete journal;
if (sort_order)
delete sort_order;
if (ledger::format_t::value_expr)
delete ledger::format_t::value_expr;
if (ledger::format_t::total_expr)