fix to textual parsing, and a bit of reconstruction in main.cc

This commit is contained in:
John Wiegley 2004-08-19 02:30:56 -04:00
parent 41604e0ea6
commit f62a4906d1
10 changed files with 285 additions and 257 deletions

View file

@ -1,4 +1,5 @@
#include "ledger.h"
#include "error.h"
#include "util.h"
#include "gmp.h"
@ -7,8 +8,6 @@
#define MPZ(x) ((MP_INT *)(x))
#define INIT() if (! quantity) _init()
namespace ledger {
static mpz_t full_divisor;
@ -229,8 +228,14 @@ amount_t& amount_t::operator=(const double value)
amount_t& amount_t::operator+=(const amount_t& amt)
{
if (amt.quantity) {
assert(! commodity || commodity == amt.commodity);
INIT();
if (! quantity) {
_init();
commodity = amt.commodity;
}
if (commodity != amt.commodity)
throw amount_error("Adding amounts with different commodities");
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
}
return *this;
@ -239,8 +244,14 @@ amount_t& amount_t::operator+=(const amount_t& amt)
amount_t& amount_t::operator-=(const amount_t& amt)
{
if (amt.quantity) {
assert(commodity == amt.commodity);
INIT();
if (! quantity) {
_init();
commodity = amt.commodity;
}
if (commodity != amt.commodity)
throw amount_error("Subtracting amounts with different commodities");
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
}
return *this;
@ -372,7 +383,10 @@ bool amount_t::operator<(const amount_t& amt) const
return amt > 0;
if (! amt.quantity) // equivalent to zero
return *this < 0;
assert(commodity == amt.commodity);
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) < 0;
}
@ -382,7 +396,10 @@ bool amount_t::operator<=(const amount_t& amt) const
return amt >= 0;
if (! amt.quantity) // equivalent to zero
return *this <= 0;
assert(commodity == amt.commodity);
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) <= 0;
}
@ -392,7 +409,10 @@ bool amount_t::operator>(const amount_t& amt) const
return amt < 0;
if (! amt.quantity) // equivalent to zero
return *this > 0;
assert(commodity == amt.commodity);
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) > 0;
}
@ -402,16 +422,23 @@ bool amount_t::operator>=(const amount_t& amt) const
return amt <= 0;
if (! amt.quantity) // equivalent to zero
return *this >= 0;
assert(commodity == amt.commodity);
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) >= 0;
}
bool amount_t::operator==(const amount_t& amt) const
{
if (commodity != amt.commodity)
if (! quantity && ! amt.quantity)
return true;
else if (! quantity || ! amt.quantity)
return false;
assert(amt.quantity);
assert(quantity);
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) == 0;
}
@ -448,7 +475,10 @@ amount_t& amount_t::operator*=(const amount_t& amt)
if (! amt.quantity)
return *this;
INIT();
if (! quantity) {
_init();
commodity = amt.commodity;
}
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), full_divisor);
@ -461,7 +491,10 @@ amount_t& amount_t::operator/=(const amount_t& amt)
if (! amt.quantity)
return *this;
INIT();
if (! quantity) {
_init();
commodity = amt.commodity;
}
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
@ -474,7 +507,10 @@ amount_t& amount_t::operator%=(const amount_t& amt)
if (! amt.quantity)
return *this;
INIT();
if (! quantity) {
_init();
commodity = amt.commodity;
}
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
mpz_tdiv_r(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
@ -650,7 +686,7 @@ void parse_commodity(std::istream& in, std::string& symbol)
if (c == '"')
in.get(c);
else
assert(0);
throw amount_error("Quoted commodity symbol lacks closing quote");
} else {
READ_INTO(in, buf, 256, c, ! std::isspace(c) && ! std::isdigit(c) &&
c != '-' && c != '.');
@ -670,7 +706,8 @@ void amount_t::parse(std::istream& in)
unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
unsigned int precision = MAX_PRECISION;
INIT();
if (! quantity)
_init();
char c = peek_next_nonws(in);
if (std::isdigit(c) || c == '.' || c == '-') {
@ -794,7 +831,8 @@ void amount_t::read_quantity(std::istream& in)
in.read((char *)&len, sizeof(len));
if (len) {
in.read(buf, len);
INIT();
if (! quantity)
_init();
#ifdef WRITE_AMOUNTS_TEXTUALLY
buf[len] = '\0';
mpz_set_str(MPZ(quantity), buf, 10);

View file

@ -97,7 +97,8 @@ Commodity reporting:\n\
-A, --average report average transaction amount\n\
-D, --deviation report deviation from the average\n\
-X, --trend report average deviation from the average\n\
-Z, --weighted-trend same as trend, but older values less significant\n\n\
-Z, --weighted-trend same as trend, but older values are less significant\n\
(-D, -X and -Z make little sense in balance reports)\n\n\
Commands:\n\
balance [REGEXP]... show balance totals for matching accounts\n\
register [REGEXP]... show register of matching transactions\n\
@ -345,7 +346,7 @@ OPT_BEGIN(trend, "X") {
OPT_BEGIN(weighted_trend, "Z") {
config->value_expr = "a";
config->total_expr
= "MD(MO/(1+(((t-d)/(30*86400))<0?0:((t-d)/(30*86400)))))";
= "MD(MO/(1+(((N-d)/(30*86400))<0?0:((N-d)/(30*86400)))))";
} OPT_END(weighted_trend);
} // namespace ledger

24
error.h
View file

@ -8,8 +8,7 @@
namespace ledger {
class error : public std::exception
{
class error : public std::exception {
std::string reason;
public:
error(const std::string& _reason) throw() : reason(_reason) {}
@ -20,36 +19,37 @@ class error : public std::exception
}
};
class compute_error : public error
{
class amount_error : public error {
public:
amount_error(const std::string& reason) throw() : error(reason) {}
virtual ~amount_error() throw() {}
};
class compute_error : public error {
public:
compute_error(const std::string& reason) throw() : error(reason) {}
virtual ~compute_error() throw() {}
};
class value_expr_error : public error
{
class value_expr_error : public error {
public:
value_expr_error(const std::string& reason) throw() : error(reason) {}
virtual ~value_expr_error() throw() {}
};
class interval_expr_error : public error
{
class interval_expr_error : public error {
public:
interval_expr_error(const std::string& reason) throw() : error(reason) {}
virtual ~interval_expr_error() throw() {}
};
class format_error : public error
{
class format_error : public error {
public:
format_error(const std::string& reason) throw() : error(reason) {}
virtual ~format_error() throw() {}
};
class parse_error : public error
{
class parse_error : public error {
unsigned int line;
std::string file;
public:

151
format.cc
View file

@ -52,97 +52,98 @@ element_t * format_t::parse_elements(const std::string& fmt)
char * q = buf;
for (const char * p = fmt.c_str(); *p; p++) {
if (*p == '%') {
if (! result) {
current = result = new element_t;
} else {
current->next = new element_t;
current = current->next;
}
if (*p != '%') {
*q++ = *p;
continue;
}
if (q != buf) {
current->type = element_t::STRING;
current->chars = std::string(buf, q);
q = buf;
if (! result) {
current = result = new element_t;
} else {
current->next = new element_t;
current = current->next;
}
current->next = new element_t;
current = current->next;
}
if (q != buf) {
current->type = element_t::STRING;
current->chars = std::string(buf, q);
q = buf;
current->next = new element_t;
current = current->next;
}
++p;
if (*p == '-') {
current->align_left = true;
++p;
if (*p == '-') {
current->align_left = true;
++p;
}
}
int num = 0;
int num = 0;
while (*p && std::isdigit(*p)) {
num *= 10;
num += *p++ - '0';
}
current->min_width = num;
if (*p == '.') {
++p;
num = 0;
while (*p && std::isdigit(*p)) {
num *= 10;
num += *p++ - '0';
}
current->min_width = num;
current->max_width = num;
if (current->min_width == 0)
current->min_width = current->max_width;
}
if (*p == '.') {
++p;
num = 0;
while (*p && std::isdigit(*p)) {
num *= 10;
num += *p++ - '0';
}
current->max_width = num;
if (current->min_width == 0)
current->min_width = current->max_width;
}
switch (*p) {
case '%':
current->type = element_t::STRING;
current->chars = "%";
break;
switch (*p) {
case '%':
current->type = element_t::STRING;
current->chars = "%";
break;
case '(': {
++p;
const char * b = p;
while (*p && *p != ')')
p++;
if (*p != ')')
throw format_error("Missing ')'");
case '(': {
++p;
const char * b = p;
while (*p && *p != ')')
p++;
if (*p != ')')
throw format_error("Missing ')'");
current->type = element_t::VALUE_EXPR;
current->val_expr = parse_value_expr(std::string(b, p));
break;
}
current->type = element_t::VALUE_EXPR;
current->val_expr = parse_value_expr(std::string(b, p));
break;
}
case '[': {
++p;
const char * b = p;
while (*p && *p != ']')
p++;
if (*p != ']')
throw format_error("Missing ']'");
case '[': {
++p;
const char * b = p;
while (*p && *p != ']')
p++;
if (*p != ']')
throw format_error("Missing ']'");
current->type = element_t::DATE_STRING;
current->chars = std::string(b, p);
break;
}
current->type = element_t::DATE_STRING;
current->chars = std::string(b, p);
break;
}
case 'D':
current->type = element_t::DATE_STRING;
current->chars = format_t::date_format;
break;
case 'D':
current->type = element_t::DATE_STRING;
current->chars = format_t::date_format;
break;
case 'X': current->type = element_t::CLEARED; break;
case 'C': current->type = element_t::CODE; break;
case 'P': current->type = element_t::PAYEE; break;
case 'n': current->type = element_t::ACCOUNT_NAME; break;
case 'N': current->type = element_t::ACCOUNT_FULLNAME; break;
case 'o': current->type = element_t::OPT_AMOUNT; break;
case 't': current->type = element_t::VALUE; break;
case 'T': current->type = element_t::TOTAL; break;
case '_': current->type = element_t::SPACER; break;
}
} else {
*q++ = *p;
case 'X': current->type = element_t::CLEARED; break;
case 'C': current->type = element_t::CODE; break;
case 'P': current->type = element_t::PAYEE; break;
case 'n': current->type = element_t::ACCOUNT_NAME; break;
case 'N': current->type = element_t::ACCOUNT_FULLNAME; break;
case 'o': current->type = element_t::OPT_AMOUNT; break;
case 't': current->type = element_t::VALUE; break;
case 'T': current->type = element_t::TOTAL; break;
case '_': current->type = element_t::SPACER; break;
}
}

View file

@ -113,10 +113,6 @@ class format_transactions : public item_handler<transaction_t>
first_line_format(_first_line_format),
next_lines_format(_next_lines_format), last_entry(NULL) {}
virtual ~format_transactions() {
flush();
}
virtual void flush() {
output_stream.flush();
}
@ -148,10 +144,6 @@ class format_account : public item_handler<account_t>
: output_stream(_output_stream), format(_format),
disp_pred(display_predicate) {}
virtual ~format_account() {
flush();
}
static bool disp_subaccounts_p(const account_t * account,
const item_predicate<account_t>& disp_pred,
const account_t *& to_show);
@ -201,10 +193,6 @@ class format_equity : public item_handler<account_t>
first_line_format.format_elements(output_stream, details_t(&header_entry));
}
virtual ~format_equity() {
flush();
}
virtual void flush() {
account_t summary(NULL, "Equity:Opening Balances");
summary.value = - total;

227
main.cc
View file

@ -106,16 +106,19 @@ regexps_to_predicate(std::list<std::string>::const_iterator begin,
if (i != regexps.begin()) {
config->predicate += "!";
}
else if (add_account_short_masks &&
(*i).find(':') == std::string::npos) {
if (! config->display_predicate.empty())
config->display_predicate += "&";
else if (! config->show_empty)
config->display_predicate += "T&";
else if (add_account_short_masks) {
if ((*i).find(':') != std::string::npos) {
config->show_subtotal = true;
} else {
if (! config->display_predicate.empty())
config->display_predicate += "&";
else if (! config->show_empty)
config->display_predicate += "T&";
config->display_predicate += "///(?:";
config->display_predicate += *i;
config->display_predicate += ")/";
config->display_predicate += "///(?:";
config->display_predicate += *i;
config->display_predicate += ")/";
}
}
if (! account_regexp)
@ -257,7 +260,28 @@ int main(int argc, char * argv[], char * envp[])
return 1;
}
// Process the remaining command-line arguments
// Configure some other options depending on report type
bool show_all_related = false;
if (command == "p" || command == "e") {
config->show_related =
show_all_related =
config->show_subtotal = true;
}
else if (command == "E") {
config->show_subtotal = true;
}
else if (config->show_related) {
if (command == "r") {
config->show_inverted = true;
} else {
config->show_subtotal = true;
show_all_related = true;
}
}
// Process remaining command-line arguments
std::auto_ptr<entry_t> new_entry;
if (command == "e") {
@ -278,7 +302,7 @@ int main(int argc, char * argv[], char * envp[])
regexps_to_predicate(i, args.end(), config);
}
// Compile the predicates
// Setup default value for the display predicate
if (config->display_predicate.empty()) {
if (command == "b") {
@ -295,7 +319,7 @@ int main(int argc, char * argv[], char * envp[])
}
}
// Compile the sorting criteria
// Compile sorting criteria
std::auto_ptr<value_expr_t> sort_order;
@ -319,7 +343,7 @@ int main(int argc, char * argv[], char * envp[])
}
}
// Setup the meaning of %t and %T, used in format strings
// Setup the values of %t and %T, used in format strings
try {
#ifdef NO_CLEANUP
@ -347,27 +371,7 @@ int main(int argc, char * argv[], char * envp[])
return 1;
}
// Configure some option depending on the report type
bool show_all_related = false;
if (command == "p" || command == "e") {
config->show_related =
show_all_related =
config->show_subtotal = true;
}
else if (command == "E") {
config->show_subtotal = true;
}
else if (config->show_related) {
if (command == "r")
config->show_inverted = true;
else
show_all_related = true;
}
// Setup a few local and global variables, depending on the config
// settings.
// Setup local and global variables, depending on config settings.
std::auto_ptr<std::ostream> output_stream;
std::auto_ptr<interval_t> report_interval;
@ -458,59 +462,23 @@ int main(int argc, char * argv[], char * envp[])
TIMER_START(report_gen);
if (command == "b") {
std::auto_ptr<item_handler<transaction_t> > formatter;
formatter.reset(new add_to_account_value);
if (config->show_related)
formatter.reset(new related_transactions(formatter.release(),
show_all_related));
formatter.reset(new filter_transactions(formatter.release(),
config->predicate));
walk_entries(journal->entries, *formatter);
formatter->flush();
// Stack up all the formatter needed to fulfills the user's
// requests. Some of these are order dependent, in terms of
// whether calc_transactions occurs before or after them.
format_account acct_formatter(OUT(), format, config->display_predicate);
sum_accounts(journal->master);
walk_accounts(journal->master, acct_formatter, sort_order.get());
acct_formatter.flush();
std::auto_ptr<item_handler<transaction_t> > formatter;
journal->master->value = journal->master->total;
if (format_account::display_account(journal->master,
item_predicate<account_t>("T"),
true)) {
std::string end_format = "--------------------\n";
format.reset(end_format + f);
format.format_elements(OUT(), details_t(journal->master));
}
}
else if (command == "E") {
std::auto_ptr<item_handler<transaction_t> > formatter;
formatter.reset(new add_to_account_value);
formatter.reset(new filter_transactions(formatter.release(),
config->predicate));
walk_entries(journal->entries, *formatter);
formatter->flush();
format_equity acct_formatter(OUT(), format, nformat,
config->display_predicate);
sum_accounts(journal->master);
walk_accounts(journal->master, acct_formatter, sort_order.get());
acct_formatter.flush();
}
else if (command == "e") {
format_transactions formatter(OUT(), format, nformat);
walk_transactions(new_entry->transactions, formatter);
formatter.flush();
}
else {
std::auto_ptr<item_handler<transaction_t> > formatter;
// Stack up all the formatter needed to fulfills the user's
// requests. Some of these are order dependent, in terms of
// whether calc_transactions occurs before or after them.
// format_transactions write each transaction received to the
// output stream.
// format_transactions write each transaction received to the
// output stream.
if (command == "b" || command == "E") {
#ifdef DEBUG_ENABLED
if (DEBUG("ledger.balance.items")) {
formatter.reset(new format_transactions(OUT(), format, nformat));
formatter.reset(new set_account_value(formatter.release()));
} else
#endif
formatter.reset(new set_account_value);
} else {
formatter.reset(new format_transactions(OUT(), format, nformat));
// sort_transactions will sort all the transactions it sees, based
@ -562,36 +530,69 @@ int main(int argc, char * argv[], char * envp[])
interval_begin));
else if (config->days_of_the_week)
formatter.reset(new dow_transactions(formatter.release()));
// 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 (config->show_related)
formatter.reset(new related_transactions(formatter.release(),
show_all_related));
// This filter_transactions will only pass through transactions
// matching the `predicate'.
formatter.reset(new filter_transactions(formatter.release(),
config->predicate));
// Once the filters are chained, walk `journal's entries and start
// feeding each transaction that matches `predicate' to the chain.
walk_entries(journal->entries, *formatter);
formatter->flush();
#ifdef DEBUG_ENABLED
// The transaction display flags (dflags) are not recorded in the
// binary cache, and only need to be cleared if the transactions
// are to be displayed a second time.
clear_display_flags cleanup;
walk_entries(journal->entries, cleanup);
cleanup.flush();
#endif
}
// 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 (config->show_related)
formatter.reset(new related_transactions(formatter.release(),
show_all_related));
// This filter_transactions will only pass through transactions
// matching the `predicate'.
formatter.reset(new filter_transactions(formatter.release(),
config->predicate));
// Once the filters are chained, walk `journal's entries and start
// feeding each transaction that matches `predicate' to the chain.
if (command == "e")
walk_transactions(new_entry->transactions, *formatter);
else
walk_entries(journal->entries, *formatter);
formatter->flush();
// At this point all printing is finished if doing a register
// report; but if it's a balance or equity report, we've only
// finished calculating the totals and there is still reporting to
// be done.
if (command == "b") {
format_account acct_formatter(OUT(), format, config->display_predicate);
sum_accounts(journal->master);
walk_accounts(journal->master, acct_formatter, sort_order.get());
acct_formatter.flush();
journal->master->value = journal->master->total;
if (format_account::display_account(journal->master,
item_predicate<account_t>("T"),
true)) {
std::string end_format = "--------------------\n";
format.reset(end_format + f);
format.format_elements(OUT(), details_t(journal->master));
}
}
else if (command == "E") {
format_equity acct_formatter(OUT(), format, nformat,
config->display_predicate);
sum_accounts(journal->master);
walk_accounts(journal->master, acct_formatter, sort_order.get());
acct_formatter.flush();
}
#ifndef NO_CLEANUP
// The transaction display flags (dflags) are not recorded in the
// binary cache, and only need to be cleared if the transactions
// are to be displayed a second time.
clear_display_flags cleanup;
walk_entries(journal->entries, cleanup);
cleanup.flush();
#endif
TIMER_STOP(report_gen);
// Save the cache, if need be

View file

@ -9,3 +9,4 @@
2004/05/29 Restaurant
Expenses:Food $50.00
Liabilities:MasterCard

View file

@ -110,6 +110,10 @@ transaction_t * parse_transaction(std::istream& in, account_t * account,
in.getline(line, MAX_LINE);
linenum++;
// Skip a possible blank line
if (*skip_ws(line) == '\0')
return NULL;
return parse_transaction_text(line, account, entry);
}
@ -321,8 +325,7 @@ unsigned int parse_textual_journal(std::istream& in, journal_t * journal,
if (peek_next_nonws(in) != '\n') {
in.getline(line, MAX_LINE);
linenum++;
throw parse_error(path, linenum,
"Ignoring entry beginning with whitespace");
throw parse_error(path, linenum, "Line begins with whitespace");
}
// fall through...

View file

@ -42,7 +42,7 @@ void calc_transactions::operator()(transaction_t * xact)
xact->cost.negate();
}
last_xact = xact;
last_xact = xact;
}
void collapse_transactions::report_cumulative_subtotal()
@ -59,8 +59,8 @@ void collapse_transactions::report_cumulative_subtotal()
for (amounts_map::const_iterator i = result.amounts.begin();
i != result.amounts.end();
i++) {
transaction_t * total_xact = new transaction_t(last_entry,
totals_account);
transaction_t * total_xact
= new transaction_t(last_entry, totals_account);
xact_temps.push_back(total_xact);
total_xact->amount = (*i).second;

33
walk.h
View file

@ -18,18 +18,19 @@ struct item_handler {
public:
item_handler() : handler(NULL) {}
item_handler(item_handler * _handler) : handler(_handler) {}
virtual ~item_handler() {}
virtual void close() {
if (handler) {
virtual ~item_handler() {
if (handler)
delete handler;
handler = NULL;
}
}
virtual void flush() {
if (handler)
handler->flush();
}
virtual void operator()(T * item) = 0;
virtual void operator()(T * item) {
if (handler)
(*handler)(item);
}
};
template <typename T>
@ -112,12 +113,18 @@ class clear_display_flags : public item_handler<transaction_t>
}
};
class add_to_account_value : public item_handler<transaction_t>
class set_account_value : public item_handler<transaction_t>
{
public:
set_account_value(item_handler<transaction_t> * handler = NULL)
: item_handler<transaction_t>(handler) {}
virtual void operator()(transaction_t * xact) {
xact->account->value += *xact;
xact->account->count++;
if (handler)
(*handler)(xact);
}
};
@ -131,7 +138,6 @@ class sort_transactions : public item_handler<transaction_t>
const value_expr_t * _sort_order)
: item_handler<transaction_t>(handler),
sort_order(_sort_order) {}
virtual ~sort_transactions() { close(); }
virtual void flush();
virtual void operator()(transaction_t * xact) {
@ -148,8 +154,6 @@ class filter_transactions : public item_handler<transaction_t>
const std::string& predicate)
: item_handler<transaction_t>(handler), pred(predicate) {}
virtual ~filter_transactions() { close(); }
virtual void operator()(transaction_t * xact) {
if (pred(xact))
(*handler)(xact);
@ -167,8 +171,6 @@ class calc_transactions : public item_handler<transaction_t>
: item_handler<transaction_t>(handler),
last_xact(NULL), inverted(_inverted) {}
virtual ~calc_transactions() { close(); }
virtual void operator()(transaction_t * xact);
};
@ -189,7 +191,6 @@ class collapse_transactions : public item_handler<transaction_t>
}
virtual ~collapse_transactions() {
close();
delete totals_account;
for (transactions_deque::iterator i = xact_temps.begin();
i != xact_temps.end();
@ -237,8 +238,6 @@ class changed_value_transactions : public item_handler<transaction_t>
changed_values_only(_changed_values_only), last_xact(NULL) {}
virtual ~changed_value_transactions() {
close();
for (entries_deque::iterator i = entry_temps.begin();
i != entry_temps.end();
i++)
@ -275,8 +274,6 @@ class subtotal_transactions : public item_handler<transaction_t>
: item_handler<transaction_t>(handler) {}
virtual ~subtotal_transactions() {
close();
for (entries_deque::iterator i = entry_temps.begin();
i != entry_temps.end();
i++)
@ -342,8 +339,6 @@ class related_transactions : public item_handler<transaction_t>
: item_handler<transaction_t>(handler),
also_matching(_also_matching) {}
virtual ~related_transactions() { close(); }
virtual void operator()(transaction_t * xact) {
for (transactions_list::iterator i = xact->entry->transactions.begin();
i != xact->entry->transactions.end();