summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am47
-rw-r--r--NEWS3
-rwxr-xr-xacprep1
-rw-r--r--config.cc545
-rw-r--r--config.h42
-rw-r--r--configure.in5
-rw-r--r--derive.cc2
-rw-r--r--emacs.cc2
-rw-r--r--format.cc10
-rw-r--r--journal.cc10
-rw-r--r--ledger.el10
-rw-r--r--ledger.h12
-rw-r--r--main.cc295
-rw-r--r--parser.cc138
-rw-r--r--parser.h18
-rw-r--r--startup.cc54
-rw-r--r--textual.cc18
-rw-r--r--textual.h2
-rw-r--r--timing.h2
-rw-r--r--valexpr.cc6
-rw-r--r--valexpr.h6
-rw-r--r--walk.cc93
-rw-r--r--walk.h60
23 files changed, 756 insertions, 625 deletions
diff --git a/Makefile.am b/Makefile.am
index 09ddbd35..bdc1f868 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
-lib_LIBRARIES = libledger.a
-libledger_a_CXXFLAGS =
-libledger_a_SOURCES = \
+lib_LTLIBRARIES = libledger.la
+libledger_la_CXXFLAGS =
+libledger_la_SOURCES = \
amount.cc \
balance.cc \
binary.cc \
@@ -16,30 +16,32 @@ libledger_a_SOURCES = \
qif.cc \
quotes.cc \
reconcile.cc \
+ startup.cc \
textual.cc \
valexpr.cc \
value.cc \
walk.cc
if HAVE_EXPAT
-libledger_a_CXXFLAGS += -DHAVE_EXPAT=1
-libledger_a_SOURCES += gnucash.cc xml.cc
+libledger_la_CXXFLAGS += -DHAVE_EXPAT=1
+libledger_la_SOURCES += gnucash.cc xml.cc
endif
if HAVE_XMLPARSE
-libledger_a_CXXFLAGS += -DHAVE_XMLPARSE=1
-libledger_a_SOURCES += gnucash.cc xml.cc
+libledger_la_CXXFLAGS += -DHAVE_XMLPARSE=1
+libledger_la_SOURCES += gnucash.cc xml.cc
endif
if HAVE_LIBOFX
-libledger_a_CXXFLAGS += -DHAVE_LIBOFX=1
-libledger_a_SOURCES += ofx.cc
+libledger_la_CXXFLAGS += -DHAVE_LIBOFX=1
+libledger_la_SOURCES += ofx.cc
endif
if HAVE_BOOST_PYTHON
-libledger_a_CXXFLAGS += -DUSE_BOOST_PYTHON=1
-libledger_a_SOURCES += py_eval.cc
+libledger_la_CXXFLAGS += -DUSE_BOOST_PYTHON=1
+libledger_la_SOURCES += py_eval.cc
endif
if DEBUG
-libledger_a_CXXFLAGS += -DDEBUG_LEVEL=4
-libledger_a_SOURCES += debug.cc
+libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
+libledger_la_SOURCES += debug.cc
endif
+libledger_la_LDFLAGS = -version-info 3:0
pkginclude_HEADERS = \
acconf.h \
@@ -79,9 +81,9 @@ ledger_CXXFLAGS =
ledger_SOURCES = main.cc
if HAVE_BOOST_PYTHON
ledger_CXXFLAGS += -DUSE_BOOST_PYTHON=1
-ledger_LDADD = $(LIBOBJS) libledger.a -lboost_python -lpython$(PYTHON_VERSION)
+ledger_LDADD = $(LIBOBJS) libledger.la -lboost_python -lpython$(PYTHON_VERSION)
else
-ledger_LDADD = $(LIBOBJS) libledger.a
+ledger_LDADD = $(LIBOBJS) libledger.la
endif
if HAVE_EXPAT
ledger_CXXFLAGS += -DHAVE_EXPAT=1
@@ -98,6 +100,7 @@ endif
if DEBUG
ledger_CXXFLAGS += -DDEBUG_LEVEL=4
endif
+ledger_LDFLAGS = -static # for the sake of command-line speed
info_TEXINFOS = ledger.texi
@@ -123,15 +126,15 @@ else
HAVE_LIBOFX_VALUE = false
endif
-ledger.so: py_eval.cc libledger.a
- CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L." \
+ledger.so: py_eval.cc libledger.la
+ CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
python setup.py build --build-lib=.
install-exec-hook:
- CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L." \
+ CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
@@ -143,7 +146,7 @@ all-clean: maintainer-clean
rm -fr *~ .*~ .\#* *.html *.info *.pdf *.a *.so *.o *.lo *.la \
*.elc *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr *.pyc \
.gdb_history gmon.out h out TAGS ledger valexpr .deps \
- build AUTHORS COPYING INSTALL Makefile aclocal.m4 autom4te \
- acconf.h acconf.h.in config.guess config.sub configure \
- depcomp install-sh missing stamp texinfo.tex Makefile.in \
- results.out
+ .libs build AUTHORS COPYING INSTALL Makefile acconf.h \
+ acconf.h.in aclocal.m4 autom4te config.guess config.sub \
+ configure depcomp install-sh libtool ltconfig ltmain.sh \
+ missing stamp texinfo.tex Makefile.in
diff --git a/NEWS b/NEWS
index 76d52503..2875df4b 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@
* 2.5
+- Much internal restruction to allow the use of libledger.so in a
+ non-command-line environment.
+
- Effective dates may now be specified for entries:
2004/10/03=2004/09/30 Credit card company
diff --git a/acprep b/acprep
index c6e0c65d..d1f85196 100755
--- a/acprep
+++ b/acprep
@@ -2,6 +2,7 @@
touch AUTHORS COPYING
+glibtoolize --automake -f -c
aclocal
autoheader
if [ "$1" = "--dist" ]; then
diff --git a/config.cc b/config.cc
index d77472e5..674c1b0f 100644
--- a/config.cc
+++ b/config.cc
@@ -20,10 +20,11 @@
namespace ledger {
-config_t config;
std::list<option_t> config_options;
-config_t::config_t()
+static config_t * config = NULL;
+
+void config_t::reset()
{
amount_expr = "a";
total_expr = "O";
@@ -73,12 +74,13 @@ config_t::config_t()
cache_dirty = false;
}
-static void
-regexps_to_predicate(config_t& config, const std::string& command,
- std::list<std::string>::const_iterator begin,
- std::list<std::string>::const_iterator end,
- const bool account_regexp = false,
- const bool add_account_short_masks = false)
+void
+config_t::regexps_to_predicate(const std::string& command,
+ std::list<std::string>::const_iterator begin,
+ std::list<std::string>::const_iterator end,
+ const bool account_regexp,
+ const bool add_account_short_masks,
+ const bool logical_and)
{
std::string regexps[2];
@@ -110,12 +112,12 @@ regexps_to_predicate(config_t& config, const std::string& command,
if (regexps[i].empty())
continue;
- if (! config.predicate.empty())
- config.predicate += "&";
+ if (! predicate.empty())
+ predicate += logical_and ? "&" : "|";
int add_predicate = 0; // 1 adds /.../, 2 adds ///.../
if (i == 1) {
- config.predicate += "!";
+ predicate += "!";
}
else if (add_account_short_masks) {
if (regexps[i].find(':') != std::string::npos ||
@@ -124,7 +126,7 @@ regexps_to_predicate(config_t& config, const std::string& command,
regexps[i].find('+') != std::string::npos ||
regexps[i].find('[') != std::string::npos ||
regexps[i].find('(') != std::string::npos) {
- config.show_subtotal = true;
+ show_subtotal = true;
add_predicate = 1;
} else {
add_predicate = 2;
@@ -135,26 +137,49 @@ regexps_to_predicate(config_t& config, const std::string& command,
}
if (i != 1 && command == "b" && account_regexp) {
- if (! config.display_predicate.empty())
- config.display_predicate += "&";
- else if (! config.show_empty)
- config.display_predicate += "T&";
+ if (! display_predicate.empty())
+ display_predicate += "&";
+ else if (! show_empty)
+ display_predicate += "T&";
if (add_predicate == 2)
- config.display_predicate += "//";
- config.display_predicate += "/(?:";
- config.display_predicate += regexps[i];
- config.display_predicate += ")/";
+ display_predicate += "//";
+ display_predicate += "/(?:";
+ display_predicate += regexps[i];
+ display_predicate += ")/";
}
if (! account_regexp)
- config.predicate += "/";
- config.predicate += "/(?:";
- config.predicate += regexps[i];
- config.predicate += ")/";
+ predicate += "/";
+ predicate += "/(?:";
+ predicate += regexps[i];
+ predicate += ")/";
}
}
+bool config_t::process_option(const std::string& opt, const char * arg)
+{
+ config = this;
+ bool result = ::process_option(config_options, opt, arg);
+ config = NULL;
+ return result;
+}
+
+void config_t::process_arguments(int argc, char ** argv, const bool anywhere,
+ std::list<std::string>& args)
+{
+ config = this;
+ ::process_arguments(config_options, argc, argv, anywhere, args);
+ config = NULL;
+}
+
+void config_t::process_environment(char ** envp, const std::string& tag)
+{
+ config = this;
+ ::process_environment(config_options, envp, tag);
+ config = NULL;
+}
+
void config_t::process_options(const std::string& command,
strings_list::iterator arg,
strings_list::iterator args_end)
@@ -189,11 +214,11 @@ void config_t::process_options(const std::string& command,
break;
if (i != arg)
- regexps_to_predicate(*this, command, arg, i, true,
+ regexps_to_predicate(command, arg, i, true,
(command == "b" && ! show_subtotal &&
display_predicate.empty()));
if (i != args_end && ++i != args_end)
- regexps_to_predicate(*this, command, i, args_end);
+ regexps_to_predicate(command, i, args_end);
}
// Setup the default value for the display predicate
@@ -258,83 +283,157 @@ void config_t::process_options(const std::string& command,
format_t::date_format = date_format;
}
-void parse_ledger_data(journal_t * journal, parser_t * cache_parser,
- parser_t * text_parser, parser_t * xml_parser)
+item_handler<transaction_t> *
+config_t::chain_xact_handlers(const std::string& command,
+ item_handler<transaction_t> * base_formatter,
+ journal_t * journal,
+ account_t * master,
+ std::list<item_handler<transaction_t> *>& ptrs)
{
- int entry_count = 0;
-
- DEBUG_PRINT("ledger.config.cache", "3. use_cache = " << config.use_cache);
-
- if (! config.init_file.empty() &&
- access(config.init_file.c_str(), R_OK) != -1) {
- if (parse_journal_file(config.init_file, journal) ||
- journal->auto_entries.size() > 0 ||
- journal->period_entries.size() > 0)
- throw error(std::string("Entries found in initialization file '") +
- config.init_file + "'");
+ item_handler<transaction_t> * formatter = NULL;
+
+ ptrs.push_back(formatter = base_formatter);
+
+ // format_transactions write each transaction received to the
+ // output stream.
+ if (! (command == "b" || command == "E")) {
+ // truncate_entries cuts off a certain number of _entries_ from
+ // being displayed. It does not affect calculation.
+ if (head_entries || tail_entries)
+ ptrs.push_back(formatter =
+ new truncate_entries(formatter,
+ head_entries, tail_entries));
+
+ // filter_transactions will only pass through transactions
+ // matching the `display_predicate'.
+ if (! display_predicate.empty())
+ ptrs.push_back(formatter =
+ new filter_transactions(formatter,
+ display_predicate));
+
+ // calc_transactions computes the running total. When this
+ // appears will determine, for example, whether filtered
+ // transactions are included or excluded from the running total.
+ ptrs.push_back(formatter = new calc_transactions(formatter));
+
+ // reconcile_transactions will pass through only those
+ // transactions which can be reconciled to a given balance
+ // (calculated against the transactions which it receives).
+ if (! reconcile_balance.empty()) {
+ value_t target_balance(reconcile_balance);
+ time_t cutoff = now;
+ if (! reconcile_date.empty())
+ parse_date(reconcile_date.c_str(), &cutoff);
+ ptrs.push_back(formatter =
+ new reconcile_transactions(formatter, target_balance,
+ cutoff));
+ }
- journal->sources.pop_front(); // remove init file
+ // sort_transactions will sort all the transactions it sees, based
+ // on the `sort_order' value expression.
+ if (! sort_string.empty())
+ ptrs.push_back(formatter =
+ new sort_transactions(formatter, sort_string));
+
+ // changed_value_transactions adds virtual transactions to the
+ // list to account for changes in market value of commodities,
+ // which otherwise would affect the running total unpredictably.
+ if (show_revalued)
+ ptrs.push_back(formatter =
+ new changed_value_transactions(formatter,
+ show_revalued_only));
+
+ // collapse_transactions causes entries with multiple transactions
+ // to appear as entries with a subtotaled transaction for each
+ // commodity used.
+ if (show_collapsed)
+ ptrs.push_back(formatter = new collapse_transactions(formatter));
}
- if (cache_parser && config.use_cache &&
- ! config.cache_file.empty() &&
- ! config.data_file.empty()) {
- DEBUG_PRINT("ledger.config.cache", "using_cache " << config.cache_file);
- config.cache_dirty = true;
- if (access(config.cache_file.c_str(), R_OK) != -1) {
- std::ifstream stream(config.cache_file.c_str());
- if (cache_parser->test(stream)) {
- std::string price_db_orig = journal->price_db;
- journal->price_db = config.price_db;
- entry_count += cache_parser->parse(stream, journal, NULL,
- &config.data_file);
- if (entry_count > 0)
- config.cache_dirty = false;
- else
- journal->price_db = price_db_orig;
- }
- }
+ // subtotal_transactions combines all the transactions it receives
+ // into one subtotal entry, which has one transaction for each
+ // commodity in each account.
+ //
+ // period_transactions is like subtotal_transactions, but it
+ // subtotals according to time periods rather than totalling
+ // everything.
+ //
+ // dow_transactions is like period_transactions, except that it
+ // reports all the transactions that fall on each subsequent day
+ // of the week.
+ if (show_subtotal && ! (command == "b" || command == "E"))
+ ptrs.push_back(formatter = new subtotal_transactions(formatter));
+
+ if (days_of_the_week)
+ ptrs.push_back(formatter = new dow_transactions(formatter));
+ else if (by_payee)
+ ptrs.push_back(formatter = new by_payee_transactions(formatter));
+
+ if (! report_period.empty()) {
+ ptrs.push_back(formatter =
+ new interval_transactions(formatter,
+ report_period,
+ report_period_sort));
+ ptrs.push_back(formatter = new sort_transactions(formatter, "d"));
}
- if (entry_count == 0 && ! config.data_file.empty()) {
- account_t * account = NULL;
- if (! config.account.empty())
- account = journal->find_account(config.account);
-
- journal->price_db = config.price_db;
- if (! journal->price_db.empty() &&
- access(journal->price_db.c_str(), R_OK) != -1) {
- if (parse_journal_file(journal->price_db, journal)) {
- throw error("Entries not allowed in price history file");
- } else {
- DEBUG_PRINT("ledger.config.cache",
- "read price database " << journal->price_db);
- journal->sources.pop_back();
- }
- }
-
- DEBUG_PRINT("ledger.config.cache",
- "rejected cache, parsing " << config.data_file);
- if (config.data_file == "-") {
- config.use_cache = false;
- journal->sources.push_back("<stdin>");
- if (xml_parser && std::cin.peek() == '<')
- entry_count += xml_parser->parse(std::cin, journal, account);
- else
- entry_count += text_parser->parse(std::cin, journal, account);
- }
- else if (access(config.data_file.c_str(), R_OK) != -1) {
- entry_count += parse_journal_file(config.data_file, journal, account);
- if (! journal->price_db.empty())
- journal->sources.push_back(journal->price_db);
- }
+ // invert_transactions inverts the value of the transactions it
+ // receives.
+ if (show_inverted)
+ ptrs.push_back(formatter = new invert_transactions(formatter));
+
+ // 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 (show_related)
+ ptrs.push_back(formatter =
+ new related_transactions(formatter,
+ show_all_related));
+
+ // This filter_transactions will only pass through transactions
+ // matching the `predicate'.
+ if (! predicate.empty())
+ ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
+
+ // budget_transactions takes a set of transactions from a data
+ // file and uses them to generate "budget transactions" which
+ // balance against the reported transactions.
+ //
+ // forecast_transactions is a lot like budget_transactions, except
+ // that it adds entries only for the future, and does not balance
+ // them against anything but the future balance.
+
+ if (budget_flags) {
+ budget_transactions * handler
+ = new budget_transactions(formatter, budget_flags);
+ handler->add_period_entries(journal->period_entries);
+ ptrs.push_back(formatter = handler);
+
+ // Apply this before the budget handler, so that only matching
+ // transactions are calculated toward the budget. The use of
+ // filter_transactions above will further clean the results so
+ // that no automated transactions that don't match the filter get
+ // reported.
+ if (! predicate.empty())
+ ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
+ }
+ else if (! forecast_limit.empty()) {
+ forecast_transactions * handler
+ = new forecast_transactions(formatter, forecast_limit);
+ handler->add_period_entries(journal->period_entries);
+ ptrs.push_back(formatter = handler);
+
+ // See above, under budget_transactions.
+ if (! predicate.empty())
+ ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
}
- if (entry_count == 0)
- throw error("Please specify ledger file using -f"
- " or LEDGER_FILE environment variable.");
+ if (comm_as_payee)
+ ptrs.push_back(formatter = new set_comm_as_payee(formatter));
- VALIDATE(journal->valid());
+ return formatter;
}
static void show_version(std::ostream& out)
@@ -586,38 +685,42 @@ OPT_BEGIN(version, "v") {
} OPT_END(version);
OPT_BEGIN(init_file, "i:") {
- config.init_file = optarg;
+ config->init_file = optarg;
} OPT_END(init_file);
OPT_BEGIN(file, "f:") {
if (std::string(optarg) == "-" || access(optarg, R_OK) != -1)
- config.data_file = optarg;
+ config->data_file = optarg;
else
throw error(std::string("The ledger file '") + optarg +
"' does not exist or is not readable");
} OPT_END(file);
OPT_BEGIN(cache, ":") {
- config.cache_file = optarg;
+ config->cache_file = optarg;
} OPT_END(cache);
OPT_BEGIN(no_cache, "") {
- config.cache_file = "<none>";
+ config->cache_file = "<none>";
} OPT_END(no_cache);
OPT_BEGIN(output, "o:") {
if (std::string(optarg) != "-")
- config.output_file = optarg;
+ config->output_file = optarg;
} OPT_END(output);
OPT_BEGIN(account, "a:") {
- config.account = optarg;
+ config->account = optarg;
} OPT_END(account);
//////////////////////////////////////////////////////////////////////
//
// Report filtering
+OPT_BEGIN(effective, "") {
+ transaction_t::use_effective_date = true;
+} OPT_END(effective);
+
OPT_BEGIN(begin, "b:") {
char buf[128];
interval_t interval(optarg);
@@ -627,11 +730,11 @@ OPT_BEGIN(begin, "b:") {
throw error(std::string("Could not determine beginning of period '") +
optarg + "'");
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "d>=[";
- config.predicate += buf;
- config.predicate += "]";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "d>=[";
+ config->predicate += buf;
+ config->predicate += "]";
} OPT_END(begin);
OPT_BEGIN(end, "e:") {
@@ -643,43 +746,43 @@ OPT_BEGIN(end, "e:") {
throw error(std::string("Could not determine end of period '") +
optarg + "'");
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "d<[";
- config.predicate += buf;
- config.predicate += "]";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "d<[";
+ config->predicate += buf;
+ config->predicate += "]";
terminus = interval.end;
} OPT_END(end);
OPT_BEGIN(current, "c") {
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "d<=m";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "d<=m";
} OPT_END(current);
OPT_BEGIN(cleared, "C") {
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "X";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "X";
} OPT_END(cleared);
OPT_BEGIN(uncleared, "U") {
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "!X";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "!X";
} OPT_END(uncleared);
OPT_BEGIN(real, "R") {
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "R";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "R";
} OPT_END(real);
OPT_BEGIN(actual, "L") {
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "L";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "L";
} OPT_END(actual);
//////////////////////////////////////////////////////////////////////
@@ -687,11 +790,11 @@ OPT_BEGIN(actual, "L") {
// Output customization
OPT_BEGIN(format, "F:") {
- config.format_string = optarg;
+ config->format_string = optarg;
} OPT_END(format);
OPT_BEGIN(date_format, "y:") {
- config.date_format = optarg;
+ config->date_format = optarg;
} OPT_END(date_format);
OPT_BEGIN(input_date_format, ":") {
@@ -700,95 +803,91 @@ OPT_BEGIN(input_date_format, ":") {
} OPT_END(input_date_format);
OPT_BEGIN(balance_format, ":") {
- config.balance_format = optarg;
+ config->balance_format = optarg;
} OPT_END(balance_format);
OPT_BEGIN(register_format, ":") {
- config.register_format = optarg;
+ config->register_format = optarg;
} OPT_END(register_format);
OPT_BEGIN(wide_register_format, ":") {
- config.wide_register_format = optarg;
+ config->wide_register_format = optarg;
} OPT_END(wide_register_format);
OPT_BEGIN(plot_amount_format, ":") {
- config.plot_amount_format = optarg;
+ config->plot_amount_format = optarg;
} OPT_END(plot_amount_format);
OPT_BEGIN(plot_total_format, ":") {
- config.plot_total_format = optarg;
-
-OPT_BEGIN(effective, "") {
- transaction_t::use_effective_date = true;
-} OPT_END(effective);
+ config->plot_total_format = optarg;
} OPT_END(plot_total_format);
OPT_BEGIN(print_format, ":") {
- config.print_format = optarg;
+ config->print_format = optarg;
} OPT_END(print_format);
OPT_BEGIN(write_hdr_format, ":") {
- config.write_hdr_format = optarg;
+ config->write_hdr_format = optarg;
} OPT_END(write_hdr_format);
OPT_BEGIN(write_xact_format, ":") {
- config.write_xact_format = optarg;
+ config->write_xact_format = optarg;
} OPT_END(write_xact_format);
OPT_BEGIN(equity_format, ":") {
- config.equity_format = optarg;
+ config->equity_format = optarg;
} OPT_END(equity_format);
OPT_BEGIN(prices_format, ":") {
- config.prices_format = optarg;
+ config->prices_format = optarg;
} OPT_END(prices_format);
OPT_BEGIN(wide, "w") {
- config.register_format = config.wide_register_format;
+ config->register_format = config->wide_register_format;
} OPT_END(wide);
OPT_BEGIN(head, ":") {
- config.head_entries = std::atoi(optarg);
+ config->head_entries = std::atoi(optarg);
} OPT_END(head);
OPT_BEGIN(tail, ":") {
- config.tail_entries = std::atoi(optarg);
+ config->tail_entries = std::atoi(optarg);
} OPT_END(tail);
OPT_BEGIN(pager, ":") {
- config.pager = optarg;
+ config->pager = optarg;
} OPT_END(pager);
OPT_BEGIN(empty, "E") {
- config.show_empty = true;
+ config->show_empty = true;
} OPT_END(empty);
OPT_BEGIN(collapse, "n") {
- config.show_collapsed = true;
+ config->show_collapsed = true;
} OPT_END(collapse);
OPT_BEGIN(subtotal, "s") {
- config.show_subtotal = true;
+ config->show_subtotal = true;
} OPT_END(subtotal);
OPT_BEGIN(totals, "") {
- config.show_totals = true;
+ config->show_totals = true;
} OPT_END(totals);
OPT_BEGIN(sort, "S:") {
- config.sort_string = optarg;
+ config->sort_string = optarg;
} OPT_END(sort);
OPT_BEGIN(related, "r") {
- config.show_related = true;
+ config->show_related = true;
} OPT_END(related);
OPT_BEGIN(period, "p:") {
- if (config.report_period.empty()) {
- config.report_period = optarg;
+ if (config->report_period.empty()) {
+ config->report_period = optarg;
} else {
- config.report_period += " ";
- config.report_period += optarg;
+ config->report_period += " ";
+ config->report_period += optarg;
}
// If the period gives a beginning and/or ending date, make sure to
@@ -796,180 +895,180 @@ OPT_BEGIN(period, "p:") {
// options) to take this into account.
char buf[128];
- interval_t interval(config.report_period);
+ interval_t interval(config->report_period);
+
if (interval.begin) {
std::strftime(buf, 127, formats[0], std::localtime(&interval.begin));
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "d>=[";
- config.predicate += buf;
- config.predicate += "]";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "d>=[";
+ config->predicate += buf;
+ config->predicate += "]";
}
+
if (interval.end) {
std::strftime(buf, 127, formats[0], std::localtime(&interval.end));
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "d<[";
- config.predicate += buf;
- config.predicate += "]";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "d<[";
+ config->predicate += buf;
+ config->predicate += "]";
terminus = interval.end;
}
} OPT_END(period);
OPT_BEGIN(period_sort, ":") {
- config.report_period_sort = optarg;
+ config->report_period_sort = optarg;
} OPT_END(period_sort);
OPT_BEGIN(weekly, "W") {
- if (config.report_period.empty())
- config.report_period = "weekly";
+ if (config->report_period.empty())
+ config->report_period = "weekly";
else
- config.report_period = std::string("weekly ") + config.report_period;
+ config->report_period = std::string("weekly ") + config->report_period;
} OPT_END(weekly);
OPT_BEGIN(monthly, "M") {
- if (config.report_period.empty())
- config.report_period = "monthly";
+ if (config->report_period.empty())
+ config->report_period = "monthly";
else
- config.report_period = std::string("monthly ") + config.report_period;
+ config->report_period = std::string("monthly ") + config->report_period;
} OPT_END(monthly);
OPT_BEGIN(yearly, "Y") {
- if (config.report_period.empty())
- config.report_period = "yearly";
+ if (config->report_period.empty())
+ config->report_period = "yearly";
else
- config.report_period = std::string("yearly ") + config.report_period;
+ config->report_period = std::string("yearly ") + config->report_period;
} OPT_END(yearly);
OPT_BEGIN(dow, "") {
- config.days_of_the_week = true;
+ config->days_of_the_week = true;
} OPT_END(dow);
OPT_BEGIN(by_payee, "P") {
- config.by_payee = true;
+ config->by_payee = true;
} OPT_END(by_payee);
OPT_BEGIN(comm_as_payee, "x") {
- config.comm_as_payee = true;
+ config->comm_as_payee = true;
} OPT_END(comm_as_payee);
OPT_BEGIN(budget, "") {
- config.budget_flags = BUDGET_BUDGETED;
+ config->budget_flags = BUDGET_BUDGETED;
} OPT_END(budget);
OPT_BEGIN(add_budget, "") {
- config.budget_flags = BUDGET_BUDGETED | BUDGET_UNBUDGETED;
+ config->budget_flags = BUDGET_BUDGETED | BUDGET_UNBUDGETED;
} OPT_END(add_budget);
OPT_BEGIN(unbudgeted, "") {
- config.budget_flags = BUDGET_UNBUDGETED;
+ config->budget_flags = BUDGET_UNBUDGETED;
} OPT_END(unbudgeted);
OPT_BEGIN(forecast, ":") {
- config.forecast_limit = optarg;
+ config->forecast_limit = optarg;
} OPT_END(forecast);
OPT_BEGIN(reconcile, ":") {
- config.reconcile_balance = optarg;
+ config->reconcile_balance = optarg;
} OPT_END(reconcile);
OPT_BEGIN(reconcile_date, ":") {
- config.reconcile_date = optarg;
+ config->reconcile_date = optarg;
} OPT_END(reconcile_date);
OPT_BEGIN(limit, "l:") {
- if (! config.predicate.empty())
- config.predicate += "&";
- config.predicate += "(";
- config.predicate += optarg;
- config.predicate += ")";
+ if (! config->predicate.empty())
+ config->predicate += "&";
+ config->predicate += "(";
+ config->predicate += optarg;
+ config->predicate += ")";
} OPT_END(limit);
OPT_BEGIN(display, "d:") {
- if (! config.display_predicate.empty())
- config.display_predicate += "&";
- config.display_predicate += "(";
- config.display_predicate += optarg;
- config.display_predicate += ")";
+ if (! config->display_predicate.empty())
+ config->display_predicate += "&";
+ config->display_predicate += "(";
+ config->display_predicate += optarg;
+ config->display_predicate += ")";
} OPT_END(display);
OPT_BEGIN(amount, "t:") {
- config.amount_expr = optarg;
+ config->amount_expr = optarg;
} OPT_END(amount);
OPT_BEGIN(total, "T:") {
- config.total_expr = optarg;
+ config->total_expr = optarg;
} OPT_END(total);
OPT_BEGIN(amount_data, "j") {
- config.format_string = config.plot_amount_format;
+ config->format_string = config->plot_amount_format;
} OPT_END(amount_data);
-
OPT_BEGIN(total_data, "J") {
- config.format_string = config.plot_total_format;
+ config->format_string = config->plot_total_format;
} OPT_END(total_data);
-
//////////////////////////////////////////////////////////////////////
//
// Commodity reporting
OPT_BEGIN(price_db, ":") {
- config.price_db = optarg;
+ config->price_db = optarg;
} OPT_END(price_db);
OPT_BEGIN(price_exp, "Z:") {
- config.pricing_leeway = std::atol(optarg) * 60;
+ config->pricing_leeway = std::atol(optarg) * 60;
} OPT_END(price_exp);
OPT_BEGIN(download, "Q") {
- config.download_quotes = true;
+ config->download_quotes = true;
} OPT_END(download);
OPT_BEGIN(quantity, "O") {
- config.amount_expr = "a";
- config.total_expr = "O";
+ config->amount_expr = "a";
+ config->total_expr = "O";
} OPT_END(quantity);
OPT_BEGIN(basis, "B") {
- config.amount_expr = "b";
- config.total_expr = "B";
+ config->amount_expr = "b";
+ config->total_expr = "B";
} OPT_END(basis);
OPT_BEGIN(market, "V") {
- config.show_revalued = true;
+ config->show_revalued = true;
- config.amount_expr = "v";
- config.total_expr = "V";
+ config->amount_expr = "v";
+ config->total_expr = "V";
} OPT_END(market);
OPT_BEGIN(performance, "g") {
- config.amount_expr = "P(a,m)-b"; // same as 'g', but priced now
- config.total_expr = "P(O,m)-B";
+ config->amount_expr = "P(a,m)-b"; // same as 'g', but priced now
+ config->total_expr = "P(O,m)-B";
} OPT_END(performance);
OPT_BEGIN(gain, "G") {
- config.show_revalued =
- config.show_revalued_only = true;
+ config->show_revalued =
+ config->show_revalued_only = true;
- config.amount_expr = "a";
- config.total_expr = "G";
+ config->amount_expr = "a";
+ config->total_expr = "G";
} OPT_END(gain);
OPT_BEGIN(average, "A") {
- config.total_expr_template = "A#";
+ config->total_expr_template = "A#";
} OPT_END(average);
OPT_BEGIN(deviation, "D") {
- config.total_expr_template = "t-A#";
+ config->total_expr_template = "t-A#";
} OPT_END(deviation);
OPT_BEGIN(percentage, "%") {
- config.total_expr_template = "^#&{100.0%}*(#/^#)";
+ config->total_expr_template = "^#&{100.0%}*(#/^#)";
} OPT_END(percentage);
#ifdef USE_BOOST_PYTHON
@@ -1017,9 +1116,6 @@ void py_add_config_option_handlers()
add_other_option_handlers(config_options);
}
-BOOST_PYTHON_FUNCTION_OVERLOADS(parse_ledger_data_overloads,
- parse_ledger_data, 1, 2)
-
void py_option_help()
{
option_help(std::cout);
@@ -1082,10 +1178,9 @@ void export_config()
.def("process_options", py_process_options)
;
- scope().attr("config") = ptr(&config);
+ scope().attr("config") = ptr(config);
def("option_help", py_option_help);
- def("parse_ledger_data", parse_ledger_data, parse_ledger_data_overloads());
def("add_config_option_handlers", py_add_config_option_handlers);
}
diff --git a/config.h b/config.h
index 14b18a47..434d2e21 100644
--- a/config.h
+++ b/config.h
@@ -1,12 +1,7 @@
#ifndef _CONFIG_H
#define _CONFIG_H
-#include "journal.h"
-#include "option.h"
-#include "valexpr.h"
-#include "datetime.h"
-#include "format.h"
-#include "parser.h"
+#include "ledger.h"
#include <iostream>
#include <memory>
@@ -14,8 +9,9 @@
namespace ledger {
-struct config_t
+class config_t
{
+ public:
// These options can all be set used text fields.
strings_list price_settings;
@@ -70,27 +66,43 @@ struct config_t
bool use_cache;
bool cache_dirty;
- config_t();
+ config_t() {
+ reset();
+ }
config_t(const config_t&) {
assert(0);
}
+ void reset();
+
+ void regexps_to_predicate(const std::string& command,
+ std::list<std::string>::const_iterator begin,
+ std::list<std::string>::const_iterator end,
+ const bool account_regexp = false,
+ const bool add_account_short_masks = false,
+ const bool logical_and = true);
+
+ bool process_option(const std::string& opt, const char * arg = NULL);
+ void process_arguments(int argc, char ** argv, const bool anywhere,
+ std::list<std::string>& args);
+ void process_environment(char ** envp, const std::string& tag);
+
void process_options(const std::string& command,
strings_list::iterator arg,
strings_list::iterator args_end);
+
+ item_handler<transaction_t> *
+ chain_xact_handlers(const std::string& command,
+ item_handler<transaction_t> * base_formatter,
+ journal_t * journal,
+ account_t * master,
+ std::list<item_handler<transaction_t> *>& ptrs);
};
-extern config_t config;
extern std::list<option_t> config_options;
void option_help(std::ostream& out);
-// Parse what ledger data can be determined from the config settings
-void parse_ledger_data(journal_t * journal,
- parser_t * cache_parser = NULL,
- parser_t * text_parser = NULL,
- parser_t * xml_parser = NULL);
-
struct declared_option_handler : public option_handler {
declared_option_handler(const std::string& label,
const std::string& opt_chars) {
diff --git a/configure.in b/configure.in
index af2a1962..0059e0ea 100644
--- a/configure.in
+++ b/configure.in
@@ -10,9 +10,8 @@ AC_CONFIG_HEADER([acconf.h])
# Checks for programs.
AC_PROG_CXX
AC_PROG_MAKE_SET
-AC_PROG_RANLIB
-#AC_PROG_LIBTOOL
-#AM_PROG_LIBTOOL
+AC_PROG_LIBTOOL
+AM_PROG_LIBTOOL
# check if UNIX pipes are available
AC_CACHE_CHECK(
diff --git a/derive.cc b/derive.cc
index 402eef8a..07a8aaa4 100644
--- a/derive.cc
+++ b/derive.cc
@@ -36,7 +36,7 @@ entry_t * derive_new_entry(journal_t& journal,
if (i == end) {
// If no argument were given but the payee, assume the user wants
// to see the same transaction as last time.
- added->code = matching->code;
+ added->code = matching->code;
for (transactions_list::iterator j = matching->transactions.begin();
j != matching->transactions.end();
diff --git a/emacs.cc b/emacs.cc
index 1a6b02a0..a7d517f7 100644
--- a/emacs.cc
+++ b/emacs.cc
@@ -47,7 +47,7 @@ void format_emacs_transactions::operator()(transaction_t& xact)
out << "\n";
}
- out << " (\"" << xact.account->fullname() << "\" \""
+ out << " (\"" << xact_account(xact)->fullname() << "\" \""
<< xact.amount << "\"";
switch (xact.state) {
diff --git a/format.cc b/format.cc
index a2c84ef2..f7a04fb2 100644
--- a/format.cc
+++ b/format.cc
@@ -474,7 +474,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (details.xact) {
switch (details.xact->state) {
case transaction_t::CLEARED:
- out << "* ";
+ out << "* ";
break;
case transaction_t::PENDING:
out << "! ";
@@ -547,8 +547,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case element_t::ACCOUNT_FULLNAME:
if (details.account) {
name += (elem->type == element_t::ACCOUNT_FULLNAME ?
- details.account->fullname() :
- partial_account_name(*details.account));
+ details.account->fullname() :
+ partial_account_name(*details.account));
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
if (elem->max_width > 2)
@@ -685,7 +685,9 @@ void print_entry(std::ostream& out, const entry_t& entry)
formatter);
formatter.flush();
- clear_all_xdata();
+ clear_transaction_xdata cleaner;
+ walk_transactions(const_cast<transactions_list&>(entry.transactions),
+ cleaner);
}
bool disp_subaccounts_p(const account_t& account,
diff --git a/journal.cc b/journal.cc
index 67cd7f57..5ef8e92d 100644
--- a/journal.cc
+++ b/journal.cc
@@ -728,11 +728,11 @@ EXC_TRANSLATOR(parse_error)
void export_journal()
{
- scope().attr("TRANSACTION_NORMAL") = TRANSACTION_NORMAL;
- scope().attr("TRANSACTION_VIRTUAL") = TRANSACTION_VIRTUAL;
- scope().attr("TRANSACTION_BALANCE") = TRANSACTION_BALANCE;
- scope().attr("TRANSACTION_AUTO") = TRANSACTION_AUTO;
- scope().attr("TRANSACTION_BULK_ALLOC") = TRANSACTION_BULK_ALLOC;
+ scope().attr("TRANSACTION_NORMAL") = TRANSACTION_NORMAL;
+ scope().attr("TRANSACTION_VIRTUAL") = TRANSACTION_VIRTUAL;
+ scope().attr("TRANSACTION_BALANCE") = TRANSACTION_BALANCE;
+ scope().attr("TRANSACTION_AUTO") = TRANSACTION_AUTO;
+ scope().attr("TRANSACTION_BULK_ALLOC") = TRANSACTION_BULK_ALLOC;
class_< transaction_t > ("Transaction")
.def(init<account_t *, amount_t, optional<unsigned int, std::string> >())
diff --git a/ledger.el b/ledger.el
index 492ef306..9d06dbe1 100644
--- a/ledger.el
+++ b/ledger.el
@@ -487,7 +487,7 @@ dropped."
(dolist (item items)
(let ((index 1))
(dolist (xact (nthcdr 5 item))
- (let ((beg (point))
+ (let ((beg (point))
(where
(with-current-buffer buf
(cons
@@ -502,14 +502,14 @@ dropped."
account (cdr (ledger-current-entry-bounds)))
(setq i (1+ i))))
(point-marker)))))))
- (insert (format "%s %-30s %-25s %15s\n"
+ (insert (format "%s %-30s %-25s %15s\n"
(format-time-string "%m/%d" (nth 2 item))
(nth 4 item) (nth 0 xact) (nth 1 xact)))
(if (nth 2 xact)
+ (set-text-properties beg (1- (point))
+ (list 'face 'bold
+ 'where where))
(set-text-properties beg (1- (point))
- (list 'face 'bold
- 'where where))
- (set-text-properties beg (1- (point))
(list 'where where))))
(setq index (1+ index)))))
(goto-char (point-min))
diff --git a/ledger.h b/ledger.h
index 0046600a..dca501ee 100644
--- a/ledger.h
+++ b/ledger.h
@@ -26,7 +26,6 @@
#include <reconcile.h>
#include <error.h>
#include <option.h>
-#include <config.h>
#include <parser.h>
#include <textual.h>
@@ -36,4 +35,15 @@
#include <qif.h>
#include <ofx.h>
+namespace ledger {
+ extern parser_t * binary_parser_ptr;
+ extern parser_t * xml_parser_ptr;
+ extern parser_t * gnucash_parser_ptr;
+ extern parser_t * ofx_parser_ptr;
+ extern parser_t * qif_parser_ptr;
+ extern parser_t * textual_parser_ptr;
+}
+
+#include <config.h>
+
#endif // _LEDGER_H
diff --git a/main.cc b/main.cc
index cec321e8..fb7d2d85 100644
--- a/main.cc
+++ b/main.cc
@@ -1,16 +1,6 @@
-#include <ledger.h>
-#include "acconf.h"
-#include "debug.h"
-#ifdef USE_BOOST_PYTHON
-#include "py_eval.h"
-#endif
-
-using namespace ledger;
-
#include <iostream>
#include <fstream>
#include <sstream>
-#include <memory>
#include <algorithm>
#include <exception>
#include <iterator>
@@ -27,218 +17,34 @@ using namespace ledger;
#include "fdstream.hpp"
#endif
-#if !defined(DEBUG_LEVEL) || DEBUG_LEVEL <= RELEASE
-
-#define auto_ptr bogus_auto_ptr
-
-// This version of auto_ptr does not delete on deconstruction.
-namespace std {
- template <typename T>
- struct bogus_auto_ptr {
- T * ptr;
- bogus_auto_ptr() : ptr(NULL) {}
- explicit bogus_auto_ptr(T * _ptr) : ptr(_ptr) {}
- T& operator*() const throw() {
- return *ptr;
- }
- T * operator->() const throw() {
- return ptr;
- }
- T * get() const throw() { return ptr; }
- T * release() throw() {
- T * tmp = ptr;
- ptr = 0;
- return tmp;
- }
- void reset(T * p = 0) throw() {
- if (p != ptr) {
- delete ptr;
- ptr = p;
- }
- }
- };
-}
-
+#include "ledger.h"
+#ifdef USE_BOOST_PYTHON
+#include "py_eval.h"
#endif
+#include "timing.h"
-item_handler<transaction_t> *
-chain_xact_handlers(const std::string& command,
- item_handler<transaction_t> * base_formatter,
- journal_t * journal,
- account_t * master,
- std::list<item_handler<transaction_t> *>& ptrs)
-{
- item_handler<transaction_t> * formatter = NULL;
-
- ptrs.push_back(formatter = base_formatter);
-
- // format_transactions write each transaction received to the
- // output stream.
- if (! (command == "b" || command == "E")) {
- // truncate_entries cuts off a certain number of _entries_ from
- // being displayed. It does not affect calculation.
- if (config.head_entries || config.tail_entries)
- ptrs.push_back(formatter =
- new truncate_entries(formatter,
- config.head_entries,
- config.tail_entries));
-
- // filter_transactions will only pass through transactions
- // matching the `display_predicate'.
- if (! config.display_predicate.empty())
- ptrs.push_back(formatter =
- new filter_transactions(formatter,
- config.display_predicate));
-
- // calc_transactions computes the running total. When this
- // appears will determine, for example, whether filtered
- // transactions are included or excluded from the running total.
- ptrs.push_back(formatter = new calc_transactions(formatter));
-
- // reconcile_transactions will pass through only those
- // transactions which can be reconciled to a given balance
- // (calculated against the transactions which it receives).
- if (! config.reconcile_balance.empty()) {
- value_t target_balance(config.reconcile_balance);
- time_t cutoff = now;
- if (! config.reconcile_date.empty())
- parse_date(config.reconcile_date.c_str(), &cutoff);
- ptrs.push_back(formatter =
- new reconcile_transactions(formatter, target_balance,
- cutoff));
- }
-
- // sort_transactions will sort all the transactions it sees, based
- // on the `sort_order' value expression.
- if (! config.sort_string.empty())
- ptrs.push_back(formatter =
- new sort_transactions(formatter, config.sort_string));
-
- // changed_value_transactions adds virtual transactions to the
- // list to account for changes in market value of commodities,
- // which otherwise would affect the running total unpredictably.
- if (config.show_revalued)
- ptrs.push_back(formatter =
- new changed_value_transactions(formatter,
- config.show_revalued_only));
-
- // collapse_transactions causes entries with multiple transactions
- // to appear as entries with a subtotaled transaction for each
- // commodity used.
- if (config.show_collapsed)
- ptrs.push_back(formatter = new collapse_transactions(formatter));
- }
-
- // subtotal_transactions combines all the transactions it receives
- // into one subtotal entry, which has one transaction for each
- // commodity in each account.
- //
- // period_transactions is like subtotal_transactions, but it
- // subtotals according to time periods rather than totalling
- // everything.
- //
- // dow_transactions is like period_transactions, except that it
- // reports all the transactions that fall on each subsequent day
- // of the week.
- if (config.show_subtotal && ! (command == "b" || command == "E"))
- ptrs.push_back(formatter = new subtotal_transactions(formatter));
-
- if (config.days_of_the_week)
- ptrs.push_back(formatter = new dow_transactions(formatter));
- else if (config.by_payee)
- ptrs.push_back(formatter = new by_payee_transactions(formatter));
-
- if (! config.report_period.empty()) {
- ptrs.push_back(formatter =
- new interval_transactions(formatter,
- config.report_period,
- config.report_period_sort));
- ptrs.push_back(formatter = new sort_transactions(formatter, "d"));
- }
-
- // invert_transactions inverts the value of the transactions it
- // receives.
- if (config.show_inverted)
- ptrs.push_back(formatter = new invert_transactions(formatter));
-
- // 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)
- ptrs.push_back(formatter =
- new related_transactions(formatter,
- config.show_all_related));
-
- // This filter_transactions will only pass through transactions
- // matching the `predicate'.
- if (! config.predicate.empty())
- ptrs.push_back(formatter = new filter_transactions(formatter,
- config.predicate));
-
- // budget_transactions takes a set of transactions from a data
- // file and uses them to generate "budget transactions" which
- // balance against the reported transactions.
- //
- // forecast_transactions is a lot like budget_transactions, except
- // that it adds entries only for the future, and does not balance
- // them against anything but the future balance.
-
- if (config.budget_flags) {
- // Don't generate a cache file after calculating a budget report,
- // since certain intermediary accounts may get created which
- // aren't intended to be saved. For example, the user might have
- // an "Expenses" budget, to catch all other expenses. This will
- // result in an "Expenses" account being created in the journal --
- // to reflect the calculated totals -- even though no such account
- // was ever actually used. Because budgeting and forecasting
- // might create such "ghost" accounts for internal purposes, we
- // don't want to change the cache.
- config.use_cache = false;
-
- budget_transactions * handler
- = new budget_transactions(formatter, config.budget_flags);
- handler->add_period_entries(journal->period_entries);
- ptrs.push_back(formatter = handler);
-
- // Apply this before the budget handler, so that only matching
- // transactions are calculated toward the budget. The use of
- // filter_transactions above will further clean the results so
- // that no automated transactions that don't match the filter get
- // reported.
- if (! config.predicate.empty())
- ptrs.push_back(formatter = new filter_transactions(formatter,
- config.predicate));
- }
- else if (! config.forecast_limit.empty()) {
- config.use_cache = false; // see note above
-
- forecast_transactions * handler
- = new forecast_transactions(formatter, config.forecast_limit);
- handler->add_period_entries(journal->period_entries);
- ptrs.push_back(formatter = handler);
-
- // See above, under budget_transactions.
- if (! config.predicate.empty())
- ptrs.push_back(formatter = new filter_transactions(formatter,
- config.predicate));
- }
-
- if (config.comm_as_payee)
- ptrs.push_back(formatter = new set_comm_as_payee(formatter));
+using namespace ledger;
- return formatter;
+namespace {
+ TIMER_DEF_(setup);
+ TIMER_DEF_(parse);
+ TIMER_DEF_(process);
+ TIMER_DEF_(walk);
+ TIMER_DEF_(cleanup);
}
int parse_and_report(int argc, char * argv[], char * envp[])
{
+ TIMER_START(setup);
+
+ config_t config;
+
std::auto_ptr<journal_t> journal(new journal_t);
// Parse command-line arguments, and those set in the environment
std::list<std::string> args;
- process_arguments(config_options, argc - 1, argv + 1, false, args);
+ config.process_arguments(argc - 1, argv + 1, false, args);
if (args.empty()) {
option_help(std::cerr);
@@ -252,19 +58,19 @@ int parse_and_report(int argc, char * argv[], char * envp[])
config.use_cache = config.data_file.empty() && config.price_db.empty();
DEBUG_PRINT("ledger.config.cache", "1. use_cache = " << config.use_cache);
- process_environment(config_options, envp, "LEDGER_");
+ config.process_environment(envp, "LEDGER_");
#if 1
// These are here for backwards compatability, but are deprecated.
if (const char * p = std::getenv("LEDGER"))
- process_option(config_options, "file", p);
+ config.process_option("file", p);
if (const char * p = std::getenv("LEDGER_INIT"))
- process_option(config_options, "init-file", p);
+ config.process_option("init-file", p);
if (const char * p = std::getenv("PRICE_HIST"))
- process_option(config_options, "price-db", p);
+ config.process_option("price-db", p);
if (const char * p = std::getenv("PRICE_EXP"))
- process_option(config_options, "price-exp", p);
+ config.process_option("price-exp", p);
#endif
const char * p = std::getenv("HOME");
@@ -311,38 +117,22 @@ int parse_and_report(int argc, char * argv[], char * envp[])
else
throw error(std::string("Unrecognized command '") + command + "'");
+ TIMER_STOP(setup);
+
// Parse initialization files, ledger data, price database, etc.
- std::auto_ptr<binary_parser_t> bin_parser(new binary_parser_t);
-#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
- std::auto_ptr<xml_parser_t> xml_parser(new xml_parser_t);
- std::auto_ptr<gnucash_parser_t> gnucash_parser(new gnucash_parser_t);
-#endif
-#ifdef HAVE_LIBOFX
- std::auto_ptr<ofx_parser_t> ofx_parser(new ofx_parser_t);
-#endif
- std::auto_ptr<qif_parser_t> qif_parser(new qif_parser_t);
- std::auto_ptr<textual_parser_t> text_parser(new textual_parser_t);
+ TIMER_START(parse);
- register_parser(bin_parser.get());
-#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
- register_parser(xml_parser.get());
- register_parser(gnucash_parser.get());
-#endif
-#ifdef HAVE_LIBOFX
- register_parser(ofx_parser.get());
-#endif
- register_parser(qif_parser.get());
- register_parser(text_parser.get());
+ if (parse_ledger_data(journal.get(), config) == 0)
+ throw error("Please specify ledger file using -f"
+ " or LEDGER_FILE environment variable.");
- parse_ledger_data(journal.get(), bin_parser.get(), text_parser.get()
-#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
- , xml_parser.get()
-#endif
- );
+ TIMER_STOP(parse);
// process the command word and its following arguments
+ TIMER_START(process);
+
std::string first_arg;
if (command == "w") {
if (arg == args.end())
@@ -461,8 +251,12 @@ def vmax(d, val):\n\
#endif // USE_BOOST_PYTHON
+ TIMER_STOP(process);
+
// Walk the entries based on the report type and the options
+ TIMER_START(walk);
+
item_handler<transaction_t> * formatter;
std::list<item_handler<transaction_t> *> formatter_ptrs;
@@ -482,10 +276,11 @@ def vmax(d, val):\n\
formatter = new format_transactions(*out, *format);
if (command == "w") {
- write_textual_journal(*journal, first_arg, *formatter, *out);
+ write_textual_journal(*journal, first_arg, *formatter,
+ config.write_hdr_format, *out);
} else {
- formatter = chain_xact_handlers(command, formatter, journal.get(),
- journal->master, formatter_ptrs);
+ formatter = config.chain_xact_handlers(command, formatter, journal.get(),
+ journal->master, formatter_ptrs);
if (command == "e")
walk_transactions(new_entry->transactions, *formatter);
else if (command == "P" || command == "D")
@@ -521,8 +316,16 @@ def vmax(d, val):\n\
acct_formatter.flush();
}
+ TIMER_STOP(walk);
+
+ TIMER_START(cleanup);
+
#if DEBUG_LEVEL >= BETA
- clear_all_xdata();
+ clear_transaction_xdata xact_cleaner;
+ walk_entries(journal->entries, xact_cleaner);
+
+ clear_account_xdata acct_cleaner;
+ walk_accounts(*journal->master, acct_cleaner);
if (! config.output_file.empty())
delete out;
@@ -554,13 +357,13 @@ def vmax(d, val):\n\
}
#endif
+ TIMER_STOP(cleanup);
+
return 0;
}
int main(int argc, char * argv[], char * envp[])
{
- std::ios::sync_with_stdio(false);
-
try {
return parse_and_report(argc, argv, envp);
}
diff --git a/parser.cc b/parser.cc
index cc93b9dc..6d589bf8 100644
--- a/parser.cc
+++ b/parser.cc
@@ -1,5 +1,6 @@
#include "parser.h"
#include "journal.h"
+#include "config.h"
#include <fstream>
#ifdef WIN32
@@ -12,18 +13,31 @@ namespace ledger {
typedef std::list<parser_t *> parsers_list;
-static parsers_list parsers;
+static parsers_list * parsers = NULL;
+
+void initialize_parser_support()
+{
+ parsers = new parsers_list;
+}
+
+void shutdown_parser_support()
+{
+ if (parsers) {
+ delete parsers;
+ parsers = NULL;
+ }
+}
bool register_parser(parser_t * parser)
{
parsers_list::iterator i;
- for (i = parsers.begin(); i != parsers.end(); i++)
+ for (i = parsers->begin(); i != parsers->end(); i++)
if (*i == parser)
break;
- if (i != parsers.end())
+ if (i != parsers->end())
return false;
- parsers.push_back(parser);
+ parsers->push_back(parser);
return true;
}
@@ -31,13 +45,13 @@ bool register_parser(parser_t * parser)
bool unregister_parser(parser_t * parser)
{
parsers_list::iterator i;
- for (i = parsers.begin(); i != parsers.end(); i++)
+ for (i = parsers->begin(); i != parsers->end(); i++)
if (*i == parser)
break;
- if (i == parsers.end())
+ if (i == parsers->end())
return false;
- parsers.erase(i);
+ parsers->erase(i);
return true;
}
@@ -50,8 +64,8 @@ unsigned int parse_journal(std::istream& in,
if (! master)
master = journal->master;
- for (parsers_list::iterator i = parsers.begin();
- i != parsers.end();
+ for (parsers_list::iterator i = parsers->begin();
+ i != parsers->end();
i++)
if ((*i)->test(in))
return (*i)->parse(in, journal, master, original_file);
@@ -76,6 +90,106 @@ unsigned int parse_journal_file(const std::string& path,
return parse_journal(stream, journal, master, original_file);
}
+unsigned int parse_ledger_data(journal_t * journal,
+ const std::string& data_file,
+ const std::string& init_file,
+ const std::string& price_db,
+ bool use_cache,
+ const std::string& cache_file,
+ bool * cache_dirty,
+ parser_t * cache_parser,
+ parser_t * xml_parser,
+ parser_t * stdin_parser,
+ const std::string& default_account)
+{
+ unsigned int entry_count = 0;
+
+ DEBUG_PRINT("ledger.config.cache", "3. use_cache = " << use_cache);
+
+ if (! init_file.empty() && access(init_file.c_str(), R_OK) != -1) {
+ if (parse_journal_file(init_file, journal) ||
+ journal->auto_entries.size() > 0 ||
+ journal->period_entries.size() > 0)
+ throw error(std::string("Entries found in initialization file '") +
+ init_file + "'");
+
+ journal->sources.pop_front(); // remove init file
+ }
+
+ if (use_cache && ! cache_file.empty() && ! data_file.empty()) {
+ DEBUG_PRINT("ledger.config.cache", "using_cache " << cache_file);
+ if (cache_dirty)
+ *cache_dirty = true;
+ if (access(cache_file.c_str(), R_OK) != -1) {
+ std::ifstream stream(cache_file.c_str());
+ if (cache_parser && cache_parser->test(stream)) {
+ std::string price_db_orig = journal->price_db;
+ journal->price_db = price_db;
+ entry_count += cache_parser->parse(stream, journal, NULL, &data_file);
+ if (entry_count > 0) {
+ if (cache_dirty)
+ *cache_dirty = false;
+ } else {
+ journal->price_db = price_db_orig;
+ }
+ }
+ }
+ }
+
+ if (entry_count == 0 && ! data_file.empty()) {
+ account_t * acct = NULL;
+ if (! default_account.empty())
+ acct = journal->find_account(default_account);
+
+ journal->price_db = price_db;
+ if (! journal->price_db.empty() &&
+ access(journal->price_db.c_str(), R_OK) != -1) {
+ if (parse_journal_file(journal->price_db, journal)) {
+ throw error("Entries not allowed in price history file");
+ } else {
+ DEBUG_PRINT("ledger.config.cache",
+ "read price database " << journal->price_db);
+ journal->sources.pop_back();
+ }
+ }
+
+ DEBUG_PRINT("ledger.config.cache",
+ "rejected cache, parsing " << data_file);
+ if (data_file == "-") {
+ use_cache = false;
+ journal->sources.push_back("<stdin>");
+#if 0
+ if (xml_parser && std::cin.peek() == '<')
+ entry_count += xml_parser->parse(std::cin, journal, acct);
+ else if (stdin_parser)
+#endif
+ entry_count += stdin_parser->parse(std::cin, journal, acct);
+ }
+ else if (access(data_file.c_str(), R_OK) != -1) {
+ entry_count += parse_journal_file(data_file, journal, acct);
+ if (! journal->price_db.empty())
+ journal->sources.push_back(journal->price_db);
+ }
+ }
+
+ VALIDATE(journal->valid());
+
+ return entry_count;
+}
+
+extern parser_t * binary_parser_ptr;
+extern parser_t * xml_parser_ptr;
+extern parser_t * textual_parser_ptr;
+
+unsigned int parse_ledger_data(journal_t * journal, config_t& config)
+{
+ return parse_ledger_data(journal, config.data_file, config.init_file,
+ config.price_db, config.use_cache,
+ config.cache_file, &config.cache_dirty,
+ binary_parser_ptr, xml_parser_ptr,
+ textual_parser_ptr, config.account);
+}
+
} // namespace ledger
#ifdef USE_BOOST_PYTHON
@@ -108,6 +222,9 @@ BOOST_PYTHON_FUNCTION_OVERLOADS(parse_journal_overloads, parse_journal, 2, 4)
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_journal_file_overloads,
parse_journal_file, 2, 4)
+BOOST_PYTHON_FUNCTION_OVERLOADS(parse_ledger_data_overloads,
+ parse_ledger_data, 1, 2)
+
void export_parser() {
class_< parser_t, parser_wrap, boost::noncopyable > ("Parser")
;
@@ -116,6 +233,9 @@ void export_parser() {
def("unregister_parser", unregister_parser);
def("parse_journal", parse_journal, parse_journal_overloads());
def("parse_journal_file", parse_journal_file, parse_journal_file_overloads());
+#if 0
+ def("parse_ledger_data", parse_ledger_data, parse_ledger_data_overloads());
+#endif
}
#endif // USE_BOOST_PYTHON
diff --git a/parser.h b/parser.h
index 515bf09b..d163e5e9 100644
--- a/parser.h
+++ b/parser.h
@@ -35,6 +35,24 @@ unsigned int parse_journal_file(const std::string& path,
account_t * master = NULL,
const std::string * original_file = NULL);
+unsigned int parse_ledger_data(journal_t * journal,
+ const std::string& data_file,
+ const std::string& init_file = "",
+ const std::string& price_db = "",
+ bool use_cache = false,
+ const std::string& cache_file = "",
+ bool * cache_dirty = NULL,
+ parser_t * cache_parser = NULL,
+ parser_t * xml_parser = NULL,
+ parser_t * stdin_parser = NULL,
+ const std::string& default_account = "");
+
+class config_t;
+unsigned int parse_ledger_data(journal_t * journal, config_t& config);
+
+void initialize_parser_support();
+void shutdown_parser_support();
+
} // namespace ledger
#endif // _PARSER_H
diff --git a/startup.cc b/startup.cc
new file mode 100644
index 00000000..ad462e36
--- /dev/null
+++ b/startup.cc
@@ -0,0 +1,54 @@
+#include "ledger.h"
+
+using namespace ledger;
+
+namespace ledger {
+ parser_t * binary_parser_ptr = NULL;
+ parser_t * xml_parser_ptr = NULL;
+ parser_t * gnucash_parser_ptr = NULL;
+ parser_t * ofx_parser_ptr = NULL;
+ parser_t * qif_parser_ptr = NULL;
+ parser_t * textual_parser_ptr = NULL;
+}
+
+namespace {
+ binary_parser_t binary_parser;
+#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+ xml_parser_t xml_parser;
+ gnucash_parser_t gnucash_parser;
+#endif
+#ifdef HAVE_LIBOFX
+ ofx_parser_t ofx_parser;
+#endif
+ qif_parser_t qif_parser;
+ textual_parser_t textual_parser;
+
+ static class startup {
+ public:
+ startup();
+ ~startup();
+ } _startup;
+
+ startup::startup()
+ {
+ std::ios::sync_with_stdio(false);
+
+ initialize_parser_support();
+
+ register_parser(&binary_parser); binary_parser_ptr = &binary_parser;
+#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+ register_parser(&xml_parser); xml_parser_ptr = &xml_parser;
+ register_parser(&gnucash_parser); gnucash_parser_ptr = &gnucash_parser;
+#endif
+#ifdef HAVE_LIBOFX
+ register_parser(&ofx_parser); ofx_parser_ptr = &ofx_parser;
+#endif
+ register_parser(&qif_parser); qif_parser_ptr = &qif_parser;
+ register_parser(&textual_parser); textual_parser_ptr = &textual_parser;
+ }
+
+ startup::~startup()
+ {
+ shutdown_parser_support();
+ }
+}
diff --git a/textual.cc b/textual.cc
index 9b1322b9..a367c01e 100644
--- a/textual.cc
+++ b/textual.cc
@@ -211,7 +211,7 @@ transaction_t * parse_transaction(char * line, account_t * account)
if (amount == note_str)
amount = NULL;
- *note_str++ = '\0';
+ *note_str++ = '\0';
note_str = skip_ws(note_str);
if (char * b = std::strchr(note_str, '['))
@@ -231,22 +231,22 @@ transaction_t * parse_transaction(char * line, account_t * account)
throw parse_error(path, linenum, "Failed to parse date");
}
- xact->note = skip_ws(note_str);
- }
+ xact->note = skip_ws(note_str);
+ }
if (amount) {
price = std::strchr(amount, '@');
if (price) {
if (price == amount)
- throw parse_error(path, linenum, "Cost specified without amount");
+ throw parse_error(path, linenum, "Cost specified without amount");
*price++ = '\0';
if (*price == '@') {
- per_unit = false;
+ per_unit = false;
price++;
- }
+ }
price = skip_ws(price);
- }
+ }
}
}
}
@@ -321,7 +321,6 @@ bool parse_transactions(std::istream& in,
}
namespace {
- TIMER_DEF(entry_finish, "finalizing entry");
TIMER_DEF(entry_xacts, "parsing transactions");
TIMER_DEF(entry_details, "parsing entry details");
TIMER_DEF(entry_date, "parsing entry date");
@@ -817,6 +816,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
void write_textual_journal(journal_t& journal, std::string path,
item_handler<transaction_t>& formatter,
+ const std::string& write_hdr_format,
std::ostream& out)
{
unsigned long index = 0;
@@ -859,7 +859,7 @@ void write_textual_journal(journal_t& journal, std::string path,
istream_pos_type pos = 0;
istream_pos_type jump_to;
- format_t hdr_fmt(config.write_hdr_format);
+ format_t hdr_fmt(write_hdr_format);
std::ifstream in(found.c_str());
while (! in.eof()) {
diff --git a/textual.h b/textual.h
index 741c21e8..64107e0a 100644
--- a/textual.h
+++ b/textual.h
@@ -2,6 +2,7 @@
#define _TEXTUAL_H
#include "parser.h"
+#include "format.h"
#include "walk.h"
namespace ledger {
@@ -22,6 +23,7 @@ transaction_t * parse_transaction(std::istream& in, account_t * account);
void write_textual_journal(journal_t& journal, std::string path,
item_handler<transaction_t>& formatter,
+ const std::string& write_hdr_format,
std::ostream& out);
} // namespace ledger
diff --git a/timing.h b/timing.h
index bc7fdfe2..9a80df2f 100644
--- a/timing.h
+++ b/timing.h
@@ -41,10 +41,12 @@ class timing_t
#ifdef DEBUG_ENABLED
#define TIMER_DEF(sym, cat) static timing_t sym(#sym, cat)
+#define TIMER_DEF_(sym) static timing_t sym(#sym, #sym)
#define TIMER_START(sym) sym.start(__FILE__, __LINE__)
#define TIMER_STOP(sym) sym.stop()
#else
#define TIMER_DEF(sym, cat)
+#define TIMER_DEF_(sym)
#define TIMER_START(sym)
#define TIMER_STOP(sym)
#endif
diff --git a/valexpr.cc b/valexpr.cc
index 1af45a2e..089b471b 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -15,6 +15,12 @@ std::auto_ptr<value_expr_t> total_expr;
std::time_t terminus = now;
+details_t::details_t(const transaction_t& _xact)
+ : entry(_xact.entry), xact(&_xact), account(xact_account(_xact))
+{
+ DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
+}
+
void value_expr_t::compute(value_t& result, const details_t& details) const
{
switch (kind) {
diff --git a/valexpr.h b/valexpr.h
index 1c015201..ff12cae6 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -20,10 +20,7 @@ struct details_t
: entry(&_entry), xact(NULL), account(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
}
- details_t(const transaction_t& _xact)
- : entry(_xact.entry), xact(&_xact), account(_xact.account) {
- DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
- }
+ details_t(const transaction_t& _xact);
details_t(const account_t& _account)
: entry(NULL), xact(NULL), account(&_account) {
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
@@ -127,7 +124,6 @@ struct value_expr_t
extern std::auto_ptr<value_expr_t> amount_expr;
extern std::auto_ptr<value_expr_t> total_expr;
-
extern std::time_t terminus;
inline void compute_amount(value_t& result, const details_t& details) {
diff --git a/walk.cc b/walk.cc
index 679b2f8e..0b128261 100644
--- a/walk.cc
+++ b/walk.cc
@@ -7,12 +7,6 @@
namespace ledger {
-std::list<transaction_xdata_t> transactions_xdata;
-std::list<void **> transactions_xdata_ptrs;
-
-std::list<account_xdata_t> accounts_xdata;
-std::list<void **> accounts_xdata_ptrs;
-
template <>
bool compare_items<transaction_t>::operator()(const transaction_t * left,
const transaction_t * right)
@@ -37,11 +31,8 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
transaction_xdata_t& transaction_xdata(const transaction_t& xact)
{
- if (! xact.data) {
- transactions_xdata.push_back(transaction_xdata_t());
- xact.data = &transactions_xdata.back();
- transactions_xdata_ptrs.push_back(&xact.data);
- }
+ if (! xact.data)
+ xact.data = new transaction_xdata_t();
return *((transaction_xdata_t *) xact.data);
}
@@ -109,7 +100,10 @@ void truncate_entries::flush()
void set_account_value::operator()(transaction_t& xact)
{
- account_xdata_t& xdata = account_xdata(*xact.account);
+ account_t * acct = xact_account(xact);
+ assert(acct);
+
+ account_xdata_t& xdata = account_xdata(*acct);
add_transaction_to(xact, xdata.value);
xdata.count++;
@@ -370,7 +364,7 @@ void subtotal_transactions::operator()(transaction_t& xact)
if (! finish || std::difftime(xact.date(), finish) > 0)
finish = xact.date();
- account_t * acct = xact.account;
+ account_t * acct = xact_account(xact);
assert(acct);
values_map::iterator i = values.find(acct->fullname());
@@ -387,9 +381,9 @@ void subtotal_transactions::operator()(transaction_t& xact)
// that contain only virtual transactions.
if (! (xact.flags & TRANSACTION_VIRTUAL))
- account_xdata(*xact.account).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
+ account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
else if (! (xact.flags & TRANSACTION_BALANCE))
- account_xdata(*xact.account).dflags |= ACCOUNT_HAS_UNB_VIRTUALS;
+ account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_UNB_VIRTUALS;
}
void interval_transactions::report_subtotal(const std::time_t moment)
@@ -559,10 +553,10 @@ void budget_transactions::report_budget_items(const std::time_t moment)
if (std::difftime(begin, moment) < 0 &&
(! (*i).first.end || std::difftime(begin, (*i).first.end) < 0)) {
- transaction_t& xact = *(*i).second;
+ transaction_t& xact = *(*i).second;
DEBUG_PRINT("ledger.walk.budget", "Reporting budget for "
- << xact.account->fullname());
+ << xact_account(xact)->fullname());
DEBUG_PRINT_TIME("ledger.walk.budget", begin);
DEBUG_PRINT_TIME("ledger.walk.budget", moment);
@@ -573,10 +567,9 @@ void budget_transactions::report_budget_items(const std::time_t moment)
xact_temps.push_back(xact);
transaction_t& temp = xact_temps.back();
- temp.entry = &entry;
- temp.flags |= TRANSACTION_AUTO;
+ temp.entry = &entry;
+ temp.flags |= TRANSACTION_AUTO | TRANSACTION_BULK_ALLOC;
temp.amount.negate();
- temp.flags |= TRANSACTION_BULK_ALLOC;
entry.add_transaction(&temp);
begin = (*i).first.increment(begin);
@@ -596,15 +589,15 @@ void budget_transactions::operator()(transaction_t& xact)
for (pending_xacts_list::iterator i = pending_xacts.begin();
i != pending_xacts.end();
i++)
- for (account_t * acct = xact.account; acct; acct = acct->parent) {
- if (acct == (*i).second->account) {
+ for (account_t * acct = xact_account(xact);
+ acct;
+ acct = acct->parent) {
+ if (acct == xact_account(*(*i).second)) {
xact_in_budget = true;
-
// Report the transaction as if it had occurred in the parent
- // account. jww (2005-07-13): Note that this assignment will
- // irrevocably change the underlying transaction.
- if (xact.account != acct)
- xact.account = acct;
+ // account.
+ if (xact_account(xact) != acct)
+ transaction_xdata(xact).account = acct;
goto handle;
}
}
@@ -705,17 +698,6 @@ void forecast_transactions::flush()
item_handler<transaction_t>::flush();
}
-void clear_transactions_xdata()
-{
- transactions_xdata.clear();
-
- for (std::list<void **>::iterator i = transactions_xdata_ptrs.begin();
- i != transactions_xdata_ptrs.end();
- i++)
- **i = NULL;
- transactions_xdata_ptrs.clear();
-}
-
template <>
bool compare_items<account_t>::operator()(const account_t * left,
const account_t * right)
@@ -740,11 +722,9 @@ bool compare_items<account_t>::operator()(const account_t * left,
account_xdata_t& account_xdata(const account_t& account)
{
- if (! account.data) {
- accounts_xdata.push_back(account_xdata_t());
- account.data = &accounts_xdata.back();
- accounts_xdata_ptrs.push_back(&account.data);
- }
+ if (! account.data)
+ account.data = new account_xdata_t();
+
return *((account_xdata_t *) account.data);
}
@@ -824,18 +804,6 @@ void walk_accounts(account_t& account,
}
}
-void clear_accounts_xdata()
-{
- accounts_xdata.clear();
-
- for (std::list<void **>::iterator i = accounts_xdata_ptrs.begin();
- i != accounts_xdata_ptrs.end();
- i++)
- **i = NULL;
- accounts_xdata_ptrs.clear();
-}
-
-
void walk_commodities(commodities_map& commodities,
item_handler<transaction_t>& handler)
{
@@ -972,7 +940,6 @@ void export_walk()
def("transaction_has_xdata", transaction_has_xdata);
def("transaction_xdata", transaction_xdata, return_internal_reference<1>());
- def("clear_transactions_xdata", clear_transactions_xdata);
def("add_transaction_to", add_transaction_to);
class_< xact_handler_t, item_handler_wrap<transaction_t> >
@@ -991,6 +958,12 @@ void export_walk()
.def("__call__", &ignore_transactions::operator());
;
+ class_< clear_transaction_xdata, bases<xact_handler_t> >
+ ("ClearTransactionXData")
+ .def("flush", &xact_handler_t::flush)
+ .def("__call__", &clear_transaction_xdata::operator());
+ ;
+
class_< truncate_entries, bases<xact_handler_t> >
("TruncateEntries", init<xact_handler_t *, int, int>()
[with_custodian_and_ward<1, 2>()])
@@ -1149,8 +1122,6 @@ void export_walk()
def("account_has_xdata", account_has_xdata);
def("account_xdata", account_xdata, return_internal_reference<1>());
- def("clear_accounts_xdata", clear_accounts_xdata);
- def("clear_all_xdata", clear_all_xdata);
class_< account_handler_t, item_handler_wrap<account_t> >
("AccountHandler")
@@ -1162,6 +1133,12 @@ void export_walk()
&item_handler_wrap<account_t>::default_call)
;
+ class_< clear_account_xdata, bases<account_handler_t> >
+ ("ClearAccountXData")
+ .def("flush", &account_handler_t::flush)
+ .def("__call__", &clear_account_xdata::operator());
+ ;
+
def("sum_accounts", sum_accounts);
def("walk_accounts", py_walk_accounts_1);
def("walk_accounts", py_walk_accounts_2);
diff --git a/walk.h b/walk.h
index 9a37d456..887963bf 100644
--- a/walk.h
+++ b/walk.h
@@ -91,18 +91,17 @@ struct transaction_xdata_t
unsigned int index;
unsigned short dflags;
std::time_t date;
+ account_t * account;
void * ptr;
- transaction_xdata_t() : index(0), dflags(0), date(0), ptr(0) {}
+ transaction_xdata_t()
+ : index(0), dflags(0), date(0), account(0), ptr(0) {}
};
inline bool transaction_has_xdata(const transaction_t& xact) {
return xact.data != NULL;
}
-extern std::list<transaction_xdata_t> transactions_xdata;
-extern std::list<void **> transactions_xdata_ptrs;
-
inline transaction_xdata_t& transaction_xdata_(const transaction_t& xact) {
return *((transaction_xdata_t *) xact.data);
}
@@ -110,6 +109,17 @@ inline transaction_xdata_t& transaction_xdata_(const transaction_t& xact) {
transaction_xdata_t& transaction_xdata(const transaction_t& xact);
void add_transaction_to(const transaction_t& xact, value_t& value);
+inline account_t * xact_account(transaction_t& xact) {
+ account_t * account = transaction_xdata(xact).account;
+ if (account)
+ return account;
+ return xact.account;
+}
+
+inline const account_t * xact_account(const transaction_t& xact) {
+ return xact_account(const_cast<transaction_t&>(xact));
+}
+
//////////////////////////////////////////////////////////////////////
inline void walk_transactions(transactions_list::iterator begin,
@@ -136,8 +146,6 @@ inline void walk_entries(entries_list& list,
walk_entries(list.begin(), list.end(), handler);
}
-void clear_transactions_xdata();
-
//////////////////////////////////////////////////////////////////////
class ignore_transactions : public item_handler<transaction_t>
@@ -146,6 +154,17 @@ class ignore_transactions : public item_handler<transaction_t>
virtual void operator()(transaction_t& xact) {}
};
+class clear_transaction_xdata : public item_handler<transaction_t>
+{
+ public:
+ virtual void operator()(transaction_t& xact) {
+ if (xact.data) {
+ delete (transaction_xdata_t *) xact.data;
+ xact.data = NULL;
+ }
+ }
+};
+
class truncate_entries : public item_handler<transaction_t>
{
int head_count;
@@ -594,9 +613,6 @@ inline bool account_has_xdata(const account_t& account) {
return account.data != NULL;
}
-extern std::list<account_xdata_t> accounts_xdata;
-extern std::list<void **> accounts_xdata_ptrs;
-
inline account_xdata_t& account_xdata_(const account_t& account) {
return *((account_xdata_t *) account.data);
}
@@ -605,6 +621,17 @@ account_xdata_t& account_xdata(const account_t& account);
//////////////////////////////////////////////////////////////////////
+class clear_account_xdata : public item_handler<account_t>
+{
+ public:
+ virtual void operator()(account_t& acct) {
+ if (acct.data) {
+ delete (account_xdata_t *) acct.data;
+ acct.data = NULL;
+ }
+ }
+};
+
void sum_accounts(account_t& account);
typedef std::deque<account_t *> accounts_deque;
@@ -619,18 +646,19 @@ void walk_accounts(account_t& account,
item_handler<account_t>& handler,
const std::string& sort_string);
-void clear_accounts_xdata();
-
-inline void clear_all_xdata() {
- clear_transactions_xdata();
- clear_accounts_xdata();
-}
-
//////////////////////////////////////////////////////////////////////
void walk_commodities(commodities_map& commodities,
item_handler<transaction_t>& handler);
+inline void clear_journal_xdata(journal_t * journal) {
+ clear_transaction_xdata xact_cleaner;
+ walk_entries(journal->entries, xact_cleaner);
+
+ clear_account_xdata acct_cleaner;
+ walk_accounts(*journal->master, acct_cleaner);
+}
+
} // namespace ledger
#endif // _WALK_H