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
|
lib_LTLIBRARIES = libledger.la
|
||||||
libledger_a_CXXFLAGS =
|
libledger_la_CXXFLAGS =
|
||||||
libledger_a_SOURCES = \
|
libledger_la_SOURCES = \
|
||||||
amount.cc \
|
amount.cc \
|
||||||
balance.cc \
|
balance.cc \
|
||||||
binary.cc \
|
binary.cc \
|
||||||
|
|
@ -16,30 +16,32 @@ libledger_a_SOURCES = \
|
||||||
qif.cc \
|
qif.cc \
|
||||||
quotes.cc \
|
quotes.cc \
|
||||||
reconcile.cc \
|
reconcile.cc \
|
||||||
|
startup.cc \
|
||||||
textual.cc \
|
textual.cc \
|
||||||
valexpr.cc \
|
valexpr.cc \
|
||||||
value.cc \
|
value.cc \
|
||||||
walk.cc
|
walk.cc
|
||||||
if HAVE_EXPAT
|
if HAVE_EXPAT
|
||||||
libledger_a_CXXFLAGS += -DHAVE_EXPAT=1
|
libledger_la_CXXFLAGS += -DHAVE_EXPAT=1
|
||||||
libledger_a_SOURCES += gnucash.cc xml.cc
|
libledger_la_SOURCES += gnucash.cc xml.cc
|
||||||
endif
|
endif
|
||||||
if HAVE_XMLPARSE
|
if HAVE_XMLPARSE
|
||||||
libledger_a_CXXFLAGS += -DHAVE_XMLPARSE=1
|
libledger_la_CXXFLAGS += -DHAVE_XMLPARSE=1
|
||||||
libledger_a_SOURCES += gnucash.cc xml.cc
|
libledger_la_SOURCES += gnucash.cc xml.cc
|
||||||
endif
|
endif
|
||||||
if HAVE_LIBOFX
|
if HAVE_LIBOFX
|
||||||
libledger_a_CXXFLAGS += -DHAVE_LIBOFX=1
|
libledger_la_CXXFLAGS += -DHAVE_LIBOFX=1
|
||||||
libledger_a_SOURCES += ofx.cc
|
libledger_la_SOURCES += ofx.cc
|
||||||
endif
|
endif
|
||||||
if HAVE_BOOST_PYTHON
|
if HAVE_BOOST_PYTHON
|
||||||
libledger_a_CXXFLAGS += -DUSE_BOOST_PYTHON=1
|
libledger_la_CXXFLAGS += -DUSE_BOOST_PYTHON=1
|
||||||
libledger_a_SOURCES += py_eval.cc
|
libledger_la_SOURCES += py_eval.cc
|
||||||
endif
|
endif
|
||||||
if DEBUG
|
if DEBUG
|
||||||
libledger_a_CXXFLAGS += -DDEBUG_LEVEL=4
|
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
|
||||||
libledger_a_SOURCES += debug.cc
|
libledger_la_SOURCES += debug.cc
|
||||||
endif
|
endif
|
||||||
|
libledger_la_LDFLAGS = -version-info 3:0
|
||||||
|
|
||||||
pkginclude_HEADERS = \
|
pkginclude_HEADERS = \
|
||||||
acconf.h \
|
acconf.h \
|
||||||
|
|
@ -79,9 +81,9 @@ ledger_CXXFLAGS =
|
||||||
ledger_SOURCES = main.cc
|
ledger_SOURCES = main.cc
|
||||||
if HAVE_BOOST_PYTHON
|
if HAVE_BOOST_PYTHON
|
||||||
ledger_CXXFLAGS += -DUSE_BOOST_PYTHON=1
|
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
|
else
|
||||||
ledger_LDADD = $(LIBOBJS) libledger.a
|
ledger_LDADD = $(LIBOBJS) libledger.la
|
||||||
endif
|
endif
|
||||||
if HAVE_EXPAT
|
if HAVE_EXPAT
|
||||||
ledger_CXXFLAGS += -DHAVE_EXPAT=1
|
ledger_CXXFLAGS += -DHAVE_EXPAT=1
|
||||||
|
|
@ -98,6 +100,7 @@ endif
|
||||||
if DEBUG
|
if DEBUG
|
||||||
ledger_CXXFLAGS += -DDEBUG_LEVEL=4
|
ledger_CXXFLAGS += -DDEBUG_LEVEL=4
|
||||||
endif
|
endif
|
||||||
|
ledger_LDFLAGS = -static # for the sake of command-line speed
|
||||||
|
|
||||||
info_TEXINFOS = ledger.texi
|
info_TEXINFOS = ledger.texi
|
||||||
|
|
||||||
|
|
@ -123,15 +126,15 @@ else
|
||||||
HAVE_LIBOFX_VALUE = false
|
HAVE_LIBOFX_VALUE = false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ledger.so: py_eval.cc libledger.a
|
ledger.so: py_eval.cc libledger.la
|
||||||
CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L." \
|
CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
|
||||||
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
|
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
|
||||||
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
|
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
|
||||||
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
|
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
|
||||||
python setup.py build --build-lib=.
|
python setup.py build --build-lib=.
|
||||||
|
|
||||||
install-exec-hook:
|
install-exec-hook:
|
||||||
CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L." \
|
CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
|
||||||
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
|
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
|
||||||
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
|
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
|
||||||
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
|
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
|
||||||
|
|
@ -143,7 +146,7 @@ all-clean: maintainer-clean
|
||||||
rm -fr *~ .*~ .\#* *.html *.info *.pdf *.a *.so *.o *.lo *.la \
|
rm -fr *~ .*~ .\#* *.html *.info *.pdf *.a *.so *.o *.lo *.la \
|
||||||
*.elc *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr *.pyc \
|
*.elc *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr *.pyc \
|
||||||
.gdb_history gmon.out h out TAGS ledger valexpr .deps \
|
.gdb_history gmon.out h out TAGS ledger valexpr .deps \
|
||||||
build AUTHORS COPYING INSTALL Makefile aclocal.m4 autom4te \
|
.libs build AUTHORS COPYING INSTALL Makefile acconf.h \
|
||||||
acconf.h acconf.h.in config.guess config.sub configure \
|
acconf.h.in aclocal.m4 autom4te config.guess config.sub \
|
||||||
depcomp install-sh missing stamp texinfo.tex Makefile.in \
|
configure depcomp install-sh libtool ltconfig ltmain.sh \
|
||||||
results.out
|
missing stamp texinfo.tex Makefile.in
|
||||||
|
|
|
||||||
3
NEWS
3
NEWS
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
* 2.5
|
* 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:
|
- Effective dates may now be specified for entries:
|
||||||
|
|
||||||
2004/10/03=2004/09/30 Credit card company
|
2004/10/03=2004/09/30 Credit card company
|
||||||
|
|
|
||||||
1
acprep
1
acprep
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
touch AUTHORS COPYING
|
touch AUTHORS COPYING
|
||||||
|
|
||||||
|
glibtoolize --automake -f -c
|
||||||
aclocal
|
aclocal
|
||||||
autoheader
|
autoheader
|
||||||
if [ "$1" = "--dist" ]; then
|
if [ "$1" = "--dist" ]; then
|
||||||
|
|
|
||||||
537
config.cc
537
config.cc
|
|
@ -20,10 +20,11 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
config_t config;
|
|
||||||
std::list<option_t> config_options;
|
std::list<option_t> config_options;
|
||||||
|
|
||||||
config_t::config_t()
|
static config_t * config = NULL;
|
||||||
|
|
||||||
|
void config_t::reset()
|
||||||
{
|
{
|
||||||
amount_expr = "a";
|
amount_expr = "a";
|
||||||
total_expr = "O";
|
total_expr = "O";
|
||||||
|
|
@ -73,12 +74,13 @@ config_t::config_t()
|
||||||
cache_dirty = false;
|
cache_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
regexps_to_predicate(config_t& config, const std::string& command,
|
config_t::regexps_to_predicate(const std::string& command,
|
||||||
std::list<std::string>::const_iterator begin,
|
std::list<std::string>::const_iterator begin,
|
||||||
std::list<std::string>::const_iterator end,
|
std::list<std::string>::const_iterator end,
|
||||||
const bool account_regexp = false,
|
const bool account_regexp,
|
||||||
const bool add_account_short_masks = false)
|
const bool add_account_short_masks,
|
||||||
|
const bool logical_and)
|
||||||
{
|
{
|
||||||
std::string regexps[2];
|
std::string regexps[2];
|
||||||
|
|
||||||
|
|
@ -110,12 +112,12 @@ regexps_to_predicate(config_t& config, const std::string& command,
|
||||||
if (regexps[i].empty())
|
if (regexps[i].empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (! config.predicate.empty())
|
if (! predicate.empty())
|
||||||
config.predicate += "&";
|
predicate += logical_and ? "&" : "|";
|
||||||
|
|
||||||
int add_predicate = 0; // 1 adds /.../, 2 adds ///.../
|
int add_predicate = 0; // 1 adds /.../, 2 adds ///.../
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
config.predicate += "!";
|
predicate += "!";
|
||||||
}
|
}
|
||||||
else if (add_account_short_masks) {
|
else if (add_account_short_masks) {
|
||||||
if (regexps[i].find(':') != std::string::npos ||
|
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 ||
|
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;
|
add_predicate = 1;
|
||||||
} else {
|
} else {
|
||||||
add_predicate = 2;
|
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 (i != 1 && command == "b" && account_regexp) {
|
||||||
if (! config.display_predicate.empty())
|
if (! display_predicate.empty())
|
||||||
config.display_predicate += "&";
|
display_predicate += "&";
|
||||||
else if (! config.show_empty)
|
else if (! show_empty)
|
||||||
config.display_predicate += "T&";
|
display_predicate += "T&";
|
||||||
|
|
||||||
if (add_predicate == 2)
|
if (add_predicate == 2)
|
||||||
config.display_predicate += "//";
|
display_predicate += "//";
|
||||||
config.display_predicate += "/(?:";
|
display_predicate += "/(?:";
|
||||||
config.display_predicate += regexps[i];
|
display_predicate += regexps[i];
|
||||||
config.display_predicate += ")/";
|
display_predicate += ")/";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! account_regexp)
|
if (! account_regexp)
|
||||||
config.predicate += "/";
|
predicate += "/";
|
||||||
config.predicate += "/(?:";
|
predicate += "/(?:";
|
||||||
config.predicate += regexps[i];
|
predicate += regexps[i];
|
||||||
config.predicate += ")/";
|
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,
|
void config_t::process_options(const std::string& command,
|
||||||
strings_list::iterator arg,
|
strings_list::iterator arg,
|
||||||
strings_list::iterator args_end)
|
strings_list::iterator args_end)
|
||||||
|
|
@ -189,11 +214,11 @@ void config_t::process_options(const std::string& command,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (i != arg)
|
if (i != arg)
|
||||||
regexps_to_predicate(*this, command, arg, i, true,
|
regexps_to_predicate(command, arg, i, true,
|
||||||
(command == "b" && ! show_subtotal &&
|
(command == "b" && ! show_subtotal &&
|
||||||
display_predicate.empty()));
|
display_predicate.empty()));
|
||||||
if (i != args_end && ++i != args_end)
|
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
|
// 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;
|
format_t::date_format = date_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_ledger_data(journal_t * journal, parser_t * cache_parser,
|
item_handler<transaction_t> *
|
||||||
parser_t * text_parser, parser_t * xml_parser)
|
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() &&
|
// format_transactions write each transaction received to the
|
||||||
access(config.init_file.c_str(), R_OK) != -1) {
|
// output stream.
|
||||||
if (parse_journal_file(config.init_file, journal) ||
|
if (! (command == "b" || command == "E")) {
|
||||||
journal->auto_entries.size() > 0 ||
|
// truncate_entries cuts off a certain number of _entries_ from
|
||||||
journal->period_entries.size() > 0)
|
// being displayed. It does not affect calculation.
|
||||||
throw error(std::string("Entries found in initialization file '") +
|
if (head_entries || tail_entries)
|
||||||
config.init_file + "'");
|
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 &&
|
// subtotal_transactions combines all the transactions it receives
|
||||||
! config.cache_file.empty() &&
|
// into one subtotal entry, which has one transaction for each
|
||||||
! config.data_file.empty()) {
|
// commodity in each account.
|
||||||
DEBUG_PRINT("ledger.config.cache", "using_cache " << config.cache_file);
|
//
|
||||||
config.cache_dirty = true;
|
// period_transactions is like subtotal_transactions, but it
|
||||||
if (access(config.cache_file.c_str(), R_OK) != -1) {
|
// subtotals according to time periods rather than totalling
|
||||||
std::ifstream stream(config.cache_file.c_str());
|
// everything.
|
||||||
if (cache_parser->test(stream)) {
|
//
|
||||||
std::string price_db_orig = journal->price_db;
|
// dow_transactions is like period_transactions, except that it
|
||||||
journal->price_db = config.price_db;
|
// reports all the transactions that fall on each subsequent day
|
||||||
entry_count += cache_parser->parse(stream, journal, NULL,
|
// of the week.
|
||||||
&config.data_file);
|
if (show_subtotal && ! (command == "b" || command == "E"))
|
||||||
if (entry_count > 0)
|
ptrs.push_back(formatter = new subtotal_transactions(formatter));
|
||||||
config.cache_dirty = false;
|
|
||||||
else
|
if (days_of_the_week)
|
||||||
journal->price_db = price_db_orig;
|
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()) {
|
// invert_transactions inverts the value of the transactions it
|
||||||
account_t * account = NULL;
|
// receives.
|
||||||
if (! config.account.empty())
|
if (show_inverted)
|
||||||
account = journal->find_account(config.account);
|
ptrs.push_back(formatter = new invert_transactions(formatter));
|
||||||
|
|
||||||
journal->price_db = config.price_db;
|
// related_transactions will pass along all transactions related
|
||||||
if (! journal->price_db.empty() &&
|
// to the transaction received. If `show_all_related' is true,
|
||||||
access(journal->price_db.c_str(), R_OK) != -1) {
|
// then all the entry's transactions are passed; meaning that if
|
||||||
if (parse_journal_file(journal->price_db, journal)) {
|
// one transaction of an entry is to be printed, all the
|
||||||
throw error("Entries not allowed in price history file");
|
// transaction for that entry will be printed.
|
||||||
} else {
|
if (show_related)
|
||||||
DEBUG_PRINT("ledger.config.cache",
|
ptrs.push_back(formatter =
|
||||||
"read price database " << journal->price_db);
|
new related_transactions(formatter,
|
||||||
journal->sources.pop_back();
|
show_all_related));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_PRINT("ledger.config.cache",
|
// This filter_transactions will only pass through transactions
|
||||||
"rejected cache, parsing " << config.data_file);
|
// matching the `predicate'.
|
||||||
if (config.data_file == "-") {
|
if (! predicate.empty())
|
||||||
config.use_cache = false;
|
ptrs.push_back(formatter = new filter_transactions(formatter, predicate));
|
||||||
journal->sources.push_back("<stdin>");
|
|
||||||
if (xml_parser && std::cin.peek() == '<')
|
// budget_transactions takes a set of transactions from a data
|
||||||
entry_count += xml_parser->parse(std::cin, journal, account);
|
// file and uses them to generate "budget transactions" which
|
||||||
else
|
// balance against the reported transactions.
|
||||||
entry_count += text_parser->parse(std::cin, journal, account);
|
//
|
||||||
}
|
// forecast_transactions is a lot like budget_transactions, except
|
||||||
else if (access(config.data_file.c_str(), R_OK) != -1) {
|
// that it adds entries only for the future, and does not balance
|
||||||
entry_count += parse_journal_file(config.data_file, journal, account);
|
// them against anything but the future balance.
|
||||||
if (! journal->price_db.empty())
|
|
||||||
journal->sources.push_back(journal->price_db);
|
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)
|
if (comm_as_payee)
|
||||||
throw error("Please specify ledger file using -f"
|
ptrs.push_back(formatter = new set_comm_as_payee(formatter));
|
||||||
" or LEDGER_FILE environment variable.");
|
|
||||||
|
|
||||||
VALIDATE(journal->valid());
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_version(std::ostream& out)
|
static void show_version(std::ostream& out)
|
||||||
|
|
@ -586,38 +685,42 @@ OPT_BEGIN(version, "v") {
|
||||||
} OPT_END(version);
|
} OPT_END(version);
|
||||||
|
|
||||||
OPT_BEGIN(init_file, "i:") {
|
OPT_BEGIN(init_file, "i:") {
|
||||||
config.init_file = optarg;
|
config->init_file = optarg;
|
||||||
} OPT_END(init_file);
|
} OPT_END(init_file);
|
||||||
|
|
||||||
OPT_BEGIN(file, "f:") {
|
OPT_BEGIN(file, "f:") {
|
||||||
if (std::string(optarg) == "-" || access(optarg, R_OK) != -1)
|
if (std::string(optarg) == "-" || access(optarg, R_OK) != -1)
|
||||||
config.data_file = optarg;
|
config->data_file = optarg;
|
||||||
else
|
else
|
||||||
throw error(std::string("The ledger file '") + optarg +
|
throw error(std::string("The ledger file '") + optarg +
|
||||||
"' does not exist or is not readable");
|
"' does not exist or is not readable");
|
||||||
} OPT_END(file);
|
} OPT_END(file);
|
||||||
|
|
||||||
OPT_BEGIN(cache, ":") {
|
OPT_BEGIN(cache, ":") {
|
||||||
config.cache_file = optarg;
|
config->cache_file = optarg;
|
||||||
} OPT_END(cache);
|
} OPT_END(cache);
|
||||||
|
|
||||||
OPT_BEGIN(no_cache, "") {
|
OPT_BEGIN(no_cache, "") {
|
||||||
config.cache_file = "<none>";
|
config->cache_file = "<none>";
|
||||||
} OPT_END(no_cache);
|
} OPT_END(no_cache);
|
||||||
|
|
||||||
OPT_BEGIN(output, "o:") {
|
OPT_BEGIN(output, "o:") {
|
||||||
if (std::string(optarg) != "-")
|
if (std::string(optarg) != "-")
|
||||||
config.output_file = optarg;
|
config->output_file = optarg;
|
||||||
} OPT_END(output);
|
} OPT_END(output);
|
||||||
|
|
||||||
OPT_BEGIN(account, "a:") {
|
OPT_BEGIN(account, "a:") {
|
||||||
config.account = optarg;
|
config->account = optarg;
|
||||||
} OPT_END(account);
|
} OPT_END(account);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Report filtering
|
// Report filtering
|
||||||
|
|
||||||
|
OPT_BEGIN(effective, "") {
|
||||||
|
transaction_t::use_effective_date = true;
|
||||||
|
} OPT_END(effective);
|
||||||
|
|
||||||
OPT_BEGIN(begin, "b:") {
|
OPT_BEGIN(begin, "b:") {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
interval_t interval(optarg);
|
interval_t interval(optarg);
|
||||||
|
|
@ -627,11 +730,11 @@ OPT_BEGIN(begin, "b:") {
|
||||||
throw error(std::string("Could not determine beginning of period '") +
|
throw error(std::string("Could not determine beginning of period '") +
|
||||||
optarg + "'");
|
optarg + "'");
|
||||||
|
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "d>=[";
|
config->predicate += "d>=[";
|
||||||
config.predicate += buf;
|
config->predicate += buf;
|
||||||
config.predicate += "]";
|
config->predicate += "]";
|
||||||
} OPT_END(begin);
|
} OPT_END(begin);
|
||||||
|
|
||||||
OPT_BEGIN(end, "e:") {
|
OPT_BEGIN(end, "e:") {
|
||||||
|
|
@ -643,43 +746,43 @@ OPT_BEGIN(end, "e:") {
|
||||||
throw error(std::string("Could not determine end of period '") +
|
throw error(std::string("Could not determine end of period '") +
|
||||||
optarg + "'");
|
optarg + "'");
|
||||||
|
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "d<[";
|
config->predicate += "d<[";
|
||||||
config.predicate += buf;
|
config->predicate += buf;
|
||||||
config.predicate += "]";
|
config->predicate += "]";
|
||||||
|
|
||||||
terminus = interval.end;
|
terminus = interval.end;
|
||||||
} OPT_END(end);
|
} OPT_END(end);
|
||||||
|
|
||||||
OPT_BEGIN(current, "c") {
|
OPT_BEGIN(current, "c") {
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "d<=m";
|
config->predicate += "d<=m";
|
||||||
} OPT_END(current);
|
} OPT_END(current);
|
||||||
|
|
||||||
OPT_BEGIN(cleared, "C") {
|
OPT_BEGIN(cleared, "C") {
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "X";
|
config->predicate += "X";
|
||||||
} OPT_END(cleared);
|
} OPT_END(cleared);
|
||||||
|
|
||||||
OPT_BEGIN(uncleared, "U") {
|
OPT_BEGIN(uncleared, "U") {
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "!X";
|
config->predicate += "!X";
|
||||||
} OPT_END(uncleared);
|
} OPT_END(uncleared);
|
||||||
|
|
||||||
OPT_BEGIN(real, "R") {
|
OPT_BEGIN(real, "R") {
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "R";
|
config->predicate += "R";
|
||||||
} OPT_END(real);
|
} OPT_END(real);
|
||||||
|
|
||||||
OPT_BEGIN(actual, "L") {
|
OPT_BEGIN(actual, "L") {
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "L";
|
config->predicate += "L";
|
||||||
} OPT_END(actual);
|
} OPT_END(actual);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -687,11 +790,11 @@ OPT_BEGIN(actual, "L") {
|
||||||
// Output customization
|
// Output customization
|
||||||
|
|
||||||
OPT_BEGIN(format, "F:") {
|
OPT_BEGIN(format, "F:") {
|
||||||
config.format_string = optarg;
|
config->format_string = optarg;
|
||||||
} OPT_END(format);
|
} OPT_END(format);
|
||||||
|
|
||||||
OPT_BEGIN(date_format, "y:") {
|
OPT_BEGIN(date_format, "y:") {
|
||||||
config.date_format = optarg;
|
config->date_format = optarg;
|
||||||
} OPT_END(date_format);
|
} OPT_END(date_format);
|
||||||
|
|
||||||
OPT_BEGIN(input_date_format, ":") {
|
OPT_BEGIN(input_date_format, ":") {
|
||||||
|
|
@ -700,95 +803,91 @@ OPT_BEGIN(input_date_format, ":") {
|
||||||
} OPT_END(input_date_format);
|
} OPT_END(input_date_format);
|
||||||
|
|
||||||
OPT_BEGIN(balance_format, ":") {
|
OPT_BEGIN(balance_format, ":") {
|
||||||
config.balance_format = optarg;
|
config->balance_format = optarg;
|
||||||
} OPT_END(balance_format);
|
} OPT_END(balance_format);
|
||||||
|
|
||||||
OPT_BEGIN(register_format, ":") {
|
OPT_BEGIN(register_format, ":") {
|
||||||
config.register_format = optarg;
|
config->register_format = optarg;
|
||||||
} OPT_END(register_format);
|
} OPT_END(register_format);
|
||||||
|
|
||||||
OPT_BEGIN(wide_register_format, ":") {
|
OPT_BEGIN(wide_register_format, ":") {
|
||||||
config.wide_register_format = optarg;
|
config->wide_register_format = optarg;
|
||||||
} OPT_END(wide_register_format);
|
} OPT_END(wide_register_format);
|
||||||
|
|
||||||
OPT_BEGIN(plot_amount_format, ":") {
|
OPT_BEGIN(plot_amount_format, ":") {
|
||||||
config.plot_amount_format = optarg;
|
config->plot_amount_format = optarg;
|
||||||
} OPT_END(plot_amount_format);
|
} OPT_END(plot_amount_format);
|
||||||
|
|
||||||
OPT_BEGIN(plot_total_format, ":") {
|
OPT_BEGIN(plot_total_format, ":") {
|
||||||
config.plot_total_format = optarg;
|
config->plot_total_format = optarg;
|
||||||
|
|
||||||
OPT_BEGIN(effective, "") {
|
|
||||||
transaction_t::use_effective_date = true;
|
|
||||||
} OPT_END(effective);
|
|
||||||
} OPT_END(plot_total_format);
|
} OPT_END(plot_total_format);
|
||||||
|
|
||||||
OPT_BEGIN(print_format, ":") {
|
OPT_BEGIN(print_format, ":") {
|
||||||
config.print_format = optarg;
|
config->print_format = optarg;
|
||||||
} OPT_END(print_format);
|
} OPT_END(print_format);
|
||||||
|
|
||||||
OPT_BEGIN(write_hdr_format, ":") {
|
OPT_BEGIN(write_hdr_format, ":") {
|
||||||
config.write_hdr_format = optarg;
|
config->write_hdr_format = optarg;
|
||||||
} OPT_END(write_hdr_format);
|
} OPT_END(write_hdr_format);
|
||||||
|
|
||||||
OPT_BEGIN(write_xact_format, ":") {
|
OPT_BEGIN(write_xact_format, ":") {
|
||||||
config.write_xact_format = optarg;
|
config->write_xact_format = optarg;
|
||||||
} OPT_END(write_xact_format);
|
} OPT_END(write_xact_format);
|
||||||
|
|
||||||
OPT_BEGIN(equity_format, ":") {
|
OPT_BEGIN(equity_format, ":") {
|
||||||
config.equity_format = optarg;
|
config->equity_format = optarg;
|
||||||
} OPT_END(equity_format);
|
} OPT_END(equity_format);
|
||||||
|
|
||||||
OPT_BEGIN(prices_format, ":") {
|
OPT_BEGIN(prices_format, ":") {
|
||||||
config.prices_format = optarg;
|
config->prices_format = optarg;
|
||||||
} OPT_END(prices_format);
|
} OPT_END(prices_format);
|
||||||
|
|
||||||
OPT_BEGIN(wide, "w") {
|
OPT_BEGIN(wide, "w") {
|
||||||
config.register_format = config.wide_register_format;
|
config->register_format = config->wide_register_format;
|
||||||
} OPT_END(wide);
|
} OPT_END(wide);
|
||||||
|
|
||||||
OPT_BEGIN(head, ":") {
|
OPT_BEGIN(head, ":") {
|
||||||
config.head_entries = std::atoi(optarg);
|
config->head_entries = std::atoi(optarg);
|
||||||
} OPT_END(head);
|
} OPT_END(head);
|
||||||
|
|
||||||
OPT_BEGIN(tail, ":") {
|
OPT_BEGIN(tail, ":") {
|
||||||
config.tail_entries = std::atoi(optarg);
|
config->tail_entries = std::atoi(optarg);
|
||||||
} OPT_END(tail);
|
} OPT_END(tail);
|
||||||
|
|
||||||
OPT_BEGIN(pager, ":") {
|
OPT_BEGIN(pager, ":") {
|
||||||
config.pager = optarg;
|
config->pager = optarg;
|
||||||
} OPT_END(pager);
|
} OPT_END(pager);
|
||||||
|
|
||||||
OPT_BEGIN(empty, "E") {
|
OPT_BEGIN(empty, "E") {
|
||||||
config.show_empty = true;
|
config->show_empty = true;
|
||||||
} OPT_END(empty);
|
} OPT_END(empty);
|
||||||
|
|
||||||
OPT_BEGIN(collapse, "n") {
|
OPT_BEGIN(collapse, "n") {
|
||||||
config.show_collapsed = true;
|
config->show_collapsed = true;
|
||||||
} OPT_END(collapse);
|
} OPT_END(collapse);
|
||||||
|
|
||||||
OPT_BEGIN(subtotal, "s") {
|
OPT_BEGIN(subtotal, "s") {
|
||||||
config.show_subtotal = true;
|
config->show_subtotal = true;
|
||||||
} OPT_END(subtotal);
|
} OPT_END(subtotal);
|
||||||
|
|
||||||
OPT_BEGIN(totals, "") {
|
OPT_BEGIN(totals, "") {
|
||||||
config.show_totals = true;
|
config->show_totals = true;
|
||||||
} OPT_END(totals);
|
} OPT_END(totals);
|
||||||
|
|
||||||
OPT_BEGIN(sort, "S:") {
|
OPT_BEGIN(sort, "S:") {
|
||||||
config.sort_string = optarg;
|
config->sort_string = optarg;
|
||||||
} OPT_END(sort);
|
} OPT_END(sort);
|
||||||
|
|
||||||
OPT_BEGIN(related, "r") {
|
OPT_BEGIN(related, "r") {
|
||||||
config.show_related = true;
|
config->show_related = true;
|
||||||
} OPT_END(related);
|
} OPT_END(related);
|
||||||
|
|
||||||
OPT_BEGIN(period, "p:") {
|
OPT_BEGIN(period, "p:") {
|
||||||
if (config.report_period.empty()) {
|
if (config->report_period.empty()) {
|
||||||
config.report_period = optarg;
|
config->report_period = optarg;
|
||||||
} else {
|
} else {
|
||||||
config.report_period += " ";
|
config->report_period += " ";
|
||||||
config.report_period += optarg;
|
config->report_period += optarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the period gives a beginning and/or ending date, make sure to
|
// 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.
|
// options) to take this into account.
|
||||||
|
|
||||||
char buf[128];
|
char buf[128];
|
||||||
interval_t interval(config.report_period);
|
interval_t interval(config->report_period);
|
||||||
|
|
||||||
if (interval.begin) {
|
if (interval.begin) {
|
||||||
std::strftime(buf, 127, formats[0], std::localtime(&interval.begin));
|
std::strftime(buf, 127, formats[0], std::localtime(&interval.begin));
|
||||||
|
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "d>=[";
|
config->predicate += "d>=[";
|
||||||
config.predicate += buf;
|
config->predicate += buf;
|
||||||
config.predicate += "]";
|
config->predicate += "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interval.end) {
|
if (interval.end) {
|
||||||
std::strftime(buf, 127, formats[0], std::localtime(&interval.end));
|
std::strftime(buf, 127, formats[0], std::localtime(&interval.end));
|
||||||
|
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "d<[";
|
config->predicate += "d<[";
|
||||||
config.predicate += buf;
|
config->predicate += buf;
|
||||||
config.predicate += "]";
|
config->predicate += "]";
|
||||||
|
|
||||||
terminus = interval.end;
|
terminus = interval.end;
|
||||||
}
|
}
|
||||||
} OPT_END(period);
|
} OPT_END(period);
|
||||||
|
|
||||||
OPT_BEGIN(period_sort, ":") {
|
OPT_BEGIN(period_sort, ":") {
|
||||||
config.report_period_sort = optarg;
|
config->report_period_sort = optarg;
|
||||||
} OPT_END(period_sort);
|
} OPT_END(period_sort);
|
||||||
|
|
||||||
OPT_BEGIN(weekly, "W") {
|
OPT_BEGIN(weekly, "W") {
|
||||||
if (config.report_period.empty())
|
if (config->report_period.empty())
|
||||||
config.report_period = "weekly";
|
config->report_period = "weekly";
|
||||||
else
|
else
|
||||||
config.report_period = std::string("weekly ") + config.report_period;
|
config->report_period = std::string("weekly ") + config->report_period;
|
||||||
} OPT_END(weekly);
|
} OPT_END(weekly);
|
||||||
|
|
||||||
OPT_BEGIN(monthly, "M") {
|
OPT_BEGIN(monthly, "M") {
|
||||||
if (config.report_period.empty())
|
if (config->report_period.empty())
|
||||||
config.report_period = "monthly";
|
config->report_period = "monthly";
|
||||||
else
|
else
|
||||||
config.report_period = std::string("monthly ") + config.report_period;
|
config->report_period = std::string("monthly ") + config->report_period;
|
||||||
} OPT_END(monthly);
|
} OPT_END(monthly);
|
||||||
|
|
||||||
OPT_BEGIN(yearly, "Y") {
|
OPT_BEGIN(yearly, "Y") {
|
||||||
if (config.report_period.empty())
|
if (config->report_period.empty())
|
||||||
config.report_period = "yearly";
|
config->report_period = "yearly";
|
||||||
else
|
else
|
||||||
config.report_period = std::string("yearly ") + config.report_period;
|
config->report_period = std::string("yearly ") + config->report_period;
|
||||||
} OPT_END(yearly);
|
} OPT_END(yearly);
|
||||||
|
|
||||||
OPT_BEGIN(dow, "") {
|
OPT_BEGIN(dow, "") {
|
||||||
config.days_of_the_week = true;
|
config->days_of_the_week = true;
|
||||||
} OPT_END(dow);
|
} OPT_END(dow);
|
||||||
|
|
||||||
OPT_BEGIN(by_payee, "P") {
|
OPT_BEGIN(by_payee, "P") {
|
||||||
config.by_payee = true;
|
config->by_payee = true;
|
||||||
} OPT_END(by_payee);
|
} OPT_END(by_payee);
|
||||||
|
|
||||||
OPT_BEGIN(comm_as_payee, "x") {
|
OPT_BEGIN(comm_as_payee, "x") {
|
||||||
config.comm_as_payee = true;
|
config->comm_as_payee = true;
|
||||||
} OPT_END(comm_as_payee);
|
} OPT_END(comm_as_payee);
|
||||||
|
|
||||||
OPT_BEGIN(budget, "") {
|
OPT_BEGIN(budget, "") {
|
||||||
config.budget_flags = BUDGET_BUDGETED;
|
config->budget_flags = BUDGET_BUDGETED;
|
||||||
} OPT_END(budget);
|
} OPT_END(budget);
|
||||||
|
|
||||||
OPT_BEGIN(add_budget, "") {
|
OPT_BEGIN(add_budget, "") {
|
||||||
config.budget_flags = BUDGET_BUDGETED | BUDGET_UNBUDGETED;
|
config->budget_flags = BUDGET_BUDGETED | BUDGET_UNBUDGETED;
|
||||||
} OPT_END(add_budget);
|
} OPT_END(add_budget);
|
||||||
|
|
||||||
OPT_BEGIN(unbudgeted, "") {
|
OPT_BEGIN(unbudgeted, "") {
|
||||||
config.budget_flags = BUDGET_UNBUDGETED;
|
config->budget_flags = BUDGET_UNBUDGETED;
|
||||||
} OPT_END(unbudgeted);
|
} OPT_END(unbudgeted);
|
||||||
|
|
||||||
OPT_BEGIN(forecast, ":") {
|
OPT_BEGIN(forecast, ":") {
|
||||||
config.forecast_limit = optarg;
|
config->forecast_limit = optarg;
|
||||||
} OPT_END(forecast);
|
} OPT_END(forecast);
|
||||||
|
|
||||||
OPT_BEGIN(reconcile, ":") {
|
OPT_BEGIN(reconcile, ":") {
|
||||||
config.reconcile_balance = optarg;
|
config->reconcile_balance = optarg;
|
||||||
} OPT_END(reconcile);
|
} OPT_END(reconcile);
|
||||||
|
|
||||||
OPT_BEGIN(reconcile_date, ":") {
|
OPT_BEGIN(reconcile_date, ":") {
|
||||||
config.reconcile_date = optarg;
|
config->reconcile_date = optarg;
|
||||||
} OPT_END(reconcile_date);
|
} OPT_END(reconcile_date);
|
||||||
|
|
||||||
OPT_BEGIN(limit, "l:") {
|
OPT_BEGIN(limit, "l:") {
|
||||||
if (! config.predicate.empty())
|
if (! config->predicate.empty())
|
||||||
config.predicate += "&";
|
config->predicate += "&";
|
||||||
config.predicate += "(";
|
config->predicate += "(";
|
||||||
config.predicate += optarg;
|
config->predicate += optarg;
|
||||||
config.predicate += ")";
|
config->predicate += ")";
|
||||||
} OPT_END(limit);
|
} OPT_END(limit);
|
||||||
|
|
||||||
OPT_BEGIN(display, "d:") {
|
OPT_BEGIN(display, "d:") {
|
||||||
if (! config.display_predicate.empty())
|
if (! config->display_predicate.empty())
|
||||||
config.display_predicate += "&";
|
config->display_predicate += "&";
|
||||||
config.display_predicate += "(";
|
config->display_predicate += "(";
|
||||||
config.display_predicate += optarg;
|
config->display_predicate += optarg;
|
||||||
config.display_predicate += ")";
|
config->display_predicate += ")";
|
||||||
} OPT_END(display);
|
} OPT_END(display);
|
||||||
|
|
||||||
OPT_BEGIN(amount, "t:") {
|
OPT_BEGIN(amount, "t:") {
|
||||||
config.amount_expr = optarg;
|
config->amount_expr = optarg;
|
||||||
} OPT_END(amount);
|
} OPT_END(amount);
|
||||||
|
|
||||||
OPT_BEGIN(total, "T:") {
|
OPT_BEGIN(total, "T:") {
|
||||||
config.total_expr = optarg;
|
config->total_expr = optarg;
|
||||||
} OPT_END(total);
|
} OPT_END(total);
|
||||||
|
|
||||||
OPT_BEGIN(amount_data, "j") {
|
OPT_BEGIN(amount_data, "j") {
|
||||||
config.format_string = config.plot_amount_format;
|
config->format_string = config->plot_amount_format;
|
||||||
} OPT_END(amount_data);
|
} OPT_END(amount_data);
|
||||||
|
|
||||||
|
|
||||||
OPT_BEGIN(total_data, "J") {
|
OPT_BEGIN(total_data, "J") {
|
||||||
config.format_string = config.plot_total_format;
|
config->format_string = config->plot_total_format;
|
||||||
} OPT_END(total_data);
|
} OPT_END(total_data);
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Commodity reporting
|
// Commodity reporting
|
||||||
|
|
||||||
OPT_BEGIN(price_db, ":") {
|
OPT_BEGIN(price_db, ":") {
|
||||||
config.price_db = optarg;
|
config->price_db = optarg;
|
||||||
} OPT_END(price_db);
|
} OPT_END(price_db);
|
||||||
|
|
||||||
OPT_BEGIN(price_exp, "Z:") {
|
OPT_BEGIN(price_exp, "Z:") {
|
||||||
config.pricing_leeway = std::atol(optarg) * 60;
|
config->pricing_leeway = std::atol(optarg) * 60;
|
||||||
} OPT_END(price_exp);
|
} OPT_END(price_exp);
|
||||||
|
|
||||||
OPT_BEGIN(download, "Q") {
|
OPT_BEGIN(download, "Q") {
|
||||||
config.download_quotes = true;
|
config->download_quotes = true;
|
||||||
} OPT_END(download);
|
} OPT_END(download);
|
||||||
|
|
||||||
OPT_BEGIN(quantity, "O") {
|
OPT_BEGIN(quantity, "O") {
|
||||||
config.amount_expr = "a";
|
config->amount_expr = "a";
|
||||||
config.total_expr = "O";
|
config->total_expr = "O";
|
||||||
} OPT_END(quantity);
|
} OPT_END(quantity);
|
||||||
|
|
||||||
OPT_BEGIN(basis, "B") {
|
OPT_BEGIN(basis, "B") {
|
||||||
config.amount_expr = "b";
|
config->amount_expr = "b";
|
||||||
config.total_expr = "B";
|
config->total_expr = "B";
|
||||||
} OPT_END(basis);
|
} OPT_END(basis);
|
||||||
|
|
||||||
OPT_BEGIN(market, "V") {
|
OPT_BEGIN(market, "V") {
|
||||||
config.show_revalued = true;
|
config->show_revalued = true;
|
||||||
|
|
||||||
config.amount_expr = "v";
|
config->amount_expr = "v";
|
||||||
config.total_expr = "V";
|
config->total_expr = "V";
|
||||||
} OPT_END(market);
|
} OPT_END(market);
|
||||||
|
|
||||||
OPT_BEGIN(performance, "g") {
|
OPT_BEGIN(performance, "g") {
|
||||||
config.amount_expr = "P(a,m)-b"; // same as 'g', but priced now
|
config->amount_expr = "P(a,m)-b"; // same as 'g', but priced now
|
||||||
config.total_expr = "P(O,m)-B";
|
config->total_expr = "P(O,m)-B";
|
||||||
} OPT_END(performance);
|
} OPT_END(performance);
|
||||||
|
|
||||||
OPT_BEGIN(gain, "G") {
|
OPT_BEGIN(gain, "G") {
|
||||||
config.show_revalued =
|
config->show_revalued =
|
||||||
config.show_revalued_only = true;
|
config->show_revalued_only = true;
|
||||||
|
|
||||||
config.amount_expr = "a";
|
config->amount_expr = "a";
|
||||||
config.total_expr = "G";
|
config->total_expr = "G";
|
||||||
} OPT_END(gain);
|
} OPT_END(gain);
|
||||||
|
|
||||||
OPT_BEGIN(average, "A") {
|
OPT_BEGIN(average, "A") {
|
||||||
config.total_expr_template = "A#";
|
config->total_expr_template = "A#";
|
||||||
} OPT_END(average);
|
} OPT_END(average);
|
||||||
|
|
||||||
OPT_BEGIN(deviation, "D") {
|
OPT_BEGIN(deviation, "D") {
|
||||||
config.total_expr_template = "t-A#";
|
config->total_expr_template = "t-A#";
|
||||||
} OPT_END(deviation);
|
} OPT_END(deviation);
|
||||||
|
|
||||||
OPT_BEGIN(percentage, "%") {
|
OPT_BEGIN(percentage, "%") {
|
||||||
config.total_expr_template = "^#&{100.0%}*(#/^#)";
|
config->total_expr_template = "^#&{100.0%}*(#/^#)";
|
||||||
} OPT_END(percentage);
|
} OPT_END(percentage);
|
||||||
|
|
||||||
#ifdef USE_BOOST_PYTHON
|
#ifdef USE_BOOST_PYTHON
|
||||||
|
|
@ -1017,9 +1116,6 @@ void py_add_config_option_handlers()
|
||||||
add_other_option_handlers(config_options);
|
add_other_option_handlers(config_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_ledger_data_overloads,
|
|
||||||
parse_ledger_data, 1, 2)
|
|
||||||
|
|
||||||
void py_option_help()
|
void py_option_help()
|
||||||
{
|
{
|
||||||
option_help(std::cout);
|
option_help(std::cout);
|
||||||
|
|
@ -1082,10 +1178,9 @@ void export_config()
|
||||||
.def("process_options", py_process_options)
|
.def("process_options", py_process_options)
|
||||||
;
|
;
|
||||||
|
|
||||||
scope().attr("config") = ptr(&config);
|
scope().attr("config") = ptr(config);
|
||||||
|
|
||||||
def("option_help", py_option_help);
|
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);
|
def("add_config_option_handlers", py_add_config_option_handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
42
config.h
42
config.h
|
|
@ -1,12 +1,7 @@
|
||||||
#ifndef _CONFIG_H
|
#ifndef _CONFIG_H
|
||||||
#define _CONFIG_H
|
#define _CONFIG_H
|
||||||
|
|
||||||
#include "journal.h"
|
#include "ledger.h"
|
||||||
#include "option.h"
|
|
||||||
#include "valexpr.h"
|
|
||||||
#include "datetime.h"
|
|
||||||
#include "format.h"
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -14,8 +9,9 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
struct config_t
|
class config_t
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
// These options can all be set used text fields.
|
// These options can all be set used text fields.
|
||||||
|
|
||||||
strings_list price_settings;
|
strings_list price_settings;
|
||||||
|
|
@ -70,27 +66,43 @@ struct config_t
|
||||||
bool use_cache;
|
bool use_cache;
|
||||||
bool cache_dirty;
|
bool cache_dirty;
|
||||||
|
|
||||||
config_t();
|
config_t() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
config_t(const config_t&) {
|
config_t(const config_t&) {
|
||||||
assert(0);
|
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,
|
void process_options(const std::string& command,
|
||||||
strings_list::iterator arg,
|
strings_list::iterator arg,
|
||||||
strings_list::iterator args_end);
|
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;
|
extern std::list<option_t> config_options;
|
||||||
|
|
||||||
void option_help(std::ostream& out);
|
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 {
|
struct declared_option_handler : public option_handler {
|
||||||
declared_option_handler(const std::string& label,
|
declared_option_handler(const std::string& label,
|
||||||
const std::string& opt_chars) {
|
const std::string& opt_chars) {
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,8 @@ AC_CONFIG_HEADER([acconf.h])
|
||||||
# Checks for programs.
|
# Checks for programs.
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
AC_PROG_RANLIB
|
AC_PROG_LIBTOOL
|
||||||
#AC_PROG_LIBTOOL
|
AM_PROG_LIBTOOL
|
||||||
#AM_PROG_LIBTOOL
|
|
||||||
|
|
||||||
# check if UNIX pipes are available
|
# check if UNIX pipes are available
|
||||||
AC_CACHE_CHECK(
|
AC_CACHE_CHECK(
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ entry_t * derive_new_entry(journal_t& journal,
|
||||||
if (i == end) {
|
if (i == end) {
|
||||||
// If no argument were given but the payee, assume the user wants
|
// If no argument were given but the payee, assume the user wants
|
||||||
// to see the same transaction as last time.
|
// to see the same transaction as last time.
|
||||||
added->code = matching->code;
|
added->code = matching->code;
|
||||||
|
|
||||||
for (transactions_list::iterator j = matching->transactions.begin();
|
for (transactions_list::iterator j = matching->transactions.begin();
|
||||||
j != matching->transactions.end();
|
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 << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
out << " (\"" << xact.account->fullname() << "\" \""
|
out << " (\"" << xact_account(xact)->fullname() << "\" \""
|
||||||
<< xact.amount << "\"";
|
<< xact.amount << "\"";
|
||||||
|
|
||||||
switch (xact.state) {
|
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) {
|
if (details.xact) {
|
||||||
switch (details.xact->state) {
|
switch (details.xact->state) {
|
||||||
case transaction_t::CLEARED:
|
case transaction_t::CLEARED:
|
||||||
out << "* ";
|
out << "* ";
|
||||||
break;
|
break;
|
||||||
case transaction_t::PENDING:
|
case transaction_t::PENDING:
|
||||||
out << "! ";
|
out << "! ";
|
||||||
|
|
@ -547,8 +547,8 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
case element_t::ACCOUNT_FULLNAME:
|
case element_t::ACCOUNT_FULLNAME:
|
||||||
if (details.account) {
|
if (details.account) {
|
||||||
name += (elem->type == element_t::ACCOUNT_FULLNAME ?
|
name += (elem->type == element_t::ACCOUNT_FULLNAME ?
|
||||||
details.account->fullname() :
|
details.account->fullname() :
|
||||||
partial_account_name(*details.account));
|
partial_account_name(*details.account));
|
||||||
|
|
||||||
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
|
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
|
||||||
if (elem->max_width > 2)
|
if (elem->max_width > 2)
|
||||||
|
|
@ -685,7 +685,9 @@ void print_entry(std::ostream& out, const entry_t& entry)
|
||||||
formatter);
|
formatter);
|
||||||
formatter.flush();
|
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,
|
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()
|
void export_journal()
|
||||||
{
|
{
|
||||||
scope().attr("TRANSACTION_NORMAL") = TRANSACTION_NORMAL;
|
scope().attr("TRANSACTION_NORMAL") = TRANSACTION_NORMAL;
|
||||||
scope().attr("TRANSACTION_VIRTUAL") = TRANSACTION_VIRTUAL;
|
scope().attr("TRANSACTION_VIRTUAL") = TRANSACTION_VIRTUAL;
|
||||||
scope().attr("TRANSACTION_BALANCE") = TRANSACTION_BALANCE;
|
scope().attr("TRANSACTION_BALANCE") = TRANSACTION_BALANCE;
|
||||||
scope().attr("TRANSACTION_AUTO") = TRANSACTION_AUTO;
|
scope().attr("TRANSACTION_AUTO") = TRANSACTION_AUTO;
|
||||||
scope().attr("TRANSACTION_BULK_ALLOC") = TRANSACTION_BULK_ALLOC;
|
scope().attr("TRANSACTION_BULK_ALLOC") = TRANSACTION_BULK_ALLOC;
|
||||||
|
|
||||||
class_< transaction_t > ("Transaction")
|
class_< transaction_t > ("Transaction")
|
||||||
.def(init<account_t *, amount_t, optional<unsigned int, std::string> >())
|
.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)
|
(dolist (item items)
|
||||||
(let ((index 1))
|
(let ((index 1))
|
||||||
(dolist (xact (nthcdr 5 item))
|
(dolist (xact (nthcdr 5 item))
|
||||||
(let ((beg (point))
|
(let ((beg (point))
|
||||||
(where
|
(where
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(cons
|
(cons
|
||||||
|
|
@ -502,14 +502,14 @@ dropped."
|
||||||
account (cdr (ledger-current-entry-bounds)))
|
account (cdr (ledger-current-entry-bounds)))
|
||||||
(setq i (1+ i))))
|
(setq i (1+ i))))
|
||||||
(point-marker)))))))
|
(point-marker)))))))
|
||||||
(insert (format "%s %-30s %-25s %15s\n"
|
(insert (format "%s %-30s %-25s %15s\n"
|
||||||
(format-time-string "%m/%d" (nth 2 item))
|
(format-time-string "%m/%d" (nth 2 item))
|
||||||
(nth 4 item) (nth 0 xact) (nth 1 xact)))
|
(nth 4 item) (nth 0 xact) (nth 1 xact)))
|
||||||
(if (nth 2 xact)
|
(if (nth 2 xact)
|
||||||
|
(set-text-properties beg (1- (point))
|
||||||
|
(list 'face 'bold
|
||||||
|
'where where))
|
||||||
(set-text-properties beg (1- (point))
|
(set-text-properties beg (1- (point))
|
||||||
(list 'face 'bold
|
|
||||||
'where where))
|
|
||||||
(set-text-properties beg (1- (point))
|
|
||||||
(list 'where where))))
|
(list 'where where))))
|
||||||
(setq index (1+ index)))))
|
(setq index (1+ index)))))
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
|
|
|
||||||
12
ledger.h
12
ledger.h
|
|
@ -26,7 +26,6 @@
|
||||||
#include <reconcile.h>
|
#include <reconcile.h>
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
#include <option.h>
|
#include <option.h>
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <parser.h>
|
#include <parser.h>
|
||||||
#include <textual.h>
|
#include <textual.h>
|
||||||
|
|
@ -36,4 +35,15 @@
|
||||||
#include <qif.h>
|
#include <qif.h>
|
||||||
#include <ofx.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
|
#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 <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <memory>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
@ -27,218 +17,34 @@ using namespace ledger;
|
||||||
#include "fdstream.hpp"
|
#include "fdstream.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(DEBUG_LEVEL) || DEBUG_LEVEL <= RELEASE
|
#include "ledger.h"
|
||||||
|
#ifdef USE_BOOST_PYTHON
|
||||||
#define auto_ptr bogus_auto_ptr
|
#include "py_eval.h"
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
item_handler<transaction_t> *
|
using namespace ledger;
|
||||||
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);
|
namespace {
|
||||||
|
TIMER_DEF_(setup);
|
||||||
// format_transactions write each transaction received to the
|
TIMER_DEF_(parse);
|
||||||
// output stream.
|
TIMER_DEF_(process);
|
||||||
if (! (command == "b" || command == "E")) {
|
TIMER_DEF_(walk);
|
||||||
// truncate_entries cuts off a certain number of _entries_ from
|
TIMER_DEF_(cleanup);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_and_report(int argc, char * argv[], char * envp[])
|
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);
|
std::auto_ptr<journal_t> journal(new journal_t);
|
||||||
|
|
||||||
// Parse command-line arguments, and those set in the environment
|
// Parse command-line arguments, and those set in the environment
|
||||||
|
|
||||||
std::list<std::string> args;
|
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()) {
|
if (args.empty()) {
|
||||||
option_help(std::cerr);
|
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();
|
config.use_cache = config.data_file.empty() && config.price_db.empty();
|
||||||
DEBUG_PRINT("ledger.config.cache", "1. use_cache = " << config.use_cache);
|
DEBUG_PRINT("ledger.config.cache", "1. use_cache = " << config.use_cache);
|
||||||
|
|
||||||
process_environment(config_options, envp, "LEDGER_");
|
config.process_environment(envp, "LEDGER_");
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
// These are here for backwards compatability, but are deprecated.
|
// These are here for backwards compatability, but are deprecated.
|
||||||
|
|
||||||
if (const char * p = std::getenv("LEDGER"))
|
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"))
|
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"))
|
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"))
|
if (const char * p = std::getenv("PRICE_EXP"))
|
||||||
process_option(config_options, "price-exp", p);
|
config.process_option("price-exp", p);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char * p = std::getenv("HOME");
|
const char * p = std::getenv("HOME");
|
||||||
|
|
@ -311,38 +117,22 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
else
|
else
|
||||||
throw error(std::string("Unrecognized command '") + command + "'");
|
throw error(std::string("Unrecognized command '") + command + "'");
|
||||||
|
|
||||||
|
TIMER_STOP(setup);
|
||||||
|
|
||||||
// Parse initialization files, ledger data, price database, etc.
|
// Parse initialization files, ledger data, price database, etc.
|
||||||
|
|
||||||
std::auto_ptr<binary_parser_t> bin_parser(new binary_parser_t);
|
TIMER_START(parse);
|
||||||
#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);
|
|
||||||
|
|
||||||
register_parser(bin_parser.get());
|
if (parse_ledger_data(journal.get(), config) == 0)
|
||||||
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
throw error("Please specify ledger file using -f"
|
||||||
register_parser(xml_parser.get());
|
" or LEDGER_FILE environment variable.");
|
||||||
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());
|
|
||||||
|
|
||||||
parse_ledger_data(journal.get(), bin_parser.get(), text_parser.get()
|
TIMER_STOP(parse);
|
||||||
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
|
||||||
, xml_parser.get()
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
|
|
||||||
// process the command word and its following arguments
|
// process the command word and its following arguments
|
||||||
|
|
||||||
|
TIMER_START(process);
|
||||||
|
|
||||||
std::string first_arg;
|
std::string first_arg;
|
||||||
if (command == "w") {
|
if (command == "w") {
|
||||||
if (arg == args.end())
|
if (arg == args.end())
|
||||||
|
|
@ -461,8 +251,12 @@ def vmax(d, val):\n\
|
||||||
|
|
||||||
#endif // USE_BOOST_PYTHON
|
#endif // USE_BOOST_PYTHON
|
||||||
|
|
||||||
|
TIMER_STOP(process);
|
||||||
|
|
||||||
// Walk the entries based on the report type and the options
|
// Walk the entries based on the report type and the options
|
||||||
|
|
||||||
|
TIMER_START(walk);
|
||||||
|
|
||||||
item_handler<transaction_t> * formatter;
|
item_handler<transaction_t> * formatter;
|
||||||
std::list<item_handler<transaction_t> *> formatter_ptrs;
|
std::list<item_handler<transaction_t> *> formatter_ptrs;
|
||||||
|
|
||||||
|
|
@ -482,10 +276,11 @@ def vmax(d, val):\n\
|
||||||
formatter = new format_transactions(*out, *format);
|
formatter = new format_transactions(*out, *format);
|
||||||
|
|
||||||
if (command == "w") {
|
if (command == "w") {
|
||||||
write_textual_journal(*journal, first_arg, *formatter, *out);
|
write_textual_journal(*journal, first_arg, *formatter,
|
||||||
|
config.write_hdr_format, *out);
|
||||||
} else {
|
} else {
|
||||||
formatter = chain_xact_handlers(command, formatter, journal.get(),
|
formatter = config.chain_xact_handlers(command, formatter, journal.get(),
|
||||||
journal->master, formatter_ptrs);
|
journal->master, formatter_ptrs);
|
||||||
if (command == "e")
|
if (command == "e")
|
||||||
walk_transactions(new_entry->transactions, *formatter);
|
walk_transactions(new_entry->transactions, *formatter);
|
||||||
else if (command == "P" || command == "D")
|
else if (command == "P" || command == "D")
|
||||||
|
|
@ -521,8 +316,16 @@ def vmax(d, val):\n\
|
||||||
acct_formatter.flush();
|
acct_formatter.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TIMER_STOP(walk);
|
||||||
|
|
||||||
|
TIMER_START(cleanup);
|
||||||
|
|
||||||
#if DEBUG_LEVEL >= BETA
|
#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())
|
if (! config.output_file.empty())
|
||||||
delete out;
|
delete out;
|
||||||
|
|
@ -554,13 +357,13 @@ def vmax(d, val):\n\
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TIMER_STOP(cleanup);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char * argv[], char * envp[])
|
int main(int argc, char * argv[], char * envp[])
|
||||||
{
|
{
|
||||||
std::ios::sync_with_stdio(false);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return parse_and_report(argc, argv, envp);
|
return parse_and_report(argc, argv, envp);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
138
parser.cc
138
parser.cc
|
|
@ -1,5 +1,6 @@
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "journal.h"
|
#include "journal.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
@ -12,18 +13,31 @@ namespace ledger {
|
||||||
|
|
||||||
typedef std::list<parser_t *> parsers_list;
|
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)
|
bool register_parser(parser_t * parser)
|
||||||
{
|
{
|
||||||
parsers_list::iterator i;
|
parsers_list::iterator i;
|
||||||
for (i = parsers.begin(); i != parsers.end(); i++)
|
for (i = parsers->begin(); i != parsers->end(); i++)
|
||||||
if (*i == parser)
|
if (*i == parser)
|
||||||
break;
|
break;
|
||||||
if (i != parsers.end())
|
if (i != parsers->end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
parsers.push_back(parser);
|
parsers->push_back(parser);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -31,13 +45,13 @@ bool register_parser(parser_t * parser)
|
||||||
bool unregister_parser(parser_t * parser)
|
bool unregister_parser(parser_t * parser)
|
||||||
{
|
{
|
||||||
parsers_list::iterator i;
|
parsers_list::iterator i;
|
||||||
for (i = parsers.begin(); i != parsers.end(); i++)
|
for (i = parsers->begin(); i != parsers->end(); i++)
|
||||||
if (*i == parser)
|
if (*i == parser)
|
||||||
break;
|
break;
|
||||||
if (i == parsers.end())
|
if (i == parsers->end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
parsers.erase(i);
|
parsers->erase(i);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -50,8 +64,8 @@ unsigned int parse_journal(std::istream& in,
|
||||||
if (! master)
|
if (! master)
|
||||||
master = journal->master;
|
master = journal->master;
|
||||||
|
|
||||||
for (parsers_list::iterator i = parsers.begin();
|
for (parsers_list::iterator i = parsers->begin();
|
||||||
i != parsers.end();
|
i != parsers->end();
|
||||||
i++)
|
i++)
|
||||||
if ((*i)->test(in))
|
if ((*i)->test(in))
|
||||||
return (*i)->parse(in, journal, master, original_file);
|
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);
|
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
|
} // namespace ledger
|
||||||
|
|
||||||
#ifdef USE_BOOST_PYTHON
|
#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,
|
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_journal_file_overloads,
|
||||||
parse_journal_file, 2, 4)
|
parse_journal_file, 2, 4)
|
||||||
|
|
||||||
|
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_ledger_data_overloads,
|
||||||
|
parse_ledger_data, 1, 2)
|
||||||
|
|
||||||
void export_parser() {
|
void export_parser() {
|
||||||
class_< parser_t, parser_wrap, boost::noncopyable > ("Parser")
|
class_< parser_t, parser_wrap, boost::noncopyable > ("Parser")
|
||||||
;
|
;
|
||||||
|
|
@ -116,6 +233,9 @@ void export_parser() {
|
||||||
def("unregister_parser", unregister_parser);
|
def("unregister_parser", unregister_parser);
|
||||||
def("parse_journal", parse_journal, parse_journal_overloads());
|
def("parse_journal", parse_journal, parse_journal_overloads());
|
||||||
def("parse_journal_file", parse_journal_file, parse_journal_file_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
|
#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,
|
account_t * master = NULL,
|
||||||
const std::string * original_file = 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
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _PARSER_H
|
#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)
|
if (amount == note_str)
|
||||||
amount = NULL;
|
amount = NULL;
|
||||||
|
|
||||||
*note_str++ = '\0';
|
*note_str++ = '\0';
|
||||||
note_str = skip_ws(note_str);
|
note_str = skip_ws(note_str);
|
||||||
|
|
||||||
if (char * b = std::strchr(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");
|
throw parse_error(path, linenum, "Failed to parse date");
|
||||||
}
|
}
|
||||||
|
|
||||||
xact->note = skip_ws(note_str);
|
xact->note = skip_ws(note_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount) {
|
if (amount) {
|
||||||
price = std::strchr(amount, '@');
|
price = std::strchr(amount, '@');
|
||||||
if (price) {
|
if (price) {
|
||||||
if (price == amount)
|
if (price == amount)
|
||||||
throw parse_error(path, linenum, "Cost specified without amount");
|
throw parse_error(path, linenum, "Cost specified without amount");
|
||||||
|
|
||||||
*price++ = '\0';
|
*price++ = '\0';
|
||||||
if (*price == '@') {
|
if (*price == '@') {
|
||||||
per_unit = false;
|
per_unit = false;
|
||||||
price++;
|
price++;
|
||||||
}
|
}
|
||||||
price = skip_ws(price);
|
price = skip_ws(price);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +321,6 @@ bool parse_transactions(std::istream& in,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
TIMER_DEF(entry_finish, "finalizing entry");
|
|
||||||
TIMER_DEF(entry_xacts, "parsing transactions");
|
TIMER_DEF(entry_xacts, "parsing transactions");
|
||||||
TIMER_DEF(entry_details, "parsing entry details");
|
TIMER_DEF(entry_details, "parsing entry details");
|
||||||
TIMER_DEF(entry_date, "parsing entry date");
|
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,
|
void write_textual_journal(journal_t& journal, std::string path,
|
||||||
item_handler<transaction_t>& formatter,
|
item_handler<transaction_t>& formatter,
|
||||||
|
const std::string& write_hdr_format,
|
||||||
std::ostream& out)
|
std::ostream& out)
|
||||||
{
|
{
|
||||||
unsigned long index = 0;
|
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 pos = 0;
|
||||||
istream_pos_type jump_to;
|
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());
|
std::ifstream in(found.c_str());
|
||||||
|
|
||||||
while (! in.eof()) {
|
while (! in.eof()) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define _TEXTUAL_H
|
#define _TEXTUAL_H
|
||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
#include "format.h"
|
||||||
#include "walk.h"
|
#include "walk.h"
|
||||||
|
|
||||||
namespace ledger {
|
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,
|
void write_textual_journal(journal_t& journal, std::string path,
|
||||||
item_handler<transaction_t>& formatter,
|
item_handler<transaction_t>& formatter,
|
||||||
|
const std::string& write_hdr_format,
|
||||||
std::ostream& out);
|
std::ostream& out);
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
2
timing.h
2
timing.h
|
|
@ -41,10 +41,12 @@ class timing_t
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
#define TIMER_DEF(sym, cat) static timing_t sym(#sym, cat)
|
#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_START(sym) sym.start(__FILE__, __LINE__)
|
||||||
#define TIMER_STOP(sym) sym.stop()
|
#define TIMER_STOP(sym) sym.stop()
|
||||||
#else
|
#else
|
||||||
#define TIMER_DEF(sym, cat)
|
#define TIMER_DEF(sym, cat)
|
||||||
|
#define TIMER_DEF_(sym)
|
||||||
#define TIMER_START(sym)
|
#define TIMER_START(sym)
|
||||||
#define TIMER_STOP(sym)
|
#define TIMER_STOP(sym)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ std::auto_ptr<value_expr_t> total_expr;
|
||||||
|
|
||||||
std::time_t terminus = now;
|
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
|
void value_expr_t::compute(value_t& result, const details_t& details) const
|
||||||
{
|
{
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,7 @@ struct details_t
|
||||||
: entry(&_entry), xact(NULL), account(NULL) {
|
: entry(&_entry), xact(NULL), account(NULL) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
|
||||||
}
|
}
|
||||||
details_t(const transaction_t& _xact)
|
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 account_t& _account)
|
details_t(const account_t& _account)
|
||||||
: entry(NULL), xact(NULL), account(&_account) {
|
: entry(NULL), xact(NULL), account(&_account) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
|
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> amount_expr;
|
||||||
extern std::auto_ptr<value_expr_t> total_expr;
|
extern std::auto_ptr<value_expr_t> total_expr;
|
||||||
|
|
||||||
extern std::time_t terminus;
|
extern std::time_t terminus;
|
||||||
|
|
||||||
inline void compute_amount(value_t& result, const details_t& details) {
|
inline void compute_amount(value_t& result, const details_t& details) {
|
||||||
|
|
|
||||||
93
walk.cc
93
walk.cc
|
|
@ -7,12 +7,6 @@
|
||||||
|
|
||||||
namespace ledger {
|
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 <>
|
template <>
|
||||||
bool compare_items<transaction_t>::operator()(const transaction_t * left,
|
bool compare_items<transaction_t>::operator()(const transaction_t * left,
|
||||||
const transaction_t * right)
|
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)
|
transaction_xdata_t& transaction_xdata(const transaction_t& xact)
|
||||||
{
|
{
|
||||||
if (! xact.data) {
|
if (! xact.data)
|
||||||
transactions_xdata.push_back(transaction_xdata_t());
|
xact.data = new transaction_xdata_t();
|
||||||
xact.data = &transactions_xdata.back();
|
|
||||||
transactions_xdata_ptrs.push_back(&xact.data);
|
|
||||||
}
|
|
||||||
return *((transaction_xdata_t *) xact.data);
|
return *((transaction_xdata_t *) xact.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +100,10 @@ void truncate_entries::flush()
|
||||||
|
|
||||||
void set_account_value::operator()(transaction_t& xact)
|
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);
|
add_transaction_to(xact, xdata.value);
|
||||||
|
|
||||||
xdata.count++;
|
xdata.count++;
|
||||||
|
|
@ -370,7 +364,7 @@ void subtotal_transactions::operator()(transaction_t& xact)
|
||||||
if (! finish || std::difftime(xact.date(), finish) > 0)
|
if (! finish || std::difftime(xact.date(), finish) > 0)
|
||||||
finish = xact.date();
|
finish = xact.date();
|
||||||
|
|
||||||
account_t * acct = xact.account;
|
account_t * acct = xact_account(xact);
|
||||||
assert(acct);
|
assert(acct);
|
||||||
|
|
||||||
values_map::iterator i = values.find(acct->fullname());
|
values_map::iterator i = values.find(acct->fullname());
|
||||||
|
|
@ -387,9 +381,9 @@ void subtotal_transactions::operator()(transaction_t& xact)
|
||||||
// that contain only virtual transactions.
|
// that contain only virtual transactions.
|
||||||
|
|
||||||
if (! (xact.flags & TRANSACTION_VIRTUAL))
|
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))
|
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)
|
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 &&
|
if (std::difftime(begin, moment) < 0 &&
|
||||||
(! (*i).first.end || std::difftime(begin, (*i).first.end) < 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 "
|
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", begin);
|
||||||
DEBUG_PRINT_TIME("ledger.walk.budget", moment);
|
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);
|
xact_temps.push_back(xact);
|
||||||
transaction_t& temp = xact_temps.back();
|
transaction_t& temp = xact_temps.back();
|
||||||
temp.entry = &entry;
|
temp.entry = &entry;
|
||||||
temp.flags |= TRANSACTION_AUTO;
|
temp.flags |= TRANSACTION_AUTO | TRANSACTION_BULK_ALLOC;
|
||||||
temp.amount.negate();
|
temp.amount.negate();
|
||||||
temp.flags |= TRANSACTION_BULK_ALLOC;
|
|
||||||
entry.add_transaction(&temp);
|
entry.add_transaction(&temp);
|
||||||
|
|
||||||
begin = (*i).first.increment(begin);
|
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();
|
for (pending_xacts_list::iterator i = pending_xacts.begin();
|
||||||
i != pending_xacts.end();
|
i != pending_xacts.end();
|
||||||
i++)
|
i++)
|
||||||
for (account_t * acct = xact.account; acct; acct = acct->parent) {
|
for (account_t * acct = xact_account(xact);
|
||||||
if (acct == (*i).second->account) {
|
acct;
|
||||||
|
acct = acct->parent) {
|
||||||
|
if (acct == xact_account(*(*i).second)) {
|
||||||
xact_in_budget = true;
|
xact_in_budget = true;
|
||||||
|
|
||||||
// Report the transaction as if it had occurred in the parent
|
// Report the transaction as if it had occurred in the parent
|
||||||
// account. jww (2005-07-13): Note that this assignment will
|
// account.
|
||||||
// irrevocably change the underlying transaction.
|
if (xact_account(xact) != acct)
|
||||||
if (xact.account != acct)
|
transaction_xdata(xact).account = acct;
|
||||||
xact.account = acct;
|
|
||||||
goto handle;
|
goto handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -705,17 +698,6 @@ void forecast_transactions::flush()
|
||||||
item_handler<transaction_t>::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 <>
|
template <>
|
||||||
bool compare_items<account_t>::operator()(const account_t * left,
|
bool compare_items<account_t>::operator()(const account_t * left,
|
||||||
const account_t * right)
|
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)
|
account_xdata_t& account_xdata(const account_t& account)
|
||||||
{
|
{
|
||||||
if (! account.data) {
|
if (! account.data)
|
||||||
accounts_xdata.push_back(account_xdata_t());
|
account.data = new account_xdata_t();
|
||||||
account.data = &accounts_xdata.back();
|
|
||||||
accounts_xdata_ptrs.push_back(&account.data);
|
|
||||||
}
|
|
||||||
return *((account_xdata_t *) account.data);
|
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,
|
void walk_commodities(commodities_map& commodities,
|
||||||
item_handler<transaction_t>& handler)
|
item_handler<transaction_t>& handler)
|
||||||
{
|
{
|
||||||
|
|
@ -972,7 +940,6 @@ void export_walk()
|
||||||
|
|
||||||
def("transaction_has_xdata", transaction_has_xdata);
|
def("transaction_has_xdata", transaction_has_xdata);
|
||||||
def("transaction_xdata", transaction_xdata, return_internal_reference<1>());
|
def("transaction_xdata", transaction_xdata, return_internal_reference<1>());
|
||||||
def("clear_transactions_xdata", clear_transactions_xdata);
|
|
||||||
def("add_transaction_to", add_transaction_to);
|
def("add_transaction_to", add_transaction_to);
|
||||||
|
|
||||||
class_< xact_handler_t, item_handler_wrap<transaction_t> >
|
class_< xact_handler_t, item_handler_wrap<transaction_t> >
|
||||||
|
|
@ -991,6 +958,12 @@ void export_walk()
|
||||||
.def("__call__", &ignore_transactions::operator());
|
.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> >
|
class_< truncate_entries, bases<xact_handler_t> >
|
||||||
("TruncateEntries", init<xact_handler_t *, int, int>()
|
("TruncateEntries", init<xact_handler_t *, int, int>()
|
||||||
[with_custodian_and_ward<1, 2>()])
|
[with_custodian_and_ward<1, 2>()])
|
||||||
|
|
@ -1149,8 +1122,6 @@ void export_walk()
|
||||||
|
|
||||||
def("account_has_xdata", account_has_xdata);
|
def("account_has_xdata", account_has_xdata);
|
||||||
def("account_xdata", account_xdata, return_internal_reference<1>());
|
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> >
|
class_< account_handler_t, item_handler_wrap<account_t> >
|
||||||
("AccountHandler")
|
("AccountHandler")
|
||||||
|
|
@ -1162,6 +1133,12 @@ void export_walk()
|
||||||
&item_handler_wrap<account_t>::default_call)
|
&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("sum_accounts", sum_accounts);
|
||||||
def("walk_accounts", py_walk_accounts_1);
|
def("walk_accounts", py_walk_accounts_1);
|
||||||
def("walk_accounts", py_walk_accounts_2);
|
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 int index;
|
||||||
unsigned short dflags;
|
unsigned short dflags;
|
||||||
std::time_t date;
|
std::time_t date;
|
||||||
|
account_t * account;
|
||||||
void * ptr;
|
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) {
|
inline bool transaction_has_xdata(const transaction_t& xact) {
|
||||||
return xact.data != NULL;
|
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) {
|
inline transaction_xdata_t& transaction_xdata_(const transaction_t& xact) {
|
||||||
return *((transaction_xdata_t *) xact.data);
|
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);
|
transaction_xdata_t& transaction_xdata(const transaction_t& xact);
|
||||||
void add_transaction_to(const transaction_t& xact, value_t& value);
|
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,
|
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);
|
walk_entries(list.begin(), list.end(), handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_transactions_xdata();
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class ignore_transactions : public item_handler<transaction_t>
|
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) {}
|
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>
|
class truncate_entries : public item_handler<transaction_t>
|
||||||
{
|
{
|
||||||
int head_count;
|
int head_count;
|
||||||
|
|
@ -594,9 +613,6 @@ inline bool account_has_xdata(const account_t& account) {
|
||||||
return account.data != NULL;
|
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) {
|
inline account_xdata_t& account_xdata_(const account_t& account) {
|
||||||
return *((account_xdata_t *) account.data);
|
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);
|
void sum_accounts(account_t& account);
|
||||||
|
|
||||||
typedef std::deque<account_t *> accounts_deque;
|
typedef std::deque<account_t *> accounts_deque;
|
||||||
|
|
@ -619,18 +646,19 @@ void walk_accounts(account_t& account,
|
||||||
item_handler<account_t>& handler,
|
item_handler<account_t>& handler,
|
||||||
const std::string& sort_string);
|
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,
|
void walk_commodities(commodities_map& commodities,
|
||||||
item_handler<transaction_t>& handler);
|
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
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _WALK_H
|
#endif // _WALK_H
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue