diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | constraint.cc | 229 | ||||
-rw-r--r-- | constraint.h | 106 | ||||
-rw-r--r-- | expr.cc | 165 | ||||
-rw-r--r-- | expr.h | 38 | ||||
-rw-r--r-- | format.cc | 3 | ||||
-rw-r--r-- | format.h | 29 | ||||
-rw-r--r-- | item.cc | 1 | ||||
-rw-r--r-- | main.cc | 213 |
10 files changed, 343 insertions, 446 deletions
@@ -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++ @@ -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 *, @@ -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: @@ -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); @@ -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; @@ -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 @@ -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(), @@ -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) |