ledger/constraint.cc
John Wiegley 94e76ae87e two major changes
Complete changed the way format strings are handled.  They are now
compiled first, which is far more efficient than what was being done
before.

Also, there is now a global ledger::commodity_t::commodities map,
which saves me from having to pass the current journal around to a
zillion different functions, for the sole purpose of making sure that
all commodity symbols that are parsed refer to the same commodity
object.
2004-07-30 21:57:02 -04:00

229 lines
5 KiB
C++

#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