summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xacprep1
-rw-r--r--doc/ledger.1298
-rw-r--r--src/account.cc13
-rw-r--r--src/account.h1
-rw-r--r--src/archive.cc20
-rw-r--r--src/archive.h6
-rw-r--r--src/chain.cc5
-rw-r--r--src/draft.h10
-rw-r--r--src/filters.cc11
-rw-r--r--src/filters.h34
-rw-r--r--src/global.cc174
-rw-r--r--src/global.h1
-rw-r--r--src/hooks.h81
-rw-r--r--src/item.h6
-rw-r--r--src/journal.cc29
-rw-r--r--src/journal.h13
-rw-r--r--src/post.h2
-rw-r--r--src/py_journal.cc143
-rw-r--r--src/py_post.cc6
-rw-r--r--src/py_xact.cc26
-rw-r--r--src/pyinterp.cc12
-rw-r--r--src/report.cc336
-rw-r--r--src/report.h49
-rw-r--r--src/session.cc6
-rw-r--r--src/session.h6
-rw-r--r--src/textual.cc13
-rw-r--r--src/utils.cc4
-rw-r--r--src/utils.h8
-rw-r--r--src/value.cc6
-rw-r--r--src/xact.cc66
-rw-r--r--src/xact.h74
-rw-r--r--test/baseline/opt-pricedb-format.test (renamed from test/baseline/opt-pricesdb-format.test)2
-rw-r--r--tools/Makefile.am4
33 files changed, 914 insertions, 552 deletions
diff --git a/acprep b/acprep
index 245f16a1..958d777e 100755
--- a/acprep
+++ b/acprep
@@ -764,6 +764,7 @@ class PrepareBuild(CommandLineApp):
self.CPPFLAGS.append('-D_GLIBCXX_FULLY_DYNAMIC_STRING=1')
self.configure_args.append('--disable-shared')
+ self.configure_args.append('--enable-doxygen')
self.options.use_glibcxx_debug = True
self.locate_my_libraries()
diff --git a/doc/ledger.1 b/doc/ledger.1
index 10fda0c5..6007aec3 100644
--- a/doc/ledger.1
+++ b/doc/ledger.1
@@ -1,4 +1,4 @@
-.Dd March 15, 2009
+.Dd November 12, 2009
.Dt ledger 1
.Sh NAME
.Nm ledger
@@ -9,45 +9,52 @@ ledger
.Op Ar options
.Op Ar arguments
.Sh DESCRIPTION
-Ledger is a command-line accounting tool providing the user access to the
-power of double-entry accounting. It is only a reporting tool, which means it
-never modifies your data files, nor can it be used to create or remove data.
+Ledger is a command-line accounting tool based on the power and completeness
+of double-entry accounting. It is only a reporting tool, which means it never
+modifies your data files, but it does offers a large selection of reports, and
+different ways to customize them to your liking.
.Pp
.Sh COMMANDS
-Ledger accepts several top-level commands, each of which is used to generate a
-different report. Most of them accept a
+Ledger accepts several top-level commands, each of which generates a different
+kind of basic report. Most of them accept a
.Ar report-query
-argument, in order to determine what to report. To understand what is
-accepted by
+argument, in order to determine what should be reported. To understand the
+syntax of a
.Ar report-query ,
see the section on
.Sx QUERIES .
-In its most basic form, simply specifying one or more strings will produce a
+In its most basic form, simply specifying one or more strings produces a
report for all accounts containing those strings.
.Pp
-If no command at all is given, Ledger enters a
+If no command is given, Ledger enters a
.Tn REPL ,
or command loop, allowing several commands to be executed against the same
dataset without reparsing.
.Pp
-The following is a summary of all the reporting commands accepted by Ledger:
+The following is a complete list of reporting commands accepted by Ledger:
.Pp
-.Bl -tag -width foo
-.It Nm balance Oo Ar query Oc
-Produce a balance report showing subtotals for matching leaf accounts, and
-aggregate totals for all the parents of those accounts. The most common
-options with this command are:
+.Bl -tag -width balance
+.It Nm balance Oo Ar report-query Oc
+Produces a balance report showing totals for all matching accounts, and
+aggregate totals for parents of those accounts. Options most commonly used
+with this command are:
.Pp
.Bl -tag -compact -width "--collapse (-n)"
+.It Fl \-basis Pq Fl B
+Report in terms of cost basis, not amount or value. This is the only form of
+report which is guaranteed to always balance to zero, when no
+.Ar report-query
+is specified.
.It Fl \-collapse Pq Fl n
-Only show totals in the top-most accounts.
+Only show totals for the top-most accounts.
.It Fl \-empty Pq Fl E
-Show matching accounts whose total happens to be zero.
+Also show accounts whose total is zero.
.It Fl \-flat
-Rather than displaying a hierarchical tree, flatten it to show only subtotals
-for accounts directly matching the query.
+Rather than display a hierarchical tree, flatten the report to show subtotals
+for only accounts matching
+.Ar report-query .
.It Fl \-no\-total
-Suppress the final total usually shown at the bottom of the report.
+Suppress the summary total shown at the bottom of the report (when not zero).
.El
.Pp
The synonyms
@@ -55,31 +62,198 @@ The synonyms
and
.Nm b
are also accepted.
-.It Nm csv Oo Ar query Oc
+.It Nm budget Oo Ar report-query Oc
+A special balance report which includes three extra columns: the amount
+budgeted during the reporting period, how spending differed from the budget,
+and the percentage of budget spent (exceeds 100% if you go over budget).
+Note that budgeting requires one or more
+.Do
+periodic transactions
+.Dc
+to be defined in your data file(s). See the manual for more information.
+.It Nm cleared Oo Ar report-query Oc
+A special balance report which adds two extra columns: the cleared balance for
+each account, and the date of the most recent cleared posting in that account.
+For this accounting to be meaningful, the cleared flag must be set on at least
+one posting. See the manual for more information.
+.It Nm csv Oo Ar report-query Oc
+Report of postings matching the
+.Ar report-query
+in CSV format (comma-separated values). Useful for exporting data to a
+spreadsheet for further analysis or charting.
+.It Nm draft Oo Ar draft-template Oc
+Generate and display a new, properly formatted Ledger transaction by comparing
+the
+.Ar draft-template
+to the transactions in your data file(s). For more information on draft
+templates and using this command to quickly create new transactions, see the
+section
+.Sx DRAFTS .
+.Pp
+The synonyms
+.Nm entry
+and
+.Nm xact
+are also accepted.
.It Nm emacs Oo Ar query Oc
+Outputs posting and transaction data in a format readily consumed by the Emacs
+editor, in a series of Lisp forms. This is used by the
+.Li ledger.el
+Emacs mode to process reporting data from Ledger.
+.Pp
The synonym
.Nm lisp
is also accepted.
-.It Nm equity Oo Ar query Oc
-.It Nm generate
-.It Nm prices Oo Ar query Oc
-.It Nm pricesdb Oo Ar query Oc
-.It Nm print Oo Ar query Oc
-.It Nm register Oo Ar query Oc
+.It Nm equity Oo Ar report-query Oc
+Prints a series of transactions that balance current totals for
+accounts matching the
+.Ar report-query
+in a special account called
+.Li Equity:Opening Balances .
+The purpose of this report is to close the books for a prior year, while using
+these equity transactions to carry forward those balances.
+.It Nm prices Oo Ar report-query Oc
+Reports prices for all commodities in postings matching the
+.Ar report-query .
+The prices are reported with the granularity of a single day.
+.It Nm pricesdb Oo Ar report-query Oc
+Reports prices for all commodities in postings matching the
+.Ar report-query .
+Prices are reported down to the second, using the same format as the
+.Li ~/.pricedb
+file.
+.It Nm print Oo Ar report-query Oc
+Prints out the full transactions of any matching postings using the same
+format as they would appear in a data file. This can be used to extract
+subsets from a Ledger file to transfer to other files.
+.It Nm push Oo Ar options Oc
+In the
+.Tn REPL ,
+this command pushes a set of command-line options, so that they will apply to
+all subsequent reports.
+.It Nm pop
+In the
+.Tn REPL ,
+pops any option settings that have been pushed.
+.It Nm register Oo Ar report-query Oc
+List all postings matching the
+.Ar report-query .
+This is one of the most common commands, and can be used to provide a variety
+of useful reports. Options most commonly used
+with this command are:
+.Pp
+.Bl -tag -compact -width "--collapse (-n)"
+.It Fl \-average Pq Fl A
+Show the running average, rather than a running total.
+.It Fl \-current Pq Fl c
+Don't show postings beyond the present day.
+.It Fl \-exchange Ar commodity Pq Fl X
+Render all values in the given
+.Ar commodity ,
+if a price conversion rate can be determined. Rates are always displayed
+relative to the date of the posting they are calculated for. This means a
+.Nm register
+report is a historical value report. For current values, it may be preferable
+to use the
+.Nm balance
+report.
+.It Fl \-gain Pq Fl G
+Show any gains (or losses) in commodity values over time.
+.It Fl \-head Ar number
+Only show the top
+.Ar number
+postings.
+.It Fl \-invert
+Invert the value of amounts shown.
+.It Fl \-market Pq Fl V
+Show current market values for all amounts. This is determined in a somewhat
+magical fashion. It is probably more straightforward to use
+.Fl \-exchange Pq Fl X .
+.It Fl \-period Ar time-period Pq Fl p
+Show postings only for the given
+.Ar time-period .
+.It Fl \-related Pq Fl r
+Show postings that are related to those that would have been shown. It has
+the effect of displaying the
+.Do
+other side
+.Dc
+of the values.
+.It Fl \-sort Ar value-expression Pq Fl S
+Sort postings by evaluating the given
+.Ar value-expression .
+Note that a comma-separated list of expressions is allowed, in which case each
+sorting term is used in order to determine the final ordering. For example,
+to search by date and then amount, one would use:
+.Li -S 'date, amount' .
+.It Fl \-tail Ar number
+Only show the last
+.Ar number
+postings.
+.It Fl \-uncleared Pq Fl U
+Only show uncleared (i.e., recent) postings.
+.El
+.Pp
+There are also several grouping options that can be useful:
+.Pp
+.Bl -tag -compact -width "--collapse (-n)"
+.It Fl \-by-payee Pq Fl P
+Group postings by common payee names.
+.It Fl \-daily Pq Fl D
+Group postings by day.
+.It Fl \-weekly Pq Fl W
+Group postings by week (starting on Sundays).
+.It Fl \-start-of-week Ar day-name
+Set the start of each grouped way to the given
+.Ar day-name .
+.It Fl \-monthly Pq Fl M
+Group postings by month.
+.It Fl \-quarterly
+Group postings by fiscal quarter.
+.It Fl \-yearly Pq Fl Y
+Group postings by year.
+.It Fl \-dow
+Group postings by the day of the week on which they took place.
+.It Fl \-subtotal Pq Fl s
+Group all postings together. This is very similar to the totals shown by the
+.Nm balance
+report.
+.El
+.Pp
The synonyms
.Nm reg
and
.Nm r
are also accepted.
-.It Nm reload
-Used solely by the
-.It Nm xact Oo Ar date Oc
-The synonym
-.Nm entry
-is also accepted.
-.Tn REPL ,
-and causes an immediate reloading of all journal files in the session.
-.It Nm stats Oo Ar query Oc
+.It Nm server
+This command requires that Python support be active. If so, it starts up an
+HTTP server listening for requests on port 9000. This provides an alternate
+interface to creating and viewing reports. Note that this is very much a
+work-in-progress, and will not be fully functional until a later version.
+.It Nm stats Oo Ar report-query Oc
+Provides summary information about all the postings matching
+.Ar report-query .
+It provides information such as:
+.Bl -bullet -offset indent -compact
+.It
+Time range of all matching postings
+.It
+Unique payees
+.It
+Unique accounts
+.It
+Postings total
+.It
+Uncleared postings
+.It
+Days since last posting
+.It
+More...
+.El
+.It Nm xml Oo Ar report-query Oc
+Outputs data relating to the current report in XML format. It includes all
+accounts and commodities involved in the report, plus the postings and the
+transactions they are contained in. See the manual for more information.
.El
.Pp
.Sh OPTIONS
@@ -349,6 +523,56 @@ for example:
.It Nm xact
.El
.Pp
+.Sh DRAFTS
+.Pp
+.Sh FORMATS
+.Pp
+.Sh DEBUG COMMANDS
+In addition to the regular reporting commands, Ledger also accepts several
+debug commands:
+.Bl -tag -width balance
+.It Nm args Oo Ar report-query Oc
+Accepts a
+.Ar report-query
+as its argument and displays it back to the user along with a complete
+analysis of how Ledger interpreted it. Useful if you want to understand how
+report queries are translated into value expressions.
+.It Nm eval Oo Ar value-expression Oc
+Evaluates the given
+.Ar value-expression
+and prints the result. For more on value expressions, see the section
+.Sx EXPRESSIONS .
+.It Nm format Oo Ar format-string Oc
+Accepts a
+.Ar format-string
+and displays an analysis of how it was parsed, and what it would look like
+applied to a sample transaction. For more on format strings, see the section
+.Sx FORMATS .
+.It Nm generate
+Generates 50 randomly composed yet valid Ledger transactions.
+.It Nm parse Oo Ar value-expression Oc
+Parses the given
+.Ar value-expression
+and display an analysis of the expression tree and its evaluated value. For
+more on value expressions, see the section
+.Sx EXPRESSIONS .
+.It Nm python Oo Ar file Oc
+Invokes a Python interpreter to read the given
+.Ar file .
+What is special about this is that the ledger module is builtin, not read from
+disk, so it doesn't require Ledger to be installed anywhere, or the shared
+library variants to be built.
+.It Nm reload
+Used only in the
+.Tn REPL ,
+it causes an immediate reloading of all data files for the current session.
+.It Nm template Oo Ar draft-template Oc
+Accepts a
+.Ar draft-template
+and displays information about how it was parsed. See the section on
+.Sx DRAFTS .
+.El
+.Pp
.Sh SEE ALSO
.Xr beancount 1,
.Xr hledger 1
diff --git a/src/account.cc b/src/account.cc
index 5cc7e070..da43745a 100644
--- a/src/account.cc
+++ b/src/account.cc
@@ -327,16 +327,25 @@ bool account_t::valid() const
return true;
}
+bool account_t::children_with_xdata() const
+{
+ foreach (const accounts_map::value_type& pair, accounts)
+ if (pair.second->has_xdata() ||
+ pair.second->children_with_xdata())
+ return true;
+
+ return false;
+}
+
std::size_t account_t::children_with_flags(xdata_t::flags_t flags) const
{
std::size_t count = 0;
bool grandchildren_visited = false;
- foreach (const accounts_map::value_type& pair, accounts) {
+ foreach (const accounts_map::value_type& pair, accounts)
if (pair.second->has_xflags(flags) ||
pair.second->children_with_flags(flags))
count++;
- }
// Although no immediately children were visited, if any progeny at all were
// visited, it counts as one.
diff --git a/src/account.h b/src/account.h
index 0ac1aeb6..1e56fe23 100644
--- a/src/account.h
+++ b/src/account.h
@@ -241,6 +241,7 @@ public:
bool has_xflags(xdata_t::flags_t flags) const {
return xdata_ && xdata_->has_flags(flags);
}
+ bool children_with_xdata() const;
std::size_t children_with_flags(xdata_t::flags_t flags) const;
#if defined(HAVE_BOOST_SERIALIZATION)
diff --git a/src/archive.cc b/src/archive.cc
index 5ea6cd8e..d36712ca 100644
--- a/src/archive.cc
+++ b/src/archive.cc
@@ -201,23 +201,23 @@ bool archive_t::should_load(const std::list<path>& data_files)
return true;
}
-bool archive_t::should_save(shared_ptr<journal_t> journal)
+bool archive_t::should_save(journal_t& journal)
{
std::list<path> data_files;
DEBUG("archive.journal", "Should the archive be saved?");
- if (journal->was_loaded) {
+ if (journal.was_loaded) {
DEBUG("archive.journal", "No, it's one we loaded before");
return false;
}
- if (journal->sources.empty()) {
+ if (journal.sources.empty()) {
DEBUG("archive.journal", "No, there were no sources!");
return false;
}
- foreach (const journal_t::fileinfo_t& i, journal->sources) {
+ foreach (const journal_t::fileinfo_t& i, journal.sources) {
if (i.from_stream) {
DEBUG("archive.journal", "No, one source was from a stream");
return false;
@@ -241,14 +241,14 @@ bool archive_t::should_save(shared_ptr<journal_t> journal)
return true;
}
-void archive_t::save(shared_ptr<journal_t> journal)
+void archive_t::save(journal_t& journal)
{
INFO_START(archive, "Saved journal file cache");
ofstream stream(file, std::ios::binary);
write_header_bits(stream);
- sources = journal->sources;
+ sources = journal.sources;
#if defined(DEBUG_ON)
foreach (const journal_t::fileinfo_t& i, sources)
@@ -263,12 +263,12 @@ void archive_t::save(shared_ptr<journal_t> journal)
DEBUG("archive.journal",
"Archiving journal with " << sources.size() << " sources");
- oa << *journal;
+ oa << journal;
INFO_FINISH(archive);
}
-bool archive_t::load(shared_ptr<journal_t> journal)
+bool archive_t::load(journal_t& journal)
{
INFO_START(archive, "Read cached journal file");
@@ -282,8 +282,8 @@ bool archive_t::load(shared_ptr<journal_t> journal)
archive_t temp;
iarchive >> temp;
- iarchive >> *journal.get();
- journal->was_loaded = true;
+ iarchive >> journal;
+ journal.was_loaded = true;
INFO_FINISH(archive);
diff --git a/src/archive.h b/src/archive.h
index 60ead5a9..03fc970a 100644
--- a/src/archive.h
+++ b/src/archive.h
@@ -69,10 +69,10 @@ public:
bool read_header();
bool should_load(const std::list<path>& data_files);
- bool should_save(shared_ptr<journal_t> journal);
+ bool should_save(journal_t& journal);
- void save(shared_ptr<journal_t> journal);
- bool load(shared_ptr<journal_t> journal);
+ void save(journal_t& journal);
+ bool load(journal_t& journal);
#if defined(HAVE_BOOST_SERIALIZATION)
private:
diff --git a/src/chain.cc b/src/chain.cc
index 5839bd9e..55ef467b 100644
--- a/src/chain.cc
+++ b/src/chain.cc
@@ -159,6 +159,11 @@ post_handler_ptr chain_post_handlers(report_t& report,
handler.reset(new sort_posts(handler, "date"));
}
+ if (report.HANDLED(date_))
+ handler.reset(new transfer_details(handler, transfer_details::SET_DATE,
+ report.session.journal->master,
+ report.HANDLER(date_).str(),
+ report));
if (report.HANDLED(account_))
handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT,
report.session.journal->master,
diff --git a/src/draft.h b/src/draft.h
index faefa67b..277b4ff8 100644
--- a/src/draft.h
+++ b/src/draft.h
@@ -30,17 +30,17 @@
*/
/**
- * @addtogroup derive
+ * @addtogroup expr
*/
/**
- * @file derive.h
+ * @file draft.h
* @author John Wiegley
*
* @ingroup report
*/
-#ifndef _DERIVE_H
-#define _DERIVE_H
+#ifndef _DRAFT_H
+#define _DRAFT_H
#include "exprbase.h"
#include "value.h"
@@ -110,4 +110,4 @@ value_t template_command(call_scope_t& args);
} // namespace ledger
-#endif // _DERIVE_H
+#endif // _DRAFT_H
diff --git a/src/filters.cc b/src/filters.cc
index 4b54a0cd..8c5d099e 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -706,22 +706,27 @@ void transfer_details::operator()(post_t& post)
temp.set_state(post.state());
bind_scope_t bound_scope(scope, temp);
+ value_t substitute(expr.calc(bound_scope));
switch (which_element) {
- case SET_PAYEE:
- xact.payee = expr.calc(bound_scope).to_string();
+ case SET_DATE:
+ xact.set_date(substitute.to_date());
break;
case SET_ACCOUNT: {
std::list<string> account_names;
temp.account->remove_post(&temp);
- split_string(expr.calc(bound_scope).to_string(), ':', account_names);
+ split_string(substitute.to_string(), ':', account_names);
temp.account = create_temp_account_from_path(account_names, temps,
xact.journal->master);
temp.account->add_post(&temp);
break;
}
+ case SET_PAYEE:
+ xact.payee = substitute.to_string();
+ break;
+
default:
assert(false);
break;
diff --git a/src/filters.h b/src/filters.h
index ba3692ac..80bbe5b4 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -61,6 +61,35 @@ public:
virtual void operator()(post_t&) {}
};
+class collect_posts : public item_handler<post_t>
+{
+public:
+ std::vector<post_t *> posts;
+
+ collect_posts() : item_handler<post_t>() {
+ TRACE_CTOR(collect_posts, "");
+ }
+ virtual ~collect_posts() {
+ TRACE_DTOR(collect_posts);
+ }
+
+ std::size_t length() const {
+ return posts.size();
+ }
+
+ std::vector<post_t *>::iterator begin() {
+ return posts.begin();
+ }
+ std::vector<post_t *>::iterator end() {
+ return posts.end();
+ }
+
+ virtual void flush() {}
+ virtual void operator()(post_t& post) {
+ posts.push_back(&post);
+ }
+};
+
class posts_iterator;
class pass_down_posts : public item_handler<post_t>
@@ -550,8 +579,9 @@ class transfer_details : public item_handler<post_t>
public:
enum element_t {
- SET_PAYEE,
- SET_ACCOUNT
+ SET_DATE,
+ SET_ACCOUNT,
+ SET_PAYEE
} which_element;
transfer_details(post_handler_ptr handler,
diff --git a/src/global.cc b/src/global.cc
index 0c8cedd8..e120e5d5 100644
--- a/src/global.cc
+++ b/src/global.cc
@@ -194,7 +194,7 @@ void global_scope_t::execute_command(strings_list args, bool at_repl)
if (! at_repl)
session().read_journal_files();
- normalize_report_options(verb);
+ report().normalize_options(verb);
if (! bool(command = look_for_command(bound_scope, verb)))
throw_(std::logic_error, _("Unrecognized command '%1'") << verb);
@@ -416,178 +416,6 @@ expr_t::func_t global_scope_t::look_for_command(scope_t& scope,
return expr_t::func_t();
}
-void global_scope_t::normalize_report_options(const string& verb)
-{
- // Patch up some of the reporting options based on what kind of
- // command it was.
-
- report_t& rep(report());
-
-#ifdef HAVE_ISATTY
- if (! rep.HANDLED(force_color)) {
- if (! rep.HANDLED(no_color) && isatty(STDOUT_FILENO))
- rep.HANDLER(color).on_only(string("?normalize"));
- if (rep.HANDLED(color) && ! isatty(STDOUT_FILENO))
- rep.HANDLER(color).off();
- }
- if (! rep.HANDLED(force_pager)) {
- if (rep.HANDLED(pager_) && ! isatty(STDOUT_FILENO))
- rep.HANDLER(pager_).off();
- }
-#endif
-
- item_t::use_effective_date = (rep.HANDLED(effective) &&
- ! rep.HANDLED(actual_dates));
-
- rep.session.journal->commodity_pool->keep_base = rep.HANDLED(base);
- rep.session.journal->commodity_pool->get_quotes = rep.session.HANDLED(download);
-
- if (rep.session.HANDLED(price_exp_))
- rep.session.journal->commodity_pool->quote_leeway =
- rep.session.HANDLER(price_exp_).value.as_long();
-
- if (rep.session.HANDLED(price_db_))
- rep.session.journal->commodity_pool->price_db =
- rep.session.HANDLER(price_db_).str();
- else
- rep.session.journal->commodity_pool->price_db = none;
-
- if (rep.HANDLED(date_format_))
- set_date_format(rep.HANDLER(date_format_).str().c_str());
- if (rep.HANDLED(datetime_format_))
- set_datetime_format(rep.HANDLER(datetime_format_).str().c_str());
- if (rep.HANDLED(start_of_week_)) {
- if (optional<date_time::weekdays> weekday =
- string_to_day_of_week(rep.HANDLER(start_of_week_).str()))
- start_of_week = *weekday;
- }
-
- if (verb == "print" || verb == "xact" || verb == "dump") {
- rep.HANDLER(related).on_only(string("?normalize"));
- rep.HANDLER(related_all).on_only(string("?normalize"));
- }
- else if (verb == "equity") {
- rep.HANDLER(equity).on_only(string("?normalize"));
- }
-
- if (verb == "print")
- rep.HANDLER(limit_).on(string("?normalize"), "actual");
-
- if (! rep.HANDLED(empty))
- rep.HANDLER(display_).on(string("?normalize"), "amount|(!post&total)");
-
- if (verb[0] != 'b' && verb[0] != 'r')
- rep.HANDLER(base).on_only(string("?normalize"));
-
- // If a time period was specified with -p, check whether it also gave a
- // begin and/or end to the report period (though these can be overridden
- // using -b or -e). Then, if no _duration_ was specified (such as monthly),
- // then ignore the period since the begin/end are the only interesting
- // details.
- if (rep.HANDLED(period_)) {
- if (! rep.HANDLED(sort_all_))
- rep.HANDLER(sort_xacts_).on_only(string("?normalize"));
-
- date_interval_t interval(rep.HANDLER(period_).str());
-
- if (! rep.HANDLED(begin_) && interval.start) {
- string predicate =
- "date>=[" + to_iso_extended_string(*interval.start) + "]";
- rep.HANDLER(limit_).on(string("?normalize"), predicate);
- }
- if (! rep.HANDLED(end_) && interval.end) {
- string predicate =
- "date<[" + to_iso_extended_string(*interval.end) + "]";
- rep.HANDLER(limit_).on(string("?normalize"), predicate);
- }
-
- if (! interval.duration)
- rep.HANDLER(period_).off();
- }
-
- // If -j or -J were specified, set the appropriate format string now so as
- // to avoid option ordering issues were we to have done it during the
- // initial parsing of the options.
- if (rep.HANDLED(amount_data)) {
- rep.HANDLER(format_)
- .on_with(string("?normalize"), rep.HANDLER(plot_amount_format_).value);
- }
- else if (rep.HANDLED(total_data)) {
- rep.HANDLER(format_)
- .on_with(string("?normalize"), rep.HANDLER(plot_total_format_).value);
- }
-
- // If the --exchange (-X) option was used, parse out any final price
- // settings that may be there.
- if (rep.HANDLED(exchange_) &&
- rep.HANDLER(exchange_).str().find('=') != string::npos) {
- value_t(0L).exchange_commodities(rep.HANDLER(exchange_).str(), true,
- rep.terminus);
- }
-
- long cols = 0;
- if (rep.HANDLED(columns_))
- cols = rep.HANDLER(columns_).value.to_long();
- else if (const char * columns = std::getenv("COLUMNS"))
- cols = lexical_cast<long>(columns);
- else
- cols = 80L;
-
- if (cols > 0) {
- DEBUG("auto.columns", "cols = " << cols);
-
- if (! rep.HANDLER(date_width_).specified)
- rep.HANDLER(date_width_)
- .on_with(none, static_cast<long>(format_date(CURRENT_DATE(),
- FMT_PRINTED).length()));
-
- long date_width = rep.HANDLER(date_width_).value.to_long();
- long payee_width = (rep.HANDLER(payee_width_).specified ?
- rep.HANDLER(payee_width_).value.to_long() :
- int(double(cols) * 0.263157));
- long account_width = (rep.HANDLER(account_width_).specified ?
- rep.HANDLER(account_width_).value.to_long() :
- int(double(cols) * 0.302631));
- long amount_width = (rep.HANDLER(amount_width_).specified ?
- rep.HANDLER(amount_width_).value.to_long() :
- int(double(cols) * 0.157894));
- long total_width = (rep.HANDLER(total_width_).specified ?
- rep.HANDLER(total_width_).value.to_long() :
- amount_width);
-
- DEBUG("auto.columns", "date_width = " << date_width);
- DEBUG("auto.columns", "payee_width = " << payee_width);
- DEBUG("auto.columns", "account_width = " << account_width);
- DEBUG("auto.columns", "amount_width = " << amount_width);
- DEBUG("auto.columns", "total_width = " << total_width);
-
- if (! rep.HANDLER(date_width_).specified &&
- ! rep.HANDLER(payee_width_).specified &&
- ! rep.HANDLER(account_width_).specified &&
- ! rep.HANDLER(amount_width_).specified &&
- ! rep.HANDLER(total_width_).specified) {
- long total = (4 /* the spaces between */ + date_width + payee_width +
- account_width + amount_width + total_width);
- if (total > cols) {
- DEBUG("auto.columns", "adjusting account down");
- account_width -= total - cols;
- DEBUG("auto.columns", "account_width now = " << account_width);
- }
- }
-
- if (! rep.HANDLER(date_width_).specified)
- rep.HANDLER(date_width_).on_with(string("?normalize"), date_width);
- if (! rep.HANDLER(payee_width_).specified)
- rep.HANDLER(payee_width_).on_with(string("?normalize"), payee_width);
- if (! rep.HANDLER(account_width_).specified)
- rep.HANDLER(account_width_).on_with(string("?normalize"), account_width);
- if (! rep.HANDLER(amount_width_).specified)
- rep.HANDLER(amount_width_).on_with(string("?normalize"), amount_width);
- if (! rep.HANDLER(total_width_).specified)
- rep.HANDLER(total_width_).on_with(string("?normalize"), total_width);
- }
-}
-
void global_scope_t::visit_man_page() const
{
int pid = fork();
diff --git a/src/global.h b/src/global.h
index f7b87973..ab3afed4 100644
--- a/src/global.h
+++ b/src/global.h
@@ -62,7 +62,6 @@ public:
void normalize_session_options();
expr_t::func_t look_for_precommand(scope_t& scope, const string& verb);
expr_t::func_t look_for_command(scope_t& scope, const string& verb);
- void normalize_report_options(const string& verb);
char * prompt_string();
diff --git a/src/hooks.h b/src/hooks.h
deleted file mode 100644
index 20c7750c..00000000
--- a/src/hooks.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2003-2009, John Wiegley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of New Artisans LLC nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @addtogroup util
- */
-
-/**
- * @file hooks.h
- * @author John Wiegley
- *
- * @ingroup util
- */
-#ifndef _HOOKS_H
-#define _HOOKS_H
-
-template <typename T, typename Data>
-class hooks_t : public boost::noncopyable
-{
-public:
- typedef boost::function<bool (Data&, bool)> function_t;
-
-protected:
- std::list<T *> list;
-
-public:
- hooks_t() {
- TRACE_CTOR(hooks_t, "");
- }
- ~hooks_t() throw() {
- TRACE_DTOR(hooks_t);
- }
-
- void add_hook(T * func, const bool prepend = false) {
- if (prepend)
- list.push_front(func);
- else
- list.push_back(func);
- }
-
- void remove_hook(T * func) {
- list.remove(func);
- }
-
- bool run_hooks(Data& item) {
- foreach (T * func, list)
- if (! (*func)(item))
- return false;
- return true;
- }
-};
-
-#endif // _HOOKS_H
diff --git a/src/item.h b/src/item.h
index 84385eb7..13332f0b 100644
--- a/src/item.h
+++ b/src/item.h
@@ -172,6 +172,12 @@ public:
virtual optional<date_t> effective_date() const {
return _date_eff;
}
+ virtual void set_date(const date_t& date) {
+ if (use_effective_date)
+ _date_eff = date;
+ else
+ _date = date;
+ }
void set_state(state_t new_state) {
_state = new_state;
diff --git a/src/journal.cc b/src/journal.cc
index b7ad9a23..2366ce30 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -126,16 +126,23 @@ bool journal_t::add_xact(xact_t * xact)
{
xact->journal = this;
- if (! xact->finalize() || ! xact_finalize_hooks.run_hooks(*xact)) {
+ if (! xact->finalize()) {
xact->journal = NULL;
return false;
}
+ extend_xact(xact);
xacts.push_back(xact);
return true;
}
+void journal_t::extend_xact(xact_base_t * xact)
+{
+ foreach (auto_xact_t * auto_xact, auto_xacts)
+ auto_xact->extend_xact(*xact);
+}
+
bool journal_t::remove_xact(xact_t * xact)
{
bool found = false;
@@ -204,6 +211,26 @@ std::size_t journal_t::read(const path& pathname,
return count;
}
+bool journal_t::has_xdata()
+{
+ foreach (xact_t * xact, xacts)
+ if (xact->has_xdata())
+ return true;
+
+ foreach (auto_xact_t * xact, auto_xacts)
+ if (xact->has_xdata())
+ return true;
+
+ foreach (period_xact_t * xact, period_xacts)
+ if (xact->has_xdata())
+ return true;
+
+ if (master->has_xdata() || master->children_with_xdata())
+ return true;
+
+ return false;
+}
+
void journal_t::clear_xdata()
{
foreach (xact_t * xact, xacts)
diff --git a/src/journal.h b/src/journal.h
index 78269348..f7124736 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -43,15 +43,14 @@
#define _JOURNAL_H
#include "utils.h"
-#include "hooks.h"
#include "times.h"
namespace ledger {
class commodity_pool_t;
+class xact_base_t;
class xact_t;
class auto_xact_t;
-class xact_finalizer_t;
class period_xact_t;
class account_t;
class scope_t;
@@ -114,7 +113,6 @@ public:
bool was_loaded;
shared_ptr<commodity_pool_t> commodity_pool;
- hooks_t<xact_finalizer_t, xact_t> xact_finalize_hooks;
journal_t();
journal_t(const path& pathname);
@@ -138,6 +136,7 @@ public:
account_t * find_account_re(const string& regexp);
bool add_xact(xact_t * xact);
+ void extend_xact(xact_base_t * xact);
bool remove_xact(xact_t * xact);
xacts_list::iterator xacts_begin() {
@@ -159,13 +158,6 @@ public:
return period_xacts.end();
}
- void add_xact_finalizer(xact_finalizer_t * finalizer) {
- xact_finalize_hooks.add_hook(finalizer);
- }
- void remove_xact_finalizer(xact_finalizer_t * finalizer) {
- xact_finalize_hooks.remove_hook(finalizer);
- }
-
std::size_t read(std::istream& in,
const path& pathname,
account_t * master = NULL,
@@ -180,6 +172,7 @@ public:
const path * original_file = NULL,
bool strict = false);
+ bool has_xdata();
void clear_xdata();
bool valid() const;
diff --git a/src/post.h b/src/post.h
index d9e50580..22f8b961 100644
--- a/src/post.h
+++ b/src/post.h
@@ -92,7 +92,7 @@ public:
{
TRACE_CTOR(post_t, "copy");
}
- ~post_t() {
+ virtual ~post_t() {
TRACE_DTOR(post_t);
}
diff --git a/src/py_journal.cc b/src/py_journal.cc
index 88447b92..bea14d66 100644
--- a/src/py_journal.cc
+++ b/src/py_journal.cc
@@ -33,9 +33,14 @@
#include "pyinterp.h"
#include "pyutils.h"
-#include "hooks.h"
#include "journal.h"
#include "xact.h"
+#include "post.h"
+#include "chain.h"
+#include "filters.h"
+#include "iterators.h"
+#include "scope.h"
+#include "report.h"
namespace ledger {
@@ -130,51 +135,113 @@ namespace {
return journal.find_account(name, auto_create);
}
- struct py_xact_finalizer_t : public xact_finalizer_t {
- object pyobj;
- py_xact_finalizer_t() {}
- py_xact_finalizer_t(object obj) : pyobj(obj) {}
- py_xact_finalizer_t(const py_xact_finalizer_t& other)
- : pyobj(other.pyobj) {}
- virtual bool operator()(xact_t& xact) {
- return call<bool>(pyobj.ptr(), xact);
- }
- };
-
- std::list<py_xact_finalizer_t> py_finalizers;
-
- void py_add_xact_finalizer(journal_t& journal, object x)
+ std::size_t py_read(journal_t& journal, const string& pathname)
{
- py_finalizers.push_back(py_xact_finalizer_t(x));
- journal.add_xact_finalizer(&py_finalizers.back());
+ return journal.read(pathname);
}
- void py_remove_xact_finalizer(journal_t& journal, object x)
+ struct collector_wrapper
{
- for (std::list<py_xact_finalizer_t>::iterator i = py_finalizers.begin();
- i != py_finalizers.end();
- i++)
- if ((*i).pyobj == x) {
- journal.remove_xact_finalizer(&(*i));
- py_finalizers.erase(i);
- return;
- }
- }
+ journal_t& journal;
+ report_t report;
+ collect_posts * posts_collector;
+ post_handler_ptr chain;
+
+ collector_wrapper(journal_t& _journal, report_t& base)
+ : journal(_journal), report(base),
+ posts_collector(new collect_posts) {}
+ ~collector_wrapper() {
+ journal.clear_xdata();
+ }
- void py_run_xact_finalizers(journal_t& journal, xact_t& xact)
+ std::size_t length() const {
+ return posts_collector->length();
+ }
+
+ std::vector<post_t *>::iterator begin() {
+ return posts_collector->begin();
+ }
+ std::vector<post_t *>::iterator end() {
+ return posts_collector->end();
+ }
+ };
+
+ shared_ptr<collector_wrapper>
+ py_collect(journal_t& journal, const string& query)
{
- journal.xact_finalize_hooks.run_hooks(xact);
+ if (journal.has_xdata()) {
+ PyErr_SetString(PyExc_RuntimeError,
+ _("Cannot have multiple journal collections open at once"));
+ throw_error_already_set();
+ }
+
+ report_t& current_report(downcast<report_t>(*scope_t::default_scope));
+ shared_ptr<collector_wrapper> coll(new collector_wrapper(journal,
+ current_report));
+ std::auto_ptr<journal_t> save_journal
+ (current_report.session.journal.release());
+ current_report.session.journal.reset(&journal);
+
+ try {
+ strings_list remaining =
+ process_arguments(split_arguments(query.c_str()), coll->report);
+ coll->report.normalize_options("register");
+
+ value_t args;
+ foreach (const string& arg, remaining)
+ args.push_back(string_value(arg));
+ coll->report.parse_query_args(args, "@Journal.collect");
+
+ journal_posts_iterator walker(coll->journal);
+ coll->chain =
+ chain_post_handlers(coll->report,
+ post_handler_ptr(coll->posts_collector));
+ pass_down_posts(coll->chain, walker);
+ }
+ catch (...) {
+ current_report.session.journal.release();
+ current_report.session.journal.reset(save_journal.release());
+ throw;
+ }
+ current_report.session.journal.release();
+ current_report.session.journal.reset(save_journal.release());
+
+ return coll;
}
- std::size_t py_read(journal_t& journal, const string& pathname)
+ post_t * posts_getitem(collector_wrapper& collector, long i)
{
- return journal.read(pathname);
+ post_t * post = collector.posts_collector->posts[i];
+ std::cerr << typeid(post).name() << std::endl;
+ std::cerr << typeid(*post).name() << std::endl;
+ std::cerr << typeid(post->account).name() << std::endl;
+ std::cerr << typeid(*post->account).name() << std::endl;
+ return post;
}
} // unnamed namespace
void export_journal()
{
+ class_< item_handler<post_t>, shared_ptr<item_handler<post_t> >,
+ boost::noncopyable >("PostHandler")
+ ;
+
+ class_< collect_posts, bases<item_handler<post_t> >,
+ shared_ptr<collect_posts>, boost::noncopyable >("PostCollector")
+ .def("__len__", &collect_posts::length)
+ .def("__iter__", range<return_internal_reference<> >
+ (&collect_posts::begin, &collect_posts::end))
+ ;
+
+ class_< collector_wrapper, shared_ptr<collector_wrapper>,
+ boost::noncopyable >("PostCollectorWrapper", no_init)
+ .def("__len__", &collector_wrapper::length)
+ .def("__getitem__", posts_getitem, return_internal_reference<>())
+ .def("__iter__", range<return_internal_reference<> >
+ (&collector_wrapper::begin, &collector_wrapper::end))
+ ;
+
class_< journal_t::fileinfo_t > ("FileInfo")
.def(init<path>())
@@ -206,11 +273,6 @@ void export_journal()
.add_property("commodity_pool",
make_getter(&journal_t::commodity_pool,
return_internal_reference<>()))
-#if 0
- .add_property("xact_finalize_hooks",
- make_getter(&journal_t::xact_finalize_hooks),
- make_setter(&journal_t::xact_finalize_hooks))
-#endif
.def("add_account", &journal_t::add_account)
.def("remove_account", &journal_t::remove_account)
@@ -223,12 +285,8 @@ void export_journal()
.def("add_xact", &journal_t::add_xact)
.def("remove_xact", &journal_t::remove_xact)
- .def("add_xact_finalizer", py_add_xact_finalizer)
- .def("remove_xact_finalizer", py_remove_xact_finalizer)
- .def("run_xact_finalizers", py_run_xact_finalizers)
-
.def("__len__", xacts_len)
- .def("__getitem__", xacts_getitem, return_internal_reference<1>())
+ .def("__getitem__", xacts_getitem, return_internal_reference<>())
.def("__iter__", range<return_internal_reference<> >
(&journal_t::xacts_begin, &journal_t::xacts_end))
@@ -243,8 +301,11 @@ void export_journal()
.def("read", py_read)
+ .def("has_xdata", &journal_t::has_xdata)
.def("clear_xdata", &journal_t::clear_xdata)
+ .def("collect", py_collect)
+
.def("valid", &journal_t::valid)
;
}
diff --git a/src/py_post.cc b/src/py_post.cc
index 80e7ee52..8aabea28 100644
--- a/src/py_post.cc
+++ b/src/py_post.cc
@@ -115,8 +115,10 @@ void export_post()
make_getter(&post_t::xdata_t::datetime),
make_setter(&post_t::xdata_t::datetime))
.add_property("account",
- make_getter(&post_t::xdata_t::account),
- make_setter(&post_t::xdata_t::account))
+ make_getter(&post_t::xdata_t::account,
+ return_internal_reference<>()),
+ make_setter(&post_t::xdata_t::account,
+ with_custodian_and_ward<1, 2>()))
.add_property("sort_values",
make_getter(&post_t::xdata_t::sort_values),
make_setter(&post_t::xdata_t::sort_values))
diff --git a/src/py_xact.cc b/src/py_xact.cc
index 6feb6080..59c599d9 100644
--- a/src/py_xact.cc
+++ b/src/py_xact.cc
@@ -122,16 +122,12 @@ void export_xact()
.def("lookup", &xact_t::lookup)
+ .def("has_xdata", &xact_t::has_xdata)
.def("clear_xdata", &xact_t::clear_xdata)
.def("valid", &xact_t::valid)
;
- class_< xact_finalizer_t, boost::noncopyable >
- ("TransactionFinalizer", no_init)
- .def("__call__", &xact_finalizer_t::operator())
- ;
-
class_< auto_xact_t, bases<xact_base_t> > ("AutomatedTransaction")
.def(init<predicate_t>())
@@ -142,16 +138,6 @@ void export_xact()
.def("extend_xact", &auto_xact_t::extend_xact)
;
- class_< auto_xact_finalizer_t, bases<xact_finalizer_t> >
- ("AutomatedTransactionFinalizer")
- .add_property("journal",
- make_getter(&auto_xact_finalizer_t::journal,
- return_internal_reference<>()),
- make_setter(&auto_xact_finalizer_t::journal,
- with_custodian_and_ward<1, 2>()))
- .def("__call__", &auto_xact_finalizer_t::operator())
- ;
-
class_< period_xact_t, bases<xact_base_t> > ("PeriodicTransaction")
.def(init<string>())
@@ -162,16 +148,6 @@ void export_xact()
make_getter(&period_xact_t::period_string),
make_setter(&period_xact_t::period_string))
;
-
- class_< func_finalizer_t, bases<xact_finalizer_t> >
- ("FunctionalFinalizer", init<func_finalizer_t::func_t>())
- .add_property("func",
- make_getter(&func_finalizer_t::func),
- make_setter(&func_finalizer_t::func))
- .def("__call__", &func_finalizer_t::operator())
- ;
-
- scope().attr("extend_xact_base") = &extend_xact_base;
}
} // namespace ledger
diff --git a/src/pyinterp.cc b/src/pyinterp.cc
index 394739c4..0a56049c 100644
--- a/src/pyinterp.cc
+++ b/src/pyinterp.cc
@@ -60,19 +60,19 @@ void export_xact();
void initialize_for_python()
{
- export_account();
+ export_times();
+ export_utils();
+ export_commodity();
export_amount();
+ export_value();
+ export_account();
export_balance();
- export_commodity();
export_expr();
export_format();
export_item();
- export_journal();
export_post();
- export_times();
- export_utils();
- export_value();
export_xact();
+ export_journal();
}
struct python_run
diff --git a/src/report.cc b/src/report.cc
index b866970f..49633350 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -33,6 +33,7 @@
#include "report.h"
#include "session.h"
+#include "pool.h"
#include "format.h"
#include "query.h"
#include "output.h"
@@ -47,6 +48,201 @@
namespace ledger {
+void report_t::normalize_options(const string& verb)
+{
+ // Patch up some of the reporting options based on what kind of
+ // command it was.
+
+#ifdef HAVE_ISATTY
+ if (! HANDLED(force_color)) {
+ if (! HANDLED(no_color) && isatty(STDOUT_FILENO))
+ HANDLER(color).on_only(string("?normalize"));
+ if (HANDLED(color) && ! isatty(STDOUT_FILENO))
+ HANDLER(color).off();
+ }
+ if (! HANDLED(force_pager)) {
+ if (HANDLED(pager_) && ! isatty(STDOUT_FILENO))
+ HANDLER(pager_).off();
+ }
+#endif
+
+ item_t::use_effective_date = (HANDLED(effective) &&
+ ! HANDLED(actual_dates));
+
+ session.journal->commodity_pool->keep_base = HANDLED(base);
+ session.journal->commodity_pool->get_quotes = session.HANDLED(download);
+
+ if (session.HANDLED(price_exp_))
+ session.journal->commodity_pool->quote_leeway =
+ session.HANDLER(price_exp_).value.as_long();
+
+ if (session.HANDLED(price_db_))
+ session.journal->commodity_pool->price_db =
+ session.HANDLER(price_db_).str();
+ else
+ session.journal->commodity_pool->price_db = none;
+
+ if (HANDLED(date_format_))
+ set_date_format(HANDLER(date_format_).str().c_str());
+ if (HANDLED(datetime_format_))
+ set_datetime_format(HANDLER(datetime_format_).str().c_str());
+ if (HANDLED(start_of_week_)) {
+ if (optional<date_time::weekdays> weekday =
+ string_to_day_of_week(HANDLER(start_of_week_).str()))
+ start_of_week = *weekday;
+ }
+
+ if (verb == "print" || verb == "xact" || verb == "dump") {
+ HANDLER(related).on_only(string("?normalize"));
+ HANDLER(related_all).on_only(string("?normalize"));
+ }
+ else if (verb == "equity") {
+ HANDLER(equity).on_only(string("?normalize"));
+ }
+
+ if (verb == "print")
+ HANDLER(limit_).on(string("?normalize"), "actual");
+
+ if (! HANDLED(empty))
+ HANDLER(display_).on(string("?normalize"), "amount|(!post&total)");
+
+ if (verb[0] != 'b' && verb[0] != 'r')
+ HANDLER(base).on_only(string("?normalize"));
+
+ // If a time period was specified with -p, check whether it also gave a
+ // begin and/or end to the report period (though these can be overridden
+ // using -b or -e). Then, if no _duration_ was specified (such as monthly),
+ // then ignore the period since the begin/end are the only interesting
+ // details.
+ if (HANDLED(period_)) {
+ if (! HANDLED(sort_all_))
+ HANDLER(sort_xacts_).on_only(string("?normalize"));
+
+ date_interval_t interval(HANDLER(period_).str());
+
+ if (! HANDLED(begin_) && interval.start) {
+ string predicate =
+ "date>=[" + to_iso_extended_string(*interval.start) + "]";
+ HANDLER(limit_).on(string("?normalize"), predicate);
+ }
+ if (! HANDLED(end_) && interval.end) {
+ string predicate =
+ "date<[" + to_iso_extended_string(*interval.end) + "]";
+ HANDLER(limit_).on(string("?normalize"), predicate);
+ }
+
+ if (! interval.duration)
+ HANDLER(period_).off();
+ }
+
+ // If -j or -J were specified, set the appropriate format string now so as
+ // to avoid option ordering issues were we to have done it during the
+ // initial parsing of the options.
+ if (HANDLED(amount_data)) {
+ HANDLER(format_)
+ .on_with(string("?normalize"), HANDLER(plot_amount_format_).value);
+ }
+ else if (HANDLED(total_data)) {
+ HANDLER(format_)
+ .on_with(string("?normalize"), HANDLER(plot_total_format_).value);
+ }
+
+ // If the --exchange (-X) option was used, parse out any final price
+ // settings that may be there.
+ if (HANDLED(exchange_) &&
+ HANDLER(exchange_).str().find('=') != string::npos) {
+ value_t(0L).exchange_commodities(HANDLER(exchange_).str(), true,
+ terminus);
+ }
+
+ long cols = 0;
+ if (HANDLED(columns_))
+ cols = HANDLER(columns_).value.to_long();
+ else if (const char * columns = std::getenv("COLUMNS"))
+ cols = lexical_cast<long>(columns);
+ else
+ cols = 80L;
+
+ if (cols > 0) {
+ DEBUG("auto.columns", "cols = " << cols);
+
+ if (! HANDLER(date_width_).specified)
+ HANDLER(date_width_)
+ .on_with(none, static_cast<long>(format_date(CURRENT_DATE(),
+ FMT_PRINTED).length()));
+
+ long date_width = HANDLER(date_width_).value.to_long();
+ long payee_width = (HANDLER(payee_width_).specified ?
+ HANDLER(payee_width_).value.to_long() :
+ int(double(cols) * 0.263157));
+ long account_width = (HANDLER(account_width_).specified ?
+ HANDLER(account_width_).value.to_long() :
+ int(double(cols) * 0.302631));
+ long amount_width = (HANDLER(amount_width_).specified ?
+ HANDLER(amount_width_).value.to_long() :
+ int(double(cols) * 0.157894));
+ long total_width = (HANDLER(total_width_).specified ?
+ HANDLER(total_width_).value.to_long() :
+ amount_width);
+
+ DEBUG("auto.columns", "date_width = " << date_width);
+ DEBUG("auto.columns", "payee_width = " << payee_width);
+ DEBUG("auto.columns", "account_width = " << account_width);
+ DEBUG("auto.columns", "amount_width = " << amount_width);
+ DEBUG("auto.columns", "total_width = " << total_width);
+
+ if (! HANDLER(date_width_).specified &&
+ ! HANDLER(payee_width_).specified &&
+ ! HANDLER(account_width_).specified &&
+ ! HANDLER(amount_width_).specified &&
+ ! HANDLER(total_width_).specified) {
+ long total = (4 /* the spaces between */ + date_width + payee_width +
+ account_width + amount_width + total_width);
+ if (total > cols) {
+ DEBUG("auto.columns", "adjusting account down");
+ account_width -= total - cols;
+ DEBUG("auto.columns", "account_width now = " << account_width);
+ }
+ }
+
+ if (! HANDLER(date_width_).specified)
+ HANDLER(date_width_).on_with(string("?normalize"), date_width);
+ if (! HANDLER(payee_width_).specified)
+ HANDLER(payee_width_).on_with(string("?normalize"), payee_width);
+ if (! HANDLER(account_width_).specified)
+ HANDLER(account_width_).on_with(string("?normalize"), account_width);
+ if (! HANDLER(amount_width_).specified)
+ HANDLER(amount_width_).on_with(string("?normalize"), amount_width);
+ if (! HANDLER(total_width_).specified)
+ HANDLER(total_width_).on_with(string("?normalize"), total_width);
+ }
+}
+
+void report_t::parse_query_args(const value_t& args, const string& whence)
+{
+ query_t query(args, what_to_keep());
+ if (! query)
+ throw_(std::runtime_error,
+ _("Invalid query predicate: %1") << query.text());
+
+ HANDLER(limit_).on(whence, query.text());
+
+ DEBUG("report.predicate",
+ "Predicate = " << HANDLER(limit_).str());
+
+ if (query.tokens_remaining()) {
+ query.parse_again();
+ if (! query)
+ throw_(std::runtime_error,
+ _("Invalid display predicate: %1") << query.text());
+
+ HANDLER(display_).on(whence, query.text());
+
+ DEBUG("report.predicate",
+ "Display predicate = " << HANDLER(display_).str());
+ }
+}
+
void report_t::posts_report(post_handler_ptr handler)
{
journal_posts_iterator walker(*session.journal.get());
@@ -353,6 +549,69 @@ value_t report_t::fn_lot_tag(call_scope_t& scope)
return NULL_VALUE;
}
+value_t report_t::fn_to_boolean(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::BOOLEAN);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_int(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::INTEGER);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_datetime(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::DATETIME);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_date(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::DATE);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_amount(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::AMOUNT);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_balance(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::BALANCE);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_string(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::STRING);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_mask(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::MASK);
+ return args.value_at(0);
+}
+
+value_t report_t::fn_to_sequence(call_scope_t& scope)
+{
+ interactive_t args(scope, "v");
+ args.value_at(0).in_place_cast(value_t::SEQUENCE);
+ return args.value_at(0);
+}
+
namespace {
value_t fn_black(call_scope_t&) {
return string_value("black");
@@ -393,54 +652,6 @@ namespace {
value_t fn_null(call_scope_t&) {
return NULL_VALUE;
}
-
- template <class Type = post_t,
- class handler_ptr = post_handler_ptr,
- void (report_t::*report_method)(handler_ptr) =
- &report_t::posts_report>
- class reporter
- {
- shared_ptr<item_handler<Type> > handler;
-
- report_t& report;
- string whence;
-
- public:
- reporter(item_handler<Type> * _handler, report_t& _report,
- const string& _whence)
- : handler(_handler), report(_report), whence(_whence) {}
-
- value_t operator()(call_scope_t& args)
- {
- if (args.size() > 0) {
- query_t query(args.value(), report.what_to_keep());
- if (! query)
- throw_(std::runtime_error,
- _("Invalid query predicate: %1") << query.text());
-
- report.HANDLER(limit_).on(whence, query.text());
-
- DEBUG("report.predicate",
- "Predicate = " << report.HANDLER(limit_).str());
-
- if (query.tokens_remaining()) {
- query.parse_again();
- if (! query)
- throw_(std::runtime_error,
- _("Invalid display predicate: %1") << query.text());
-
- report.HANDLER(display_).on(whence, query.text());
-
- DEBUG("report.predicate",
- "Display predicate = " << report.HANDLER(display_).str());
- }
- }
-
- (report.*report_method)(handler_ptr(handler));
-
- return true;
- }
- };
}
value_t report_t::reload_command(call_scope_t&)
@@ -561,6 +772,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
break;
case 'd':
OPT(daily);
+ else OPT(date_);
else OPT(date_format_);
else OPT(datetime_format_);
else OPT(depth_);
@@ -634,7 +846,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT(plot_total_format_);
else OPT(price);
else OPT(prices_format_);
- else OPT(pricesdb_format_);
+ else OPT(pricedb_format_);
else OPT(print_format_);
else OPT(payee_width_);
else OPT(prepend_format_);
@@ -822,6 +1034,24 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return MAKE_FUNCTOR(report_t::fn_today);
else if (is_eq(p, "t"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
+ else if (is_eq(p, "to_boolean"))
+ return MAKE_FUNCTOR(report_t::fn_to_boolean);
+ else if (is_eq(p, "to_int"))
+ return MAKE_FUNCTOR(report_t::fn_to_int);
+ else if (is_eq(p, "to_datetime"))
+ return MAKE_FUNCTOR(report_t::fn_to_datetime);
+ else if (is_eq(p, "to_date"))
+ return MAKE_FUNCTOR(report_t::fn_to_date);
+ else if (is_eq(p, "to_amount"))
+ return MAKE_FUNCTOR(report_t::fn_to_amount);
+ else if (is_eq(p, "to_balance"))
+ return MAKE_FUNCTOR(report_t::fn_to_balance);
+ else if (is_eq(p, "to_string"))
+ return MAKE_FUNCTOR(report_t::fn_to_string);
+ else if (is_eq(p, "to_mask"))
+ return MAKE_FUNCTOR(report_t::fn_to_mask);
+ else if (is_eq(p, "to_sequence"))
+ return MAKE_FUNCTOR(report_t::fn_to_sequence);
break;
case 'T':
@@ -929,12 +1159,12 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
(new format_posts(*this, report_format(HANDLER(prices_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#prices"));
- else if (is_eq(p, "pricesdb"))
+ else if (is_eq(p, "pricedb"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
- (new format_posts(*this, report_format(HANDLER(pricesdb_format_)),
+ (new format_posts(*this, report_format(HANDLER(pricedb_format_)),
maybe_format(HANDLER(prepend_format_))),
- *this, "#pricesdb"));
+ *this, "#pricedb"));
break;
case 'r':
@@ -951,8 +1181,6 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
case 's':
if (is_eq(p, "stats") || is_eq(p, "stat"))
return WRAP_FUNCTOR(report_statistics);
- else if (is_eq(p, "server"))
- return session.lookup(symbol_t::COMMAND, "server");
break;
case 'x':
diff --git a/src/report.h b/src/report.h
index e27abbf9..c656829b 100644
--- a/src/report.h
+++ b/src/report.h
@@ -44,6 +44,7 @@
#include "interactive.h"
#include "expr.h"
+#include "query.h"
#include "chain.h"
#include "stream.h"
#include "option.h"
@@ -124,6 +125,9 @@ public:
output_stream.close();
}
+ void normalize_options(const string& verb);
+ void parse_query_args(const value_t& args, const string& whence);
+
void posts_report(post_handler_ptr handler);
void generate_report(post_handler_ptr handler);
void xact_report(post_handler_ptr handler, xact_t& xact);
@@ -155,6 +159,15 @@ public:
value_t fn_lot_date(call_scope_t& scope);
value_t fn_lot_price(call_scope_t& scope);
value_t fn_lot_tag(call_scope_t& scope);
+ value_t fn_to_boolean(call_scope_t& scope);
+ value_t fn_to_int(call_scope_t& scope);
+ value_t fn_to_datetime(call_scope_t& scope);
+ value_t fn_to_date(call_scope_t& scope);
+ value_t fn_to_amount(call_scope_t& scope);
+ value_t fn_to_balance(call_scope_t& scope);
+ value_t fn_to_string(call_scope_t& scope);
+ value_t fn_to_mask(call_scope_t& scope);
+ value_t fn_to_sequence(call_scope_t& scope);
value_t fn_now(call_scope_t&) {
return terminus;
@@ -217,6 +230,7 @@ public:
HANDLER(csv_format_).report(out);
HANDLER(current).report(out);
HANDLER(daily).report(out);
+ HANDLER(date_).report(out);
HANDLER(date_format_).report(out);
HANDLER(datetime_format_).report(out);
HANDLER(depth_).report(out);
@@ -262,7 +276,7 @@ public:
HANDLER(prepend_format_).report(out);
HANDLER(price).report(out);
HANDLER(prices_format_).report(out);
- HANDLER(pricesdb_format_).report(out);
+ HANDLER(pricedb_format_).report(out);
HANDLER(print_format_).report(out);
HANDLER(quantity).report(out);
HANDLER(quarterly).report(out);
@@ -450,6 +464,7 @@ public:
parent->HANDLER(period_).on(string("--daily"), "daily");
});
+ OPTION(report_t, date_);
OPTION(report_t, date_format_);
OPTION(report_t, datetime_format_);
@@ -712,11 +727,11 @@ public:
OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) {
on(none,
- "%-.9(date) %-8(account) %(justify(scrub(display_amount), 12, "
+ "%(date) %-8(account) %(justify(scrub(display_amount), 12, "
" 2 + 9 + 8 + 12, true, color))\n");
});
- OPTION__(report_t, pricesdb_format_, CTOR(report_t, pricesdb_format_) {
+ OPTION__(report_t, pricedb_format_, CTOR(report_t, pricedb_format_) {
on(none,
"P %(datetime) %(account) %(scrub(display_amount))\n");
});
@@ -896,6 +911,34 @@ public:
DO_(args) { value = args[1].to_long(); specified = true; });
};
+
+template <class Type = post_t,
+ class handler_ptr = post_handler_ptr,
+ void (report_t::*report_method)(handler_ptr) =
+ &report_t::posts_report>
+class reporter
+{
+ shared_ptr<item_handler<Type> > handler;
+
+ report_t& report;
+ string whence;
+
+public:
+ reporter(item_handler<Type> * _handler,
+ report_t& _report, const string& _whence)
+ : handler(_handler), report(_report), whence(_whence) {}
+
+ value_t operator()(call_scope_t& args)
+ {
+ if (args.size() > 0)
+ report.parse_query_args(args.value(), whence);
+
+ (report.*report_method)(handler_ptr(handler));
+
+ return true;
+ }
+};
+
} // namespace ledger
#endif // _REPORT_H
diff --git a/src/session.cc b/src/session.cc
index 9fb8df46..0d6a6829 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -106,7 +106,7 @@ std::size_t session_t::read_data(const string& master_account)
if (! (cache &&
cache->should_load(HANDLER(file_).data_files) &&
- cache->load(journal))) {
+ cache->load(*journal.get()))) {
#endif // HAVE_BOOST_SERIALIZATION
if (price_db_path) {
if (exists(*price_db_path)) {
@@ -142,8 +142,8 @@ std::size_t session_t::read_data(const string& master_account)
assert(xact_count == journal->xacts.size());
#if defined(HAVE_BOOST_SERIALIZATION)
- if (cache && cache->should_save(journal))
- cache->save(journal);
+ if (cache && cache->should_save(*journal.get()))
+ cache->save(*journal.get());
}
#endif // HAVE_BOOST_SERIALIZATION
diff --git a/src/session.h b/src/session.h
index bde37f46..5c4612a0 100644
--- a/src/session.h
+++ b/src/session.h
@@ -58,9 +58,9 @@ class session_t : public symbol_scope_t
friend void set_session_context(session_t * session);
public:
- bool flush_on_next_data_file;
- date_t::year_type current_year;
- shared_ptr<journal_t> journal;
+ bool flush_on_next_data_file;
+ date_t::year_type current_year;
+ std::auto_ptr<journal_t> journal;
explicit session_t();
virtual ~session_t() {
diff --git a/src/textual.cc b/src/textual.cc
index 8f0dd89c..27ea13b8 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -79,8 +79,6 @@ namespace {
optional<date_t::year_type> current_year;
- scoped_ptr<auto_xact_finalizer_t> auto_xact_finalizer;
-
instance_t(std::list<account_t *>& _account_stack,
std::list<string>& _tag_stack,
#if defined(TIMELOG_SUPPORT)
@@ -227,9 +225,6 @@ instance_t::~instance_t()
TRACE_DTOR(instance_t);
account_stack.pop_front();
-
- if (auto_xact_finalizer.get())
- journal.remove_xact_finalizer(auto_xact_finalizer.get());
}
void instance_t::parse()
@@ -546,11 +541,6 @@ void instance_t::automated_xact_directive(char * line)
try {
- if (! auto_xact_finalizer.get()) {
- auto_xact_finalizer.reset(new auto_xact_finalizer_t(&journal));
- journal.add_xact_finalizer(auto_xact_finalizer.get());
- }
-
std::auto_ptr<auto_xact_t> ae
(new auto_xact_t(query_t(string(skip_ws(line + 1)),
keep_details_t(true, true, true))));
@@ -601,8 +591,7 @@ void instance_t::period_xact_directive(char * line)
pe->journal = &journal;
if (pe->finalize()) {
- extend_xact_base(&journal, *pe.get());
-
+ journal.extend_xact(pe.get());
journal.period_xacts.push_back(pe.get());
pe->pos = position_t();
diff --git a/src/utils.cc b/src/utils.cc
index 6cef1a8c..d661f410 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -417,6 +417,8 @@ void report_memory(std::ostream& out, bool report_all)
namespace ledger {
+#if defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON)
+
string::string() : std::string() {
TRACE_CTOR(string, "");
}
@@ -453,6 +455,8 @@ string::~string() throw() {
TRACE_DTOR(string);
}
+#endif // defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON)
+
string empty_string("");
strings_list split_arguments(const char * line)
diff --git a/src/utils.h b/src/utils.h
index 8ddc3c44..f0a165c2 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -181,6 +181,8 @@ void report_memory(std::ostream& out, bool report_all = false);
namespace ledger {
+#if defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON)
+
class string : public std::string
{
public:
@@ -253,6 +255,12 @@ inline bool operator!=(const char* __lhs, const string& __rhs)
inline bool operator!=(const string& __lhs, const char* __rhs)
{ return __lhs.compare(__rhs) != 0; }
+#else // defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON)
+
+typedef std::string string;
+
+#endif // defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON)
+
extern string empty_string;
strings_list split_arguments(const char * line);
diff --git a/src/value.cc b/src/value.cc
index ae86eb7c..910a9140 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -1146,6 +1146,12 @@ void value_t::in_place_cast(type_t cast_type)
case AMOUNT:
set_amount(amount_t(as_string()));
return;
+ case DATE:
+ set_date(parse_date(as_string()));
+ return;
+ case DATETIME:
+ set_datetime(parse_datetime(as_string()));
+ return;
case MASK:
set_mask(as_string());
return;
diff --git a/src/xact.cc b/src/xact.cc
index 8ac5280a..1cece187 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -76,6 +76,15 @@ bool xact_base_t::remove_post(post_t * post)
return true;
}
+bool xact_base_t::has_xdata()
+{
+ foreach (post_t * post, posts)
+ if (post->has_xdata())
+ return true;
+
+ return false;
+}
+
void xact_base_t::clear_xdata()
{
foreach (post_t * post, posts)
@@ -372,6 +381,55 @@ bool xact_base_t::finalize()
return true;
}
+bool xact_base_t::verify()
+{
+ // Scan through and compute the total balance for the xact.
+
+ value_t balance;
+
+ foreach (post_t * post, posts) {
+ if (! post->must_balance())
+ continue;
+
+ amount_t& p(post->cost ? *post->cost : post->amount);
+ assert(! p.is_null());
+
+ // If the amount was a cost, it very likely has the "keep_precision" flag
+ // set, meaning commodity display precision is ignored when displaying the
+ // amount. We never want this set for the balance, so we must clear the
+ // flag in a temporary to avoid it propagating into the balance.
+ add_or_set_value(balance, p.keep_precision() ?
+ p.rounded().reduced() : p.reduced());
+ }
+ VERIFY(balance.valid());
+
+ // Now that the post list has its final form, calculate the balance once
+ // more in terms of total cost, accounting for any possible gain/loss
+ // amounts.
+
+ foreach (post_t * post, posts) {
+ if (! post->cost)
+ continue;
+
+ if (post->amount.commodity() == post->cost->commodity())
+ throw_(balance_error,
+ _("A posting's cost must be of a different commodity than its amount"));
+ }
+
+ if (! balance.is_null() && ! balance.is_zero()) {
+ add_error_context(item_context(*this, _("While balancing transaction")));
+ add_error_context(_("Unbalanced remainder is:"));
+ add_error_context(value_context(balance));
+ add_error_context(_("Amount to balance against:"));
+ add_error_context(value_context(magnitude()));
+ throw_(balance_error, _("Transaction does not balance"));
+ }
+
+ VERIFY(valid());
+
+ return true;
+}
+
xact_t::xact_t(const xact_t& e)
: xact_base_t(e), code(e.code), payee(e.payee)
{
@@ -486,6 +544,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact)
try {
+ bool needs_further_verification = false;
+
foreach (post_t * initial_post, initial_posts) {
if (! initial_post->has_flags(ITEM_GENERATED) &&
predicate(*initial_post)) {
@@ -555,10 +615,16 @@ void auto_xact_t::extend_xact(xact_base_t& xact)
xact.add_post(new_post);
new_post->account->add_post(new_post);
+
+ if (new_post->must_balance())
+ needs_further_verification = true;
}
}
}
+ if (needs_further_verification)
+ xact.verify();
+
}
catch (const std::exception& err) {
add_error_context(item_context(*this, _("While applying automated transaction")));
diff --git a/src/xact.h b/src/xact.h
index ff1f65fa..fe748fcc 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -77,8 +77,10 @@ public:
value_t magnitude() const;
- virtual bool finalize();
+ bool finalize();
+ bool verify();
+ bool has_xdata();
void clear_xdata();
virtual bool valid() const {
@@ -140,11 +142,6 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
-struct xact_finalizer_t {
- virtual ~xact_finalizer_t() {}
- virtual bool operator()(xact_t& xact) = 0;
-};
-
class auto_xact_t : public xact_base_t
{
public:
@@ -183,39 +180,6 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
-struct auto_xact_finalizer_t : public xact_finalizer_t
-{
- journal_t * journal;
-
- auto_xact_finalizer_t() : journal(NULL) {
- TRACE_CTOR(auto_xact_finalizer_t, "");
- }
- auto_xact_finalizer_t(const auto_xact_finalizer_t& other)
- : xact_finalizer_t(), journal(other.journal) {
- TRACE_CTOR(auto_xact_finalizer_t, "copy");
- }
- auto_xact_finalizer_t(journal_t * _journal) : journal(_journal) {
- TRACE_CTOR(auto_xact_finalizer_t, "journal_t *");
- }
- ~auto_xact_finalizer_t() throw() {
- TRACE_DTOR(auto_xact_finalizer_t);
- }
-
- virtual bool operator()(xact_t& xact);
-
-#if defined(HAVE_BOOST_SERIALIZATION)
-private:
- /** Serialization. */
-
- friend class boost::serialization::access;
-
- template<class Archive>
- void serialize(Archive& ar, const unsigned int /* version */) {
- ar & journal;
- }
-#endif // HAVE_BOOST_SERIALIZATION
-};
-
class period_xact_t : public xact_base_t
{
public:
@@ -253,38 +217,6 @@ private:
#endif // HAVE_BOOST_SERIALIZATION
};
-class func_finalizer_t : public xact_finalizer_t
-{
- func_finalizer_t();
-
-public:
- typedef function<bool (xact_t& xact)> func_t;
-
- func_t func;
-
- func_finalizer_t(func_t _func) : func(_func) {
- TRACE_CTOR(func_finalizer_t, "func_t");
- }
- func_finalizer_t(const func_finalizer_t& other) :
- xact_finalizer_t(), func(other.func) {
- TRACE_CTOR(func_finalizer_t, "copy");
- }
- ~func_finalizer_t() throw() {
- TRACE_DTOR(func_finalizer_t);
- }
-
- virtual bool operator()(xact_t& xact) {
- return func(xact);
- }
-};
-
-void extend_xact_base(journal_t * journal, xact_base_t& xact);
-
-inline bool auto_xact_finalizer_t::operator()(xact_t& xact) {
- extend_xact_base(journal, xact);
- return true;
-}
-
typedef std::list<xact_t *> xacts_list;
typedef std::list<auto_xact_t *> auto_xacts_list;
typedef std::list<period_xact_t *> period_xacts_list;
diff --git a/test/baseline/opt-pricesdb-format.test b/test/baseline/opt-pricedb-format.test
index b90371cd..5dbff609 100644
--- a/test/baseline/opt-pricesdb-format.test
+++ b/test/baseline/opt-pricedb-format.test
@@ -1,4 +1,4 @@
-pricesdb --pricesdb-format='P %(date) %(scrub(display_amount))\n'
+pricedb --pricedb-format='P %(date) %(scrub(display_amount))\n'
<<<
P 2009/01/01 13:30:00 AAPL $10.00
P 2009/01/01 14:30:00 AAPL $20.00
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 8c28277b..9821bf4c 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -89,7 +89,6 @@ libledger_report_la_LDFLAGS = -release $(VERSION)
pkginclude_HEADERS = \
src/utils.h \
src/flags.h \
- src/hooks.h \
src/error.h \
src/times.h \
src/mask.h \
@@ -440,7 +439,8 @@ fullcheck: cppunittests
######################################################################
-EXTRA_DIST += doc/README doc/LICENSE doc/NEWS doc/ledger.pdf
+EXTRA_DIST += doc/README doc/NEWS doc/ledger.pdf
+EXTRA_DIST += doc/LICENSE doc/LICENSE-sha1 doc/LICENSE-utfcpp
if USE_DOXYGEN
EXTRA_DIST += doc/Doxyfile doc/refman.pdf
endif