Restructed the code that it can build and be used as a shared library.
The command-line version is still statically bound in the build process by default (for the sake of speed).
This commit is contained in:
parent
6f4957c8c3
commit
e1d0dbf220
23 changed files with 752 additions and 621 deletions
47
Makefile.am
47
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
|
||||
|
|
|
|||
3
NEWS
3
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
|
||||
|
|
|
|||
1
acprep
1
acprep
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
touch AUTHORS COPYING
|
||||
|
||||
glibtoolize --automake -f -c
|
||||
aclocal
|
||||
autoheader
|
||||
if [ "$1" = "--dist" ]; then
|
||||
|
|
|
|||
537
config.cc
537
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;
|
||||
item_handler<transaction_t> * formatter = NULL;
|
||||
|
||||
DEBUG_PRINT("ledger.config.cache", "3. use_cache = " << config.use_cache);
|
||||
ptrs.push_back(formatter = base_formatter);
|
||||
|
||||
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 + "'");
|
||||
// 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));
|
||||
|
||||
journal->sources.pop_front(); // remove init file
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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);
|
||||
// invert_transactions inverts the value of the transactions it
|
||||
// receives.
|
||||
if (show_inverted)
|
||||
ptrs.push_back(formatter = new invert_transactions(formatter));
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
// 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));
|
||||
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
42
config.h
42
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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
2
emacs.cc
2
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) {
|
||||
|
|
|
|||
10
format.cc
10
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,
|
||||
|
|
|
|||
10
journal.cc
10
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> >())
|
||||
|
|
|
|||
10
ledger.el
10
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))
|
||||
|
|
|
|||
12
ledger.h
12
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
|
||||
|
|
|
|||
295
main.cc
295
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;
|
||||
using namespace ledger;
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
138
parser.cc
138
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
|
||||
|
|
|
|||
18
parser.h
18
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
|
||||
|
|
|
|||
54
startup.cc
Normal file
54
startup.cc
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
18
textual.cc
18
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()) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
2
timing.h
2
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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
93
walk.cc
93
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);
|
||||
|
|
|
|||
60
walk.h
60
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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue