diff options
-rw-r--r-- | amount.cc | 76 | ||||
-rw-r--r-- | config.cc | 5 | ||||
-rw-r--r-- | error.h | 24 | ||||
-rw-r--r-- | format.cc | 155 | ||||
-rw-r--r-- | format.h | 12 | ||||
-rw-r--r-- | main.cc | 223 | ||||
-rw-r--r-- | sample.dat | 1 | ||||
-rw-r--r-- | textual.cc | 7 | ||||
-rw-r--r-- | walk.cc | 6 | ||||
-rw-r--r-- | walk.h | 33 |
10 files changed, 285 insertions, 257 deletions
@@ -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); @@ -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 @@ -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: @@ -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; + while (*p && std::isdigit(*p)) { + num *= 10; + num += *p++ - '0'; + } + current->min_width = num; - int num = 0; + if (*p == '.') { + ++p; + 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->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; - - case '(': { - ++p; - const char * b = p; - while (*p && *p != ')') - p++; - if (*p != ')') - throw format_error("Missing ')'"); + current->max_width = num; + if (current->min_width == 0) + current->min_width = current->max_width; + } - current->type = element_t::VALUE_EXPR; - current->val_expr = parse_value_expr(std::string(b, p)); - 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::DATE_STRING; - current->chars = std::string(b, p); - break; - } + 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; + } - 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; } } @@ -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; @@ -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&"; - - config->display_predicate += "///(?:"; - config->display_predicate += *i; - config->display_predicate += ")/"; + 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 += ")/"; + } } 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. + // 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(); -#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 + 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 @@ -9,3 +9,4 @@ 2004/05/29 Restaurant Expenses:Food $50.00 Liabilities:MasterCard + @@ -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... @@ -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; @@ -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(); |