summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2004-07-30 23:42:59 -0400
committerJohn Wiegley <johnw@newartisans.com>2004-07-30 23:42:59 -0400
commit493694f84869190340a035e524c78d6a6a71bf6c (patch)
treee87ab37f98b0fe2ec05ef3da92b67710cad25a87
parent94e76ae87e883291d13320738fe165c7a2a2415b (diff)
downloadfork-ledger-493694f84869190340a035e524c78d6a6a71bf6c.tar.gz
fork-ledger-493694f84869190340a035e524c78d6a6a71bf6c.tar.bz2
fork-ledger-493694f84869190340a035e524c78d6a6a71bf6c.zip
predicate are now used instead of constraints
-rw-r--r--Makefile3
-rw-r--r--NEWS2
-rw-r--r--constraint.cc229
-rw-r--r--constraint.h106
-rw-r--r--expr.cc165
-rw-r--r--expr.h38
-rw-r--r--format.cc3
-rw-r--r--format.h29
-rw-r--r--item.cc1
-rw-r--r--main.cc213
10 files changed, 343 insertions, 446 deletions
diff --git a/Makefile b/Makefile
index bbdee80d..4c57bab2 100644
--- a/Makefile
+++ b/Makefile
@@ -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++
diff --git a/NEWS b/NEWS
index 6ede1bef..e99cf1ad 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/constraint.cc b/constraint.cc
deleted file mode 100644
index fe00f4d2..00000000
--- a/constraint.cc
+++ /dev/null
@@ -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
diff --git a/constraint.h b/constraint.h
index 79e01fc6..7ee77581 100644
--- a/constraint.h
+++ b/constraint.h
@@ -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;
+
+ // 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 transaction_t * xact) const;
- bool operator ()(const entry_t * entry) const;
- bool operator ()(const item_t * item) const;
+ bool operator ()(const item_t * item) const {
+ return ! predicate || predicate->compute(item);
+ }
};
typedef constrained_iterator<transactions_list::const_iterator, transaction_t *,
diff --git a/expr.cc b/expr.cc
index 2020d20f..7536c260 100644
--- a/expr.cc
+++ b/expr.cc
@@ -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 INDEX:
- temp = amount_t(item->index + 1);
+ case CLEARED:
+#if 0
+ temp = amount_t(item->state == CLEARED ? 1 : 0);
+#endif
break;
- case BEGIN_DATE:
- temp = amount_t((unsigned int) begin);
+ case REAL:
+#if 0
+ temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1);
+#endif
break;
- case END_DATE:
- temp = amount_t((unsigned int) end);
+ case INDEX:
+ temp = amount_t(item->index + 1);
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_PAYEE_MASK:
+ assert(mask);
+ temp = mask->match(item->payee);
break;
- case F_REGEXP:
+ 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 << "P_MASK(" << node->mask->pattern << ")";
+ break;
+
+ case node_t::F_ACCOUNT_MASK:
assert(node->mask);
- out << "RE(" << node->mask->pattern << ")";
+ out << "A_MASK(" << node->mask->pattern << ")";
break;
case node_t::F_VALUE:
diff --git a/expr.h b/expr.h
index f4961bfc..9cf3f78e 100644
--- a/expr.h
+++ b/expr.h
@@ -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);
diff --git a/format.cc b/format.cc
index 6b5a4a9b..3fc65c2b 100644
--- a/format.cc
+++ b/format.cc
@@ -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;
diff --git a/format.h b/format.h
index 33443508..96faab3c 100644
--- a/format.h
+++ b/format.h
@@ -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();
- }
-#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();
+ return total_expr ? total_expr->compute(item) : balance_t();
}
-#endif
};
} // namespace ledger
diff --git a/item.cc b/item.cc
index abdeea9f..ada926db 100644
--- a/item.cc
+++ b/item.cc
@@ -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(),
diff --git a/main.cc b/main.cc
index d20e8c23..5e40309c 100644
--- a/main.cc
+++ b/main.cc
@@ -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;
- }
- 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;
- }
+ if (! predicate.empty())
+ predicate += "&";
+ predicate += "(d<[";
+ predicate += optarg;
+ predicate += "])";
+ break;
+
+ 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)