summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xacprep1
-rw-r--r--lisp/ledger.el42
-rw-r--r--src/account.cc4
-rw-r--r--src/amount.cc12
-rw-r--r--src/balance.cc6
-rw-r--r--src/balance.h7
-rw-r--r--src/commodity.cc6
-rw-r--r--src/derive.cc144
-rw-r--r--src/filters.cc9
-rw-r--r--src/filters.h8
-rw-r--r--src/global.cc81
-rw-r--r--src/global.h7
-rw-r--r--src/item.cc8
-rw-r--r--src/option.cc18
-rw-r--r--src/option.h48
-rw-r--r--src/output.cc4
-rw-r--r--src/report.cc48
-rw-r--r--src/report.h388
-rw-r--r--src/session.cc19
-rw-r--r--src/session.h20
-rw-r--r--src/textual.cc8
-rw-r--r--src/unistring.h13
-rw-r--r--src/value.cc21
-rw-r--r--src/value.h1
-rw-r--r--src/xact.h8
-rw-r--r--test/baseline/feat-balance-assignments.test37
-rw-r--r--test/baseline/feat-fixated-prices.test (renamed from test/baseline/feature-fixated-prices.test)0
-rw-r--r--test/input/drewr.dat4
-rw-r--r--test/regress/04C5E1CA.test18
-rw-r--r--test/regress/5A03CFC3.test72
-rw-r--r--test/regress/5FBF2ED8.test20
-rw-r--r--test/regress/647D5DB9.test17
-rw-r--r--test/regress/6DAB9FE3.test10
-rw-r--r--test/regress/727B2DF8.test81
-rw-r--r--test/regress/793F6BF0.test52
-rw-r--r--test/regress/B68FFB0D.test15
-rw-r--r--test/regress/C523E23F.test21
-rw-r--r--test/regress/D943AE0F.test13
-rw-r--r--test/unit/t_amount.cc40
39 files changed, 1021 insertions, 310 deletions
diff --git a/acprep b/acprep
index c1a0ddc7..8790501e 100755
--- a/acprep
+++ b/acprep
@@ -996,6 +996,7 @@ class PrepareBuild(CommandLineApp):
line = re.sub('^\t(\$\((LIBTOOL|CXX)\).*?\.(cc|cpp))$',
'\t@echo " " CXX \$@;\\1 > /dev/null', line)
line = re.sub('^\tmv -f', '\t@mv -f', line)
+ line = re.sub('^\t\$\(am__mv\)', '\t@$(am__mv)', line)
line = re.sub('^\t(\$\((.*?)LINK\).*)',
'\t@echo " " LD \$@;\\1 > /dev/null', line)
Makefile_new.write(line)
diff --git a/lisp/ledger.el b/lisp/ledger.el
index a649250d..b09ddc34 100644
--- a/lisp/ledger.el
+++ b/lisp/ledger.el
@@ -128,17 +128,17 @@ text that should replace the format specifier."
(defvar bold 'bold)
(defvar ledger-font-lock-keywords
- '(("^[0-9./=]+\\s-+\\(?:([^)]+)\\s-+\\)?\\([^*].+\\)" 1 bold)
- ("\s \\{5,\\}\\([$]-[0-9][0-9,.]*\\)" 1 font-lock-warning-face)
- ("\s \\{5,\\}\\(-?[0-9][0-9,.]*\\)" 1 font-lock-type-face)
- ("^\\s-+.+?\\( \\|\t\\|\n\\|\\s-+$\\)" . font-lock-keyword-face)
- ("^\s +\\((\\)\\([A-Za-z0-9]+:[A-Za-z0-9]+[A-Za-z0-9: ]*\\)\\()\\)"
- (1 font-lock-function-name-face)
- (2 font-lock-variable-name-face)
- (3 font-lock-function-name-face))
- ("^[0-9]+[/-][0-9]+.*\\([*]\\)" 1 bold)
- ("^\\([~=]\\)\\s " 1 font-lock-function-name-face))
- "Improved expressions to highlight in Ledger mode.")
+ '(("\\( \\| \\|^\\)\\(;.*\\)" 2 font-lock-comment-face)
+ ("^[0-9]+[-/.=][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)" 2 bold)
+ ;;("^[0-9]+[-/.=][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([*].+?\\)\\(\\( ;\\| ;\\|$\\)\\)"
+ ;; 2 font-lock-type-face)
+ ("^\\s-+\\([*]\\s-*\\)?\\(\\([[(]\\)?[^*:
+ ]+?:[^]);
+ ]+?\\([])]\\)?\\)\\( \\| \\|$\\)"
+ 2 font-lock-keyword-face)
+ ("^\\([~=].+\\)" 1 font-lock-function-name-face)
+ ("^\\([A-Za-z]+ .+\\)" 1 font-lock-function-name-face))
+ "Expressions to highlight in Ledger mode.")
(defsubst ledger-current-year ()
(format-time-string "%Y"))
@@ -225,9 +225,7 @@ Return the difference in the format of a time value."
(mapcar 'eval args)))
(goto-char (point-min))
(if (looking-at "Error: ")
- (progn
- (message (buffer-string))
- (error))
+ (error (buffer-string))
(buffer-string)))
"\n"))))
@@ -415,8 +413,20 @@ dropped."
(defun ledger-toggle-current (&optional style)
(interactive)
- (if ledger-clear-whole-entries
- (ledger-toggle-current-entry style)
+ (if (or ledger-clear-whole-entries
+ (eq 'entry (ledger-thing-at-point)))
+ (progn
+ (save-excursion
+ (forward-line)
+ (goto-char (line-beginning-position))
+ (while (and (not (eolp))
+ (save-excursion
+ (not (eq 'entry (ledger-thing-at-point)))))
+ (if (looking-at "\\s-+[*!]")
+ (ledger-toggle-current-transaction nil))
+ (forward-line)
+ (goto-char (line-beginning-position))))
+ (ledger-toggle-current-entry style))
(ledger-toggle-current-transaction style)))
(defvar ledger-mode-abbrev-table)
diff --git a/src/account.cc b/src/account.cc
index 711e815e..58d9392f 100644
--- a/src/account.cc
+++ b/src/account.cc
@@ -140,7 +140,7 @@ string account_t::partial_name(bool flat) const
if (! flat) {
std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY);
assert(count > 0);
- if (count > 1)
+ if (count > 1 || acct->has_flags(ACCOUNT_EXT_TO_DISPLAY))
break;
}
pname = acct->name + ":" + pname;
@@ -202,7 +202,7 @@ namespace {
acct = acct->parent) {
std::size_t count = acct->children_with_flags(ACCOUNT_EXT_TO_DISPLAY);
assert(count > 0);
- if (count > 1)
+ if (count > 1 || acct->has_flags(ACCOUNT_EXT_TO_DISPLAY))
depth++;
}
diff --git a/src/amount.cc b/src/amount.cc
index 7c9a6931..9dea532c 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -343,8 +343,8 @@ amount_t& amount_t::operator*=(const amount_t& amt)
_dup();
mpq_mul(MP(quantity), MP(quantity), MP(amt.quantity));
- quantity->prec = static_cast<precision_t>(quantity->prec +
- amt.quantity->prec);
+ quantity->prec =
+ static_cast<precision_t>(quantity->prec + amt.quantity->prec);
if (! has_commodity())
commodity_ = amt.commodity_;
@@ -382,7 +382,7 @@ amount_t& amount_t::operator/=(const amount_t& amt)
mpq_div(MP(quantity), MP(quantity), MP(amt.quantity));
quantity->prec =
static_cast<precision_t>(quantity->prec + amt.quantity->prec +
- quantity->prec + extend_by_digits);
+ extend_by_digits);
if (! has_commodity())
commodity_ = amt.commodity_;
@@ -740,7 +740,7 @@ void amount_t::annotate(const annotation_t& details)
if (! quantity)
throw_(amount_error, _("Cannot annotate the commodity of an uninitialized amount"));
else if (! has_commodity())
- throw_(amount_error, _("Cannot annotate an amount with no commodity"));
+ return; // ignore attempt to annotate a "bare commodity
if (commodity().annotated) {
this_ann = &as_annotated_commodity(commodity());
@@ -1062,8 +1062,10 @@ void amount_t::print(std::ostream& _out) const
bool amount_t::valid() const
{
if (quantity) {
- if (! quantity->valid())
+ if (! quantity->valid()) {
+ DEBUG("ledger.validate", "amount_t: ! quantity->valid()");
return false;
+ }
if (quantity->ref == 0) {
DEBUG("ledger.validate", "amount_t: quantity->ref == 0");
diff --git a/src/balance.cc b/src/balance.cc
index 864926cf..8acb6378 100644
--- a/src/balance.cc
+++ b/src/balance.cc
@@ -243,7 +243,8 @@ balance_t::strip_annotations(const keep_details_t& what_to_keep) const
void balance_t::print(std::ostream& out,
const int first_width,
const int latter_width,
- const bool right_justify) const
+ const bool right_justify,
+ const bool colorize) const
{
bool first = true;
int lwidth = latter_width;
@@ -272,7 +273,8 @@ void balance_t::print(std::ostream& out,
std::ostringstream buf;
buf << *amount;
- justify(out, buf.str(), width, right_justify);
+ justify(out, buf.str(), width, right_justify,
+ colorize && amount->sign() < 0);
}
if (first) {
diff --git a/src/balance.h b/src/balance.h
index 15657c5e..142621f8 100644
--- a/src/balance.h
+++ b/src/balance.h
@@ -483,7 +483,8 @@ public:
void print(std::ostream& out,
const int first_width = -1,
const int latter_width = -1,
- const bool right_justify = true) const;
+ const bool right_justify = true,
+ const bool colorize = true) const;
/**
* Debugging methods. There are two methods defined to help with
@@ -511,8 +512,10 @@ public:
bool valid() const {
foreach (const amounts_map::value_type& pair, amounts)
- if (! pair.second.valid())
+ if (! pair.second.valid()) {
+ DEBUG("ledger.validate", "balance_t: ! pair.second.valid()");
return false;
+ }
return true;
}
};
diff --git a/src/commodity.cc b/src/commodity.cc
index 67a86b87..d826feb6 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -418,11 +418,13 @@ commodity_t::exchange(const amount_t& amount,
if (commodity.annotated)
current_annotation = &as_annotated_commodity(commodity).details;
- amount_t per_unit_cost = (is_per_unit ? cost : cost / amount).abs();
+ amount_t per_unit_cost =
+ (is_per_unit || amount.is_realzero() ? cost : cost / amount).abs();
DEBUG("commodity.prices.add", "exchange: per-unit-cost = " << per_unit_cost);
- exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
+ if (! per_unit_cost.is_realzero())
+ exchange(commodity, per_unit_cost, moment ? *moment : CURRENT_TIME());
cost_breakdown_t breakdown;
breakdown.final_cost = ! is_per_unit ? cost : cost * amount;
diff --git a/src/derive.cc b/src/derive.cc
index 98c50fe1..2fe5754f 100644
--- a/src/derive.cc
+++ b/src/derive.cc
@@ -98,7 +98,7 @@ namespace {
foreach (const post_template_t& post, posts) {
straccstream accum;
out << std::endl
- << ACCUM(accum << _("[Posting \"%1\"]_")
+ << ACCUM(accum << _("[Posting \"%1\"]")
<< (post.from ? _("from") : _("to")))
<< std::endl;
@@ -240,28 +240,30 @@ namespace {
report_t& report)
{
if (tmpl.payee_mask.empty())
- throw std::runtime_error(_("'xact' command requires at least a payee"));
-
- xact_t * matching = NULL;
- journal_t& journal(*report.session.journal.get());
+ throw std::runtime_error(_("xact' command requires at least a payee"));
+ xact_t * matching = NULL;
+ journal_t& journal(*report.session.journal.get());
std::auto_ptr<xact_t> added(new xact_t);
- xacts_list::reverse_iterator j;
-
- for (j = journal.xacts.rbegin();
+ for (xacts_list::reverse_iterator j = journal.xacts.rbegin();
j != journal.xacts.rend();
j++) {
if (tmpl.payee_mask.match((*j)->payee)) {
matching = *j;
+ DEBUG("derive.xact",
+ "Found payee match: transaction on line " << (*j)->beg_line);
break;
}
}
- if (! tmpl.date)
+ if (! tmpl.date) {
added->_date = CURRENT_DATE();
- else
+ DEBUG("derive.xact", "Setting date to current date");
+ } else {
added->_date = tmpl.date;
+ DEBUG("derive.xact", "Setting date to template date: " << *tmpl.date);
+ }
added->set_state(item_t::UNCLEARED);
@@ -269,17 +271,32 @@ namespace {
added->payee = matching->payee;
added->code = matching->code;
added->note = matching->note;
+
+#if defined(DEBUG_ON)
+ DEBUG("derive.xact", "Setting payee from match: " << added->payee);
+ if (added->code)
+ DEBUG("derive.xact", "Setting code from match: " << *added->code);
+ if (added->note)
+ DEBUG("derive.xact", "Setting note from match: " << *added->note);
+#endif
} else {
added->payee = tmpl.payee_mask.expr.str();
+ DEBUG("derive.xact", "Setting payee from template: " << added->payee);
}
- if (tmpl.code)
+ if (tmpl.code) {
added->code = tmpl.code;
- if (tmpl.note)
+ DEBUG("derive.xact", "Now setting code from template: " << *added->code);
+ }
+ if (tmpl.note) {
added->note = tmpl.note;
+ DEBUG("derive.xact", "Now setting note from template: " << *added->note);
+ }
if (tmpl.posts.empty()) {
if (matching) {
+ DEBUG("derive.xact", "Template had no postings, copying from match");
+
foreach (post_t * post, matching->posts) {
added->add_post(new post_t(*post));
added->posts.back()->set_state(item_t::UNCLEARED);
@@ -290,9 +307,12 @@ namespace {
<< tmpl.payee_mask);
}
} else {
+ DEBUG("derive.xact", "Template had postings");
+
bool any_post_has_amount = false;
foreach (xact_template_t::post_template_t& post, tmpl.posts) {
if (post.amount) {
+ DEBUG("derive.xact", " and at least one has an amount specified");
any_post_has_amount = true;
break;
}
@@ -305,90 +325,149 @@ namespace {
if (matching) {
if (post.account_mask) {
+ DEBUG("derive.xact",
+ "Looking for matching posting based on account mask");
+
foreach (post_t * x, matching->posts) {
if (post.account_mask->match(x->account->fullname())) {
new_post.reset(new post_t(*x));
+ DEBUG("derive.xact",
+ "Founding posting from line " << x->beg_line);
break;
}
}
} else {
- if (post.from)
- new_post.reset(new post_t(*matching->posts.back()));
- else
- new_post.reset(new post_t(*matching->posts.front()));
+ if (post.from) {
+ for (posts_list::reverse_iterator j = matching->posts.rbegin();
+ j != matching->posts.rend();
+ j++) {
+ if ((*j)->must_balance()) {
+ new_post.reset(new post_t(**j));
+ DEBUG("derive.xact",
+ "Copied last real posting from matching");
+ break;
+ }
+ }
+ } else {
+ for (posts_list::iterator j = matching->posts.begin();
+ j != matching->posts.end();
+ j++) {
+ if ((*j)->must_balance()) {
+ new_post.reset(new post_t(**j));
+ DEBUG("derive.xact",
+ "Copied first real posting from matching");
+ break;
+ }
+ }
+ }
}
}
- if (! new_post.get())
+ if (! new_post.get()) {
new_post.reset(new post_t);
+ DEBUG("derive.xact", "New posting was NULL, creating a blank one");
+ }
if (! new_post->account) {
+ DEBUG("derive.xact", "New posting still needs an account");
+
if (post.account_mask) {
+ DEBUG("derive.xact", "The template has an account mask");
+
account_t * acct = NULL;
- if (! acct)
+ if (! acct) {
acct = journal.find_account_re(post.account_mask->expr.str());
- if (! acct)
+#if defined(DEBUG_ON)
+ if (acct)
+ DEBUG("derive.xact", "Found account as a regular expression");
+#endif
+ }
+ if (! acct) {
acct = journal.find_account(post.account_mask->expr.str());
+#if defined(DEBUG_ON)
+ if (acct)
+ DEBUG("derive.xact", "Found (or created) account by name");
+#endif
+ }
// Find out the default commodity to use by looking at the last
// commodity used in that account
- xacts_list::reverse_iterator j;
-
- for (j = journal.xacts.rbegin();
+ for (xacts_list::reverse_iterator j = journal.xacts.rbegin();
j != journal.xacts.rend();
j++) {
foreach (post_t * x, (*j)->posts) {
if (x->account == acct && ! x->amount.is_null()) {
new_post.reset(new post_t(*x));
+ DEBUG("derive.xact",
+ "Found account in journal postings, setting new posting");
break;
}
}
}
- if (! new_post.get())
- new_post.reset(new post_t);
new_post->account = acct;
+ DEBUG("derive.xact",
+ "Set new posting's account to: " << acct->fullname());
} else {
-
- if (post.from)
+ if (post.from) {
new_post->account = journal.find_account(_("Liabilities:Unknown"));
- else
+ DEBUG("derive.xact",
+ "Set new posting's account to: Liabilities:Unknown");
+ } else {
new_post->account = journal.find_account(_("Expenses:Unknown"));
+ DEBUG("derive.xact",
+ "Set new posting's account to: Expenses:Unknown");
+ }
}
}
if (new_post.get() && ! new_post->amount.is_null()) {
found_commodity = &new_post->amount.commodity();
- if (any_post_has_amount)
+ if (any_post_has_amount) {
new_post->amount = amount_t();
- else
+ DEBUG("derive.xact", "New posting has an amount, but we cleared it");
+ } else {
any_post_has_amount = true;
+ DEBUG("derive.xact", "New posting has an amount, and we're using it");
+ }
}
if (post.amount) {
new_post->amount = *post.amount;
- if (post.from)
+ DEBUG("derive.xact", "Copied over posting amount");
+
+ if (post.from) {
new_post->amount.in_place_negate();
+ DEBUG("derive.xact", "Negated new posting amount");
+ }
}
if (found_commodity &&
! new_post->amount.is_null() &&
! new_post->amount.has_commodity()) {
new_post->amount.set_commodity(*found_commodity);
+ DEBUG("derive.xact", "Set posting amount commodity to: "
+ << new_post->amount.commodity());
+
new_post->amount = new_post->amount.rounded();
+ DEBUG("derive.xact",
+ "Rounded posting amount to: " << new_post->amount);
}
added->add_post(new_post.release());
added->posts.back()->set_state(item_t::UNCLEARED);
+
+ DEBUG("derive.xact", "Added new posting to derived entry");
}
}
if (! journal.xact_finalize_hooks.run_hooks(*added.get(), false) ||
! added->finalize() ||
- ! journal.xact_finalize_hooks.run_hooks(*added.get(), true))
+ ! journal.xact_finalize_hooks.run_hooks(*added.get(), true)) {
throw_(std::runtime_error,
_("Failed to finalize derived transaction (check commodities)"));
+ }
return added.release();
}
@@ -423,7 +502,8 @@ value_t xact_command(call_scope_t& args)
xact_template_t tmpl = args_to_xact_template(begin, end);
std::auto_ptr<xact_t> new_xact(derive_xact_from_template(tmpl, report));
- report.HANDLER(limit_).on("actual"); // jww (2009-02-27): make this more general
+ // jww (2009-02-27): make this more general
+ report.HANDLER(limit_).on(string("#xact"), "actual");
report.xact_report(post_handler_ptr
(new format_posts(report,
diff --git a/src/filters.cc b/src/filters.cc
index bb2681c6..ab7d4b74 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -388,6 +388,15 @@ void related_posts::flush()
item_handler<post_t>::flush();
}
+void changed_value_posts::flush()
+{
+ if (last_post && last_post->date() <= report.terminus) {
+ output_revaluation(last_post, report.terminus);
+ last_post = NULL;
+ }
+ item_handler<post_t>::flush();
+}
+
void changed_value_posts::output_revaluation(post_t * post, const date_t& date)
{
if (is_valid(date))
diff --git a/src/filters.h b/src/filters.h
index 14b97c23..050e3dcf 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -468,13 +468,7 @@ public:
clear_xacts_posts(xact_temps);
}
- virtual void flush() {
- if (last_post && last_post->date() <= CURRENT_DATE()) {
- output_revaluation(last_post, CURRENT_DATE());
- last_post = NULL;
- }
- item_handler<post_t>::flush();
- }
+ virtual void flush();
void output_revaluation(post_t * post, const date_t& current);
void output_rounding(post_t * post);
diff --git a/src/global.cc b/src/global.cc
index a258d0bb..38e0634e 100644
--- a/src/global.cc
+++ b/src/global.cc
@@ -204,6 +204,13 @@ void global_scope_t::execute_command(strings_list args, bool at_repl)
optional<path>(path(report().HANDLER(pager_).str())) :
optional<path>());
+ // Now that the output stream is initialized, report the options that will
+ // participate in this report, if the user specified --options
+
+ if (HANDLED(options)) {
+ report_options(report(), report().output_stream);
+ }
+
// Create an argument scope containing the report command's arguments, and
// then invoke the command. The bound scope causes lookups to happen
// first in the global scope, and then in the report scope.
@@ -239,6 +246,29 @@ int global_scope_t::execute_command_wrapper(strings_list args, bool at_repl)
return status;
}
+void global_scope_t::report_options(report_t& report, std::ostream& out)
+{
+ out << "<=============================================================================>"
+ << std::endl;
+ out << "[Global scope options]" << std::endl;
+
+ HANDLER(args_only).report(out);
+ HANDLER(debug_).report(out);
+ HANDLER(init_file_).report(out);
+ HANDLER(script_).report(out);
+ HANDLER(trace_).report(out);
+ HANDLER(verbose).report(out);
+ HANDLER(verify).report(out);
+
+ out << std::endl << "[Session scope options]" << std::endl;
+ report.session.report_options(out);
+
+ out << std::endl << "[Report scope options]" << std::endl;
+ report.report_options(out);
+ out << "<=============================================================================>"
+ << std::endl;
+}
+
option_t<global_scope_t> * global_scope_t::lookup_option(const char * p)
{
switch (*p) {
@@ -260,6 +290,9 @@ option_t<global_scope_t> * global_scope_t::lookup_option(const char * p)
case 'i':
OPT(init_file_);
break;
+ case 'o':
+ OPT(options);
+ break;
case 's':
OPT(script_);
break;
@@ -287,13 +320,13 @@ expr_t::ptr_op_t global_scope_t::lookup(const string& name)
break;
case 'p':
- if (WANT_PRECMD()) { p += PRECMD_PREFIX_LEN;
- switch (*p) {
+ if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN;
+ switch (*q) {
case 'p':
- if (is_eq(p, "push"))
- MAKE_FUNCTOR(global_scope_t::push_command);
- else if (is_eq(p, "pop"))
- MAKE_FUNCTOR(global_scope_t::pop_command);
+ if (is_eq(q, "push"))
+ return MAKE_FUNCTOR(global_scope_t::push_command);
+ else if (is_eq(q, "pop"))
+ return MAKE_FUNCTOR(global_scope_t::pop_command);
break;
}
}
@@ -316,18 +349,18 @@ void global_scope_t::read_environment_settings(char * envp[])
if (const char * p = std::getenv("LEDGER")) {
if (! std::getenv("LEDGER_FILE"))
- process_option("file", report(), p, "LEDGER");
+ process_option("environ", "file", report(), p, "LEDGER");
}
if (const char * p = std::getenv("LEDGER_INIT")) {
if (! std::getenv("LEDGER_INIT_FILE"))
- process_option("init-file", report(), p, "LEDGER_INIT");
+ process_option("environ", "init-file", report(), p, "LEDGER_INIT");
}
if (const char * p = std::getenv("PRICE_HIST")) {
if (! std::getenv("LEDGER_PRICEDB"))
- process_option("price-db", report(), p, "PRICE_HIST");
+ process_option("environ", "price-db", report(), p, "PRICE_HIST");
}
if (const char * p = std::getenv("PRICE_EXP"))
- process_option("price-exp", report(), p, "PRICE_EXP");
+ process_option("environ", "price-exp", report(), p, "PRICE_EXP");
#endif
TRACE_FINISH(environment, 1);
@@ -397,29 +430,29 @@ void global_scope_t::normalize_report_options(const string& verb)
// I might be able to do it with command objects, like register_t, which
// each know how to adjust the report based on its current option settings.
if (verb == "print" || verb == "xact" || verb == "dump") {
- rep.HANDLER(related).on_only();
- rep.HANDLER(related_all).on_only();
+ rep.HANDLER(related).on_only(string("?normalize"));
+ rep.HANDLER(related_all).on_only(string("?normalize"));
}
else if (verb == "equity") {
- rep.HANDLER(equity).on_only();
+ rep.HANDLER(equity).on_only(string("?normalize"));
}
else if (rep.HANDLED(related)) {
if (verb[0] == 'r') {
- rep.HANDLER(invert).on_only();
+ rep.HANDLER(invert).on_only(string("?normalize"));
} else {
- rep.HANDLER(subtotal).on_only();
- rep.HANDLER(related_all).on_only();
+ rep.HANDLER(subtotal).on_only(string("?normalize"));
+ rep.HANDLER(related_all).on_only(string("?normalize"));
}
}
if (! rep.HANDLED(empty))
- rep.HANDLER(display_).on("amount|(!post&total)");
+ rep.HANDLER(display_).on(string("?normalize"), "amount|(!post&total)");
if (verb[0] != 'b' && verb[0] != 'r')
- rep.HANDLER(base).on_only();
+ rep.HANDLER(base).on_only(string("?normalize"));
if (rep.HANDLED(period_) && ! rep.HANDLED(sort_all_))
- rep.HANDLER(sort_xacts_).on_only();
+ rep.HANDLER(sort_xacts_).on_only(string("?normalize"));
long cols = 0;
if (rep.HANDLED(columns_))
@@ -465,15 +498,15 @@ void global_scope_t::normalize_report_options(const string& verb)
}
if (! rep.HANDLER(date_width_).specified)
- rep.HANDLER(date_width_).on_with(date_width);
+ rep.HANDLER(date_width_).on_with(string("?normalize"), date_width);
if (! rep.HANDLER(payee_width_).specified)
- rep.HANDLER(payee_width_).on_with(payee_width);
+ rep.HANDLER(payee_width_).on_with(string("?normalize"), payee_width);
if (! rep.HANDLER(account_width_).specified)
- rep.HANDLER(account_width_).on_with(account_width);
+ rep.HANDLER(account_width_).on_with(string("?normalize"), account_width);
if (! rep.HANDLER(amount_width_).specified)
- rep.HANDLER(amount_width_).on_with(amount_width);
+ rep.HANDLER(amount_width_).on_with(string("?normalize"), amount_width);
if (! rep.HANDLER(total_width_).specified)
- rep.HANDLER(total_width_).on_with(total_width);
+ rep.HANDLER(total_width_).on_with(string("?normalize"), total_width);
}
}
diff --git a/src/global.h b/src/global.h
index 2ef5a545..df9845d5 100644
--- a/src/global.h
+++ b/src/global.h
@@ -113,6 +113,8 @@ See LICENSE file included with the distribution for details and disclaimer.");
out << std::endl;
}
+ void report_options(report_t& report, std::ostream& out);
+
option_t<global_scope_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
@@ -133,11 +135,12 @@ See LICENSE file included with the distribution for details and disclaimer.");
CTOR(global_scope_t, init_file_) {
if (const char * home_var = std::getenv("HOME"))
- on((path(home_var) / ".ledgerrc").string());
+ on(none, (path(home_var) / ".ledgerrc").string());
else
- on(path("./.ledgerrc").string());
+ on(none, path("./.ledgerrc").string());
});
+ OPTION(global_scope_t, options);
OPTION(global_scope_t, script_);
OPTION(global_scope_t, trace_);
OPTION(global_scope_t, verbose);
diff --git a/src/item.cc b/src/item.cc
index 758ce254..e54fa6be 100644
--- a/src/item.cc
+++ b/src/item.cc
@@ -258,10 +258,12 @@ value_t get_comment(item_t& item)
if (! item.note) {
return string_value("");
} else {
- // jww (2009-03-01): If the comment is a short one-liner, put it at the
- // end of the post/xact
std::ostringstream buf;
- buf << "\n ;";
+ if (item.note->length() > 15)
+ buf << "\n ;";
+ else
+ buf << " ;";
+
bool need_separator = false;
for (const char * p = item.note->c_str(); *p; p++) {
if (*p == '\n') {
diff --git a/src/option.cc b/src/option.cc
index 7d5b272d..1275e270 100644
--- a/src/option.cc
+++ b/src/option.cc
@@ -76,11 +76,13 @@ namespace {
return op_bool_tuple(scope.lookup(buf), false);
}
- void process_option(const function_t& opt, scope_t& scope,
- const char * arg, const string& name)
+ void process_option(const string& whence, const function_t& opt,
+ scope_t& scope, const char * arg, const string& name)
{
try {
call_scope_t args(scope);
+
+ args.push_back(string_value(whence));
if (arg)
args.push_back(string_value(arg));
@@ -97,12 +99,12 @@ namespace {
}
}
-void process_option(const string& name, scope_t& scope,
+void process_option(const string& whence, const string& name, scope_t& scope,
const char * arg, const string& varname)
{
op_bool_tuple opt(find_option(scope, name));
if (opt.first)
- process_option(opt.first->as_function(), scope, arg, varname);
+ process_option(whence, opt.first->as_function(), scope, arg, varname);
}
void process_environment(const char ** envp, const string& tag,
@@ -129,7 +131,7 @@ void process_environment(const char ** envp, const string& tag,
try {
string value = string(*p, q - *p);
if (! value.empty())
- process_option(string(buf), scope, q + 1, value);
+ process_option(string("$") + buf, string(buf), scope, q + 1, value);
}
catch (const std::exception& err) {
add_error_context(_("While parsing environment variable option '%1':")
@@ -201,7 +203,8 @@ strings_list process_arguments(strings_list args, scope_t& scope)
if (value == NULL)
throw_(option_error, _("Missing option argument for --%1") << name);
}
- process_option(opt.first->as_function(), scope, value,
+ process_option(string("--") + name,
+ opt.first->as_function(), scope, value,
string("--") + name);
}
else if ((*i)[1] == '\0') {
@@ -230,7 +233,8 @@ strings_list process_arguments(strings_list args, scope_t& scope)
throw_(option_error,
_("Missing option argument for -%1") << o.ch);
}
- process_option(o.op->as_function(), scope, value, string("-") + o.ch);
+ process_option(string("-") + o.ch, o.op->as_function(), scope, value,
+ string("-") + o.ch);
}
}
}
diff --git a/src/option.h b/src/option.h
index 86899931..c9b05d87 100644
--- a/src/option.h
+++ b/src/option.h
@@ -56,10 +56,11 @@ template <typename T>
class option_t
{
protected:
- const char * name;
- std::size_t name_len;
- const char ch;
- bool handled;
+ const char * name;
+ std::size_t name_len;
+ const char ch;
+ bool handled;
+ optional<string> source;
option_t& operator=(const option_t&);
@@ -90,6 +91,18 @@ public:
TRACE_DTOR(option_t);
}
+ void report(std::ostream& out) const {
+ if (handled && source) {
+ if (wants_arg) {
+ out << desc() << " => ";
+ value.dump(out);
+ } else {
+ out << desc();
+ }
+ out << " <" << *source << ">" << std::endl;
+ }
+ }
+
string desc() const {
std::ostringstream out;
out << "--";
@@ -117,31 +130,42 @@ public:
return value.as_string_lval();
}
- void on_only() {
+ string str() const {
+ assert(handled);
+ if (! value)
+ throw_(std::runtime_error, _("No argument provided for %1") << desc());
+ return value.as_string();
+ }
+
+ void on_only(const optional<string>& whence) {
handled = true;
+ source = whence;
}
- void on(const string& str) {
- on_with(string_value(str));
+ void on(const optional<string>& whence, const string& str) {
+ on_with(whence, string_value(str));
}
- virtual void on_with(const value_t& val) {
+ virtual void on_with(const optional<string>& whence,
+ const value_t& val) {
handled = true;
value = val;
+ source = whence;
}
void off() {
handled = false;
value = value_t();
+ source = none;
}
virtual void handler_thunk(call_scope_t&) {}
virtual void handler(call_scope_t& args) {
if (wants_arg) {
- if (args.empty())
+ if (args.empty() || args.size() == 1)
throw_(std::runtime_error, _("No argument provided for %1") << desc());
- on_with(args[0]);
+ on_with(args[0].as_string(), args[1]);
} else {
- on_only();
+ on_only(args[0].as_string());
}
handler_thunk(args);
@@ -270,7 +294,7 @@ inline bool is_eq(const char * p, const char * n) {
#define WANT_DIR() \
(std::strncmp(p, DIR_PREFIX, DIR_PREFIX_LEN) == 0)
-void process_option(const string& name, scope_t& scope,
+void process_option(const string& whence, const string& name, scope_t& scope,
const char * arg, const string& varname);
void process_environment(const char ** envp, const string& tag,
diff --git a/src/output.cc b/src/output.cc
index 63d8c919..c4317934 100644
--- a/src/output.cc
+++ b/src/output.cc
@@ -179,7 +179,9 @@ format_accounts::mark_accounts(account_t& account, const bool flat)
(account.has_flags(ACCOUNT_EXT_VISITED) || (! flat && visited > 0))) {
bind_scope_t bound_scope(report, account);
if ((! flat && to_display > 1) ||
- (disp_pred(bound_scope) && (flat || to_display != 1))) {
+ ((flat || to_display != 1 ||
+ account.has_flags(ACCOUNT_EXT_VISITED)) &&
+ disp_pred(bound_scope))) {
account.xdata().add_flags(ACCOUNT_EXT_TO_DISPLAY);
DEBUG("account.display", "Marking account as TO_DISPLAY");
to_display = 1;
diff --git a/src/report.cc b/src/report.cc
index be1e47ee..fde3365b 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -55,12 +55,15 @@ void report_t::posts_report(post_handler_ptr handler)
void report_t::generate_report(post_handler_ptr handler)
{
- HANDLER(limit_).on("actual"); // jww (2009-02-27): make this more general
+ // jww (2009-02-27): make this more general
+ HANDLER(limit_).on(string("#generate"), "actual");
+
generate_posts_iterator walker
(session, HANDLED(seed_) ?
static_cast<unsigned int>(HANDLER(seed_).value.to_long()) : 0,
HANDLED(head_) ?
static_cast<unsigned int>(HANDLER(head_).value.to_long()) : 50);
+
pass_down_posts(chain_post_handlers(*this, handler), walker);
}
@@ -227,13 +230,14 @@ value_t report_t::fn_truncated(call_scope_t& scope)
value_t report_t::fn_justify(call_scope_t& scope)
{
- interactive_t args(scope, "vl&lbs");
+ interactive_t args(scope, "vl&lbbs");
std::ostringstream out;
args.value_at(0)
.print(out, args.get<long>(1),
args.has(2) ? args.get<long>(2) : -1,
- args.has(3),
- args.has(4) ? args.get<string>(4) :
+ args.has(3) ? args.get<bool>(3) : false,
+ args.has(4) ? args.get<bool>(4) : false,
+ args.has(5) ? args.get<string>(5) :
(HANDLED(date_format_) ?
HANDLER(date_format_).str() : optional<string>()));
return string_value(out.str());
@@ -349,10 +353,12 @@ namespace {
shared_ptr<item_handler<Type> > handler;
report_t& report;
+ string whence;
public:
- reporter(item_handler<Type> * _handler, report_t& _report)
- : handler(_handler), report(_report) {}
+ reporter(item_handler<Type> * _handler, report_t& _report,
+ const string& _whence)
+ : handler(_handler), report(_report), whence(_whence) {}
value_t operator()(call_scope_t& args)
{
@@ -365,7 +371,7 @@ namespace {
string limit = args_to_predicate_expr(begin, end);
if (! limit.empty())
- report.HANDLER(limit_).on(limit);
+ report.HANDLER(limit_).on(whence, limit);
DEBUG("report.predicate",
"Predicate = " << report.HANDLER(limit_).str());
@@ -375,7 +381,7 @@ namespace {
display = args_to_predicate_expr(begin, end);
if (! display.empty())
- report.HANDLER(display_).on(display);
+ report.HANDLER(display_).on(whence, display);
DEBUG("report.predicate",
"Display predicate = " << report.HANDLER(display_).str());
@@ -675,7 +681,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(balance_format_))),
- *this));
+ *this, "#balance"));
break;
case 'c':
@@ -683,7 +689,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(csv_format_))),
- *this));
+ *this, "#csv"));
break;
case 'e':
@@ -691,12 +697,12 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(print_format_))),
- *this));
+ *this, "#equity"));
else if (is_eq(q, "xact") || is_eq(q, "entry"))
return WRAP_FUNCTOR(xact_command);
else if (is_eq(q, "emacs"))
return WRAP_FUNCTOR
- (reporter<>(new format_emacs_posts(output_stream), *this));
+ (reporter<>(new format_emacs_posts(output_stream), *this, "#emacs"));
break;
case 'p':
@@ -704,17 +710,17 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(print_format_)),
- HANDLED(raw)), *this));
+ HANDLED(raw)), *this, "#print"));
else if (is_eq(q, "prices"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(prices_format_))),
- *this));
+ *this, "#prices"));
else if (is_eq(q, "pricesdb"))
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_))),
- *this));
+ *this, "#pricesdb"));
else if (is_eq(q, "python") && maybe_import("ledger.interp"))
return session.lookup(string(CMD_PREFIX) + "python");
break;
@@ -724,7 +730,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(register_format_))),
- *this));
+ *this, "#register"));
else if (is_eq(q, "reload"))
return MAKE_FUNCTOR(report_t::reload_command);
break;
@@ -747,6 +753,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return MAKE_FUNCTOR(report_t::fn_display_amount);
else if (is_eq(p, "display_total"))
return MAKE_FUNCTOR(report_t::fn_display_total);
+ else if (is_eq(p, "date"))
+ return MAKE_FUNCTOR(report_t::fn_today);
break;
case 'f':
@@ -783,6 +791,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
case 'n':
if (is_eq(p, "null"))
return WRAP_FUNCTOR(fn_null);
+#if 0
+ else if (is_eq(p, "now"))
+ return MAKE_FUNCTOR(report_t::fn_now);
+#endif
break;
case 'o':
@@ -815,7 +827,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::generate_report>
(new format_posts(*this, report_format(HANDLER(print_format_)),
- false), *this));
+ false), *this, "#generate"));
case 'h':
if (is_eq(q, "hello") && maybe_import("ledger.hello"))
return session.lookup(string(PRECMD_PREFIX) + "hello");
@@ -862,6 +874,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return MAKE_FUNCTOR(report_t::fn_truncated);
else if (is_eq(p, "total_expr"))
return MAKE_FUNCTOR(report_t::fn_total_expr);
+ else if (is_eq(p, "today"))
+ return MAKE_FUNCTOR(report_t::fn_today);
break;
case 'u':
diff --git a/src/report.h b/src/report.h
index 5eb2a706..d48b5a78 100644
--- a/src/report.h
+++ b/src/report.h
@@ -119,10 +119,12 @@ public:
#define BUDGET_BUDGETED 0x01
#define BUDGET_UNBUDGETED 0x02
+ date_t terminus;
uint_least8_t budget_flags;
explicit report_t(session_t& _session)
- : session(_session), budget_flags(BUDGET_NO_BUDGET) {}
+ : session(_session), terminus(CURRENT_DATE()),
+ budget_flags(BUDGET_NO_BUDGET) {}
virtual ~report_t() {
output_stream.close();
@@ -154,6 +156,15 @@ public:
value_t fn_format_date(call_scope_t& scope);
value_t fn_ansify_if(call_scope_t& scope);
+#if 0
+ value_t fn_now(call_scope_t&) {
+ return CURRENT_TIME();
+ }
+#endif
+ value_t fn_today(call_scope_t&) {
+ return terminus;
+ }
+
value_t fn_options(call_scope_t&) {
return value_t(static_cast<scope_t *>(this));
}
@@ -176,6 +187,113 @@ public:
bool maybe_import(const string& module);
+ void report_options(std::ostream& out)
+ {
+ HANDLER(abbrev_len_).report(out);
+ HANDLER(account_).report(out);
+ HANDLER(actual).report(out);
+ HANDLER(add_budget).report(out);
+ HANDLER(amount_).report(out);
+ HANDLER(amount_data).report(out);
+ HANDLER(anon).report(out);
+ HANDLER(average).report(out);
+ HANDLER(balance_format_).report(out);
+ HANDLER(base).report(out);
+ HANDLER(basis).report(out);
+ HANDLER(begin_).report(out);
+ HANDLER(budget).report(out);
+ HANDLER(by_payee).report(out);
+ HANDLER(cleared).report(out);
+ HANDLER(code_as_payee).report(out);
+ HANDLER(comm_as_payee).report(out);
+ HANDLER(code_as_account).report(out);
+ HANDLER(comm_as_account).report(out);
+ HANDLER(color).report(out);
+ HANDLER(collapse).report(out);
+ HANDLER(collapse_if_zero).report(out);
+ HANDLER(columns_).report(out);
+ HANDLER(csv_format_).report(out);
+ HANDLER(current).report(out);
+ HANDLER(daily).report(out);
+ HANDLER(date_format_).report(out);
+ HANDLER(depth_).report(out);
+ HANDLER(deviation).report(out);
+ HANDLER(display_).report(out);
+ HANDLER(display_amount_).report(out);
+ HANDLER(display_total_).report(out);
+ HANDLER(dow).report(out);
+ HANDLER(effective).report(out);
+ HANDLER(empty).report(out);
+ HANDLER(end_).report(out);
+ HANDLER(equity).report(out);
+ HANDLER(exact).report(out);
+ HANDLER(exchange_).report(out);
+ HANDLER(flat).report(out);
+ HANDLER(forecast_while_).report(out);
+ HANDLER(format_).report(out);
+ HANDLER(gain).report(out);
+ HANDLER(head_).report(out);
+ HANDLER(invert).report(out);
+ HANDLER(limit_).report(out);
+ HANDLER(lot_dates).report(out);
+ HANDLER(lot_prices).report(out);
+ HANDLER(lot_tags).report(out);
+ HANDLER(lots).report(out);
+ HANDLER(lots_actual).report(out);
+ HANDLER(market).report(out);
+ HANDLER(monthly).report(out);
+ HANDLER(no_total).report(out);
+ HANDLER(only_).report(out);
+ HANDLER(output_).report(out);
+ HANDLER(pager_).report(out);
+ HANDLER(payee_as_account).report(out);
+ HANDLER(pending).report(out);
+ HANDLER(percentage).report(out);
+ HANDLER(period_).report(out);
+ HANDLER(period_sort_).report(out);
+ HANDLER(plot_amount_format_).report(out);
+ HANDLER(plot_total_format_).report(out);
+ HANDLER(price).report(out);
+ HANDLER(price_exp_).report(out);
+ HANDLER(prices_format_).report(out);
+ HANDLER(pricesdb_format_).report(out);
+ HANDLER(print_format_).report(out);
+ HANDLER(quantity).report(out);
+ HANDLER(quarterly).report(out);
+ HANDLER(raw).report(out);
+ HANDLER(real).report(out);
+ HANDLER(register_format_).report(out);
+ HANDLER(related).report(out);
+ HANDLER(related_all).report(out);
+ HANDLER(revalued).report(out);
+ HANDLER(revalued_only).report(out);
+ HANDLER(revalued_total_).report(out);
+ HANDLER(seed_).report(out);
+ HANDLER(set_account_).report(out);
+ HANDLER(set_payee_).report(out);
+ HANDLER(set_price_).report(out);
+ HANDLER(sort_).report(out);
+ HANDLER(sort_all_).report(out);
+ HANDLER(sort_xacts_).report(out);
+ HANDLER(start_of_week_).report(out);
+ HANDLER(subtotal).report(out);
+ HANDLER(tail_).report(out);
+ HANDLER(total_).report(out);
+ HANDLER(total_data).report(out);
+ HANDLER(truncate_).report(out);
+ HANDLER(unbudgeted).report(out);
+ HANDLER(uncleared).report(out);
+ HANDLER(unround).report(out);
+ HANDLER(weekly).report(out);
+ HANDLER(wide).report(out);
+ HANDLER(yearly).report(out);
+ HANDLER(date_width_).report(out);
+ HANDLER(payee_width_).report(out);
+ HANDLER(account_width_).report(out);
+ HANDLER(amount_width_).report(out);
+ HANDLER(total_width_).report(out);
+ }
+
option_t<report_t> * lookup_option(const char * p);
virtual void define(const string& name, expr_t::ptr_op_t def);
@@ -187,11 +305,11 @@ public:
*/
OPTION__(report_t, abbrev_len_,
- CTOR(report_t, abbrev_len_) { on_with(2L); });
+ CTOR(report_t, abbrev_len_) { on_with(none, 2L); });
OPTION(report_t, account_);
OPTION_(report_t, actual, DO() { // -L
- parent->HANDLER(limit_).on("actual");
+ parent->HANDLER(limit_).on(string("--actual"), "actual");
});
OPTION_(report_t, add_budget, DO() {
@@ -202,53 +320,54 @@ public:
(report_t, amount_, // -t
expr_t expr;
CTOR(report_t, amount_) {
- set_expr("amount");
+ set_expr(none, "amount");
}
- void set_expr(const string& str) {
+ void set_expr(const optional<string>& whence, const string& str) {
expr = str;
- on(str);
+ on(whence, str);
}
DO_(args) {
- set_expr(args[0].to_string());
+ set_expr(args[0].to_string(), args[1].to_string());
});
OPTION_(report_t, amount_data, DO() { // -j
- parent->HANDLER(format_).on_with(parent->HANDLER(plot_amount_format_).value);
+ parent->HANDLER(format_)
+ .on_with(none, parent->HANDLER(plot_amount_format_).value);
});
OPTION(report_t, anon);
OPTION_(report_t, average, DO() { // -A
- parent->HANDLER(display_total_).set_expr("total_expr/count");
+ parent->HANDLER(display_total_).set_expr(string("--average"),
+ "total_expr/count");
});
OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) {
- on("%(ansify_if(justify(scrub(display_total), 20, -1, true), "
- " red if color & scrub(display_total) < 0))"
+ on(none,
+ "%(justify(scrub(display_total), 20, -1, true, color))"
" %(!options.flat ? depth_spacer : \"\")"
"%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
- "%(ansify_if(justify(scrub(display_total), 20, -1, true), "
- " red if color & scrub(display_total) < 0))\n%/"
+ "%(justify(scrub(display_total), 20, -1, true, color))\n%/"
"--------------------\n");
});
OPTION(report_t, base);
OPTION_(report_t, basis, DO() { // -B
- parent->HANDLER(revalued).on_only();
- parent->HANDLER(amount_).set_expr("rounded(cost)");
+ parent->HANDLER(revalued).on_only(string("--basis"));
+ parent->HANDLER(amount_).set_expr(string("--basis"), "rounded(cost)");
});
OPTION_(report_t, begin_, DO_(args) { // -b
- date_interval_t interval(args[0].to_string());
+ date_interval_t interval(args[1].to_string());
if (! interval.start)
throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'")
- << args[0].to_string());
+ << args[1].to_string());
string predicate =
"date>=[" + to_iso_extended_string(*interval.start) + "]";
- parent->HANDLER(limit_).on(predicate);
+ parent->HANDLER(limit_).on(string("--begin"), predicate);
});
OPTION_(report_t, budget, DO() {
@@ -258,7 +377,7 @@ public:
OPTION(report_t, by_payee); // -P
OPTION_(report_t, cleared, DO() { // -C
- parent->HANDLER(limit_).on("cleared");
+ parent->HANDLER(limit_).on(string("--cleared"), "cleared");
});
OPTION(report_t, code_as_payee);
@@ -270,17 +389,18 @@ public:
OPTION_(report_t, collapse, DO() { // -n
// Make sure that balance reports are collapsed too, but only apply it
// to account xacts
- parent->HANDLER(display_).on("post|depth<=1");
+ parent->HANDLER(display_).on(string("--collapse"), "post|depth<=1");
});
OPTION_(report_t, collapse_if_zero, DO() {
- parent->HANDLER(collapse).on_only();
+ parent->HANDLER(collapse).on_only(string("--collapse-if-zero"));
});
OPTION(report_t, columns_);
OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) {
- on("%(quoted(date)),"
+ on(none,
+ "%(quoted(date)),"
"%(quoted(payee)),"
"%(quoted(account)),"
"%(quoted(scrub(display_amount))),"
@@ -290,35 +410,38 @@ public:
});
OPTION_(report_t, current, DO() { // -c
- parent->HANDLER(limit_).on("date<=today");
+ parent->HANDLER(limit_).on(string("--current"), "date<=today");
});
OPTION_(report_t, daily, DO() {
- parent->HANDLER(period_).on("daily");
+ parent->HANDLER(period_).on(string("--daily"), "daily");
});
OPTION__(report_t, date_format_, // -y
CTOR(report_t, date_format_) {
- on("%y-%b-%d");
+ on(none, "%y-%b-%d");
});
OPTION_(report_t, depth_, DO_(scope) {
- interactive_t args(scope, "l");
- parent->HANDLER(display_).on(string("depth<=") + args.get<string>(0));
+ interactive_t args(scope, "sl");
+ parent->HANDLER(display_).on(string("--depth"),
+ string("depth<=") + args.get<string>(1));
});
OPTION_(report_t, deviation, DO() { // -D
- parent->HANDLER(display_total_).set_expr("amount_expr-total_expr/count");
+ parent->HANDLER(display_total_).set_expr(string("--deviation"),
+ "amount_expr-total_expr/count");
});
OPTION__
(report_t, display_, // -d
CTOR(report_t, display_) {}
- virtual void on_with(const value_t& text) {
+ virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
- option_t<report_t>::on_with(text);
+ option_t<report_t>::on_with(whence, text);
else
- option_t<report_t>::on_with(string_value(string("(") + str() + ")&(" +
+ option_t<report_t>::on_with(whence,
+ string_value(string("(") + str() + ")&(" +
text.as_string() + ")"));
});
@@ -326,28 +449,28 @@ public:
(report_t, display_amount_,
expr_t expr;
CTOR(report_t, display_amount_) {
- set_expr("amount_expr");
+ set_expr(none, "amount_expr");
}
- void set_expr(const string& str) {
+ void set_expr(const optional<string>& whence, const string& str) {
expr = str;
- on(str);
+ on(whence, str);
}
DO_(args) {
- set_expr(args[0].to_string());
+ set_expr(args[0].to_string(), args[1].to_string());
});
OPTION__
(report_t, display_total_,
expr_t expr;
CTOR(report_t, display_total_) {
- set_expr("total_expr");
+ set_expr(none, "total_expr");
}
- void set_expr(const string& str) {
+ void set_expr(const optional<string>& whence, const string& str) {
expr = str;
- on(str);
+ on(whence, str);
}
DO_(args) {
- set_expr(args[0].to_string());
+ set_expr(args[0].to_string(), args[1].to_string());
});
OPTION(report_t, dow);
@@ -355,26 +478,26 @@ public:
OPTION(report_t, empty); // -E
OPTION_(report_t, end_, DO_(args) { // -e
- date_interval_t interval(args[0].to_string());
+ date_interval_t interval(args[1].to_string());
if (! interval.start)
throw_(std::invalid_argument,
_("Could not determine end of period '%1'")
- << args[0].to_string());
+ << args[1].to_string());
string predicate =
"date<[" + to_iso_extended_string(*interval.start) + "]";
- parent->HANDLER(limit_).on(predicate);
-#if 0
- terminus = interval.begin;
-#endif
+ parent->HANDLER(limit_).on(string("--end"), predicate);
+
+ parent->terminus = *interval.start;
});
OPTION(report_t, equity);
OPTION(report_t, exact);
OPTION_(report_t, exchange_, DO_(args) { // -X
- on_with(args[0]);
+ on_with(args[0].as_string(), args[1]);
call_scope_t no_args(*parent);
+ no_args.push_back(args[0]);
parent->HANDLER(market).parent = parent;
parent->HANDLER(market).handler(no_args);
});
@@ -384,21 +507,24 @@ public:
OPTION(report_t, format_); // -F
OPTION_(report_t, gain, DO() { // -G
- parent->HANDLER(revalued).on_only();
- parent->HANDLER(amount_).set_expr("(amount, cost)");
+ parent->HANDLER(revalued).on_only(string("--gain"));
+ parent->HANDLER(amount_).set_expr(string("--gain"), "(amount, cost)");
// Since we are displaying the amounts of revalued postings, they
// will end up being composite totals, and hence a pair of pairs.
parent->HANDLER(display_amount_)
- .set_expr("use_direct_amount ? amount :"
+ .set_expr(string("--gain"),
+ "use_direct_amount ? amount :"
" (is_seq(get_at(amount_expr, 0)) ?"
" get_at(get_at(amount_expr, 0), 0) :"
" market(get_at(amount_expr, 0), date, exchange)"
" - get_at(amount_expr, 1))");
parent->HANDLER(revalued_total_)
- .set_expr("(market(get_at(total_expr, 0), date, exchange), "
+ .set_expr(string("--gain"),
+ "(market(get_at(total_expr, 0), date, exchange), "
"get_at(total_expr, 1))");
parent->HANDLER(display_total_)
- .set_expr("use_direct_amount ? total_expr :"
+ .set_expr(string("--gain"),
+ "use_direct_amount ? total_expr :"
" market(get_at(total_expr, 0), date, exchange)"
" - get_at(total_expr, 1)");
});
@@ -406,17 +532,18 @@ public:
OPTION(report_t, head_);
OPTION_(report_t, invert, DO() {
- parent->HANDLER(amount_).set_expr("-amount");
+ parent->HANDLER(amount_).set_expr(string("--invert"), "-amount");
});
OPTION__
(report_t, limit_, // -l
CTOR(report_t, limit_) {}
- virtual void on_with(const value_t& text) {
+ virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
- option_t<report_t>::on_with(text);
+ option_t<report_t>::on_with(whence, text);
else
- option_t<report_t>::on_with(string_value(string("(") + str() + ")&(" +
+ option_t<report_t>::on_with(whence,
+ string_value(string("(") + str() + ")&(" +
text.as_string() + ")"));
});
@@ -427,15 +554,15 @@ public:
OPTION(report_t, lots_actual);
OPTION_(report_t, market, DO() { // -V
- parent->HANDLER(revalued).on_only();
+ parent->HANDLER(revalued).on_only(string("--market"));
parent->HANDLER(display_amount_)
- .set_expr("market(amount_expr, date, exchange)");
+ .set_expr(string("--market"), "market(amount_expr, date, exchange)");
parent->HANDLER(display_total_)
- .set_expr("market(total_expr, date, exchange)");
+ .set_expr(string("--market"), "market(total_expr, date, exchange)");
});
OPTION_(report_t, monthly, DO() { // -M
- parent->HANDLER(period_).on("monthly");
+ parent->HANDLER(period_).on(string("--monthly"), "monthly");
});
OPTION(report_t, no_total);
@@ -443,11 +570,12 @@ public:
OPTION__
(report_t, only_,
CTOR(report_t, only_) {}
- virtual void on_with(const value_t& text) {
+ virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
- option_t<report_t>::on_with(text);
+ option_t<report_t>::on_with(whence, text);
else
- option_t<report_t>::on_with(string_value(string("(") + str() + ")&(" +
+ option_t<report_t>::on_with(whence,
+ string_value(string("(") + str() + ")&(" +
text.as_string() + ")"));
});
@@ -456,7 +584,7 @@ public:
OPTION(report_t, payee_as_account);
OPTION_(report_t, pending, DO() { // -C
- parent->HANDLER(limit_).on("pending");
+ parent->HANDLER(limit_).on(string("--pending"), "pending");
});
OPTION(report_t, percentage); // -%
@@ -464,40 +592,46 @@ public:
OPTION__
(report_t, period_, // -p
CTOR(report_t, period_) {}
- virtual void on_with(const value_t& text) {
+ virtual void on_with(const optional<string>& whence, const value_t& text) {
if (! handled)
- option_t<report_t>::on_with(text);
+ option_t<report_t>::on_with(whence, text);
else
- option_t<report_t>::on_with(string_value(text.as_string() + " " + str()));
+ option_t<report_t>::on_with(whence,
+ string_value(text.as_string() + " " + str()));
});
OPTION(report_t, period_sort_);
OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) {
- on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
+ on(none,
+ "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
});
OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) {
- on("%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
+ on(none,
+ "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
});
OPTION_(report_t, price, DO() { // -I
parent->HANDLER(revalued).off();
- parent->HANDLER(amount_).set_expr("price");
+ parent->HANDLER(amount_).set_expr(string("--price"), "price");
});
OPTION(report_t, price_exp_); // -Z
OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) {
- on("%-.9(date) %-8(account) %12(scrub(display_amount))\n");
+ on(none,
+ "%-.9(date) %-8(account) %12(scrub(display_amount))\n");
});
OPTION__(report_t, pricesdb_format_, CTOR(report_t, pricesdb_format_) {
- on("P %[%Y/%m/%d %H:%M:%S] %A %t\n");
+ on(none,
+ "P %[%Y/%m/%d %H:%M:%S] %A %t\n");
});
OPTION__(report_t, print_format_, CTOR(report_t, print_format_) {
- on("%(format_date(xact.date, \"%Y/%m/%d\"))"
+ on(none,
+ "%(format_date(xact.date, \"%Y/%m/%d\"))"
"%(!effective & xact.effective_date ?"
" \"=\" + format_date(xact.effective_date, \"%Y/%m/%d\") : \"\")"
"%(xact.cleared ? \" *\" : (xact.pending ? \" !\" : \"\"))"
@@ -521,41 +655,42 @@ public:
OPTION_(report_t, quantity, DO() { // -O
parent->HANDLER(revalued).off();
- parent->HANDLER(amount_).set_expr("amount");
- parent->HANDLER(total_).set_expr("total");
+ parent->HANDLER(amount_).set_expr(string("--quantity"), "amount");
+ parent->HANDLER(total_).set_expr(string("--quantity"), "total");
});
OPTION_(report_t, quarterly, DO() {
- parent->HANDLER(period_).on("quarterly");
+ parent->HANDLER(period_).on(string("--quarterly"), "quarterly");
});
OPTION(report_t, raw);
OPTION_(report_t, real, DO() { // -R
- parent->HANDLER(limit_).on("real");
+ parent->HANDLER(limit_).on(string("--real"), "real");
});
OPTION__(report_t, register_format_, CTOR(report_t, register_format_) {
- on("%(ansify_if(justify(date, date_width), green if color & date > today))"
+ on(none,
+ "%(ansify_if(justify(date, date_width), green if color & date > today))"
" %(ansify_if(justify(truncated(payee, payee_width), payee_width), "
" bold if color & !cleared))"
" %(ansify_if(justify(truncated(account, account_width, abbrev_len), "
" account_width), blue if color))"
- " %(ansify_if(justify(scrub(display_amount), amount_width, "
- " 3 + date_width + payee_width + account_width + amount_width, true), "
- " red if color & scrub(display_amount) < 0))"
- " %(ansify_if(justify(scrub(display_total), total_width, "
+ " %(justify(scrub(display_amount), amount_width, "
+ " 3 + date_width + payee_width + account_width + amount_width, "
+ " true, color))"
+ " %(justify(scrub(display_total), total_width, "
" 4 + date_width + payee_width + account_width + amount_width "
- " + total_width, true), red if color & scrub(display_amount) < 0))\n%/"
+ " + total_width, true, color))\n%/"
"%(justify(\" \", 2 + date_width + payee_width))"
"%(ansify_if(justify(truncated(account, account_width, abbrev_len), "
" account_width), blue if color))"
- " %(ansify_if(justify(scrub(display_amount), amount_width, "
- " 3 + date_width + payee_width + account_width + amount_width, true), "
- " red if color & scrub(display_amount) < 0))"
- " %(ansify_if(justify(scrub(display_total), total_width, "
+ " %(justify(scrub(display_amount), amount_width, "
+ " 3 + date_width + payee_width + account_width + amount_width, "
+ " true, color))"
+ " %(justify(scrub(display_total), total_width, "
" 4 + date_width + payee_width + account_width + amount_width "
- " + total_width, true), red if color & scrub(display_amount) < 0))\n");
+ " + total_width, true, color))\n");
});
OPTION(report_t, related); // -r
@@ -567,12 +702,12 @@ public:
(report_t, revalued_total_,
expr_t expr;
CTOR(report_t, revalued_total_) {}
- void set_expr(const string& str) {
+ void set_expr(const optional<string>& whence, const string& str) {
expr = str;
- on(str);
+ on(whence, str);
}
DO_(args) {
- set_expr(args[0].to_string());
+ set_expr(args[0].to_string(), args[1].to_string());
});
OPTION(report_t, seed_);
@@ -581,18 +716,18 @@ public:
OPTION(report_t, set_price_);
OPTION_(report_t, sort_, DO_(args) { // -S
- on_with(args[0]);
+ on_with(args[0].as_string(), args[1]);
parent->HANDLER(sort_xacts_).off();
parent->HANDLER(sort_all_).off();
});
OPTION_(report_t, sort_all_, DO_(args) {
- parent->HANDLER(sort_).on_with(args[0]);
+ parent->HANDLER(sort_).on_with(string("--sort-all"), args[1]);
parent->HANDLER(sort_xacts_).off();
});
OPTION_(report_t, sort_xacts_, DO_(args) {
- parent->HANDLER(sort_).on_with(args[0]);
+ parent->HANDLER(sort_).on_with(string("--sort-xacts"), args[1]);
parent->HANDLER(sort_all_).off();
});
@@ -604,23 +739,24 @@ public:
(report_t, total_, // -T
expr_t expr;
CTOR(report_t, total_) {
- set_expr("total");
+ set_expr(none, "total");
}
- void set_expr(const string& str) {
+ void set_expr(const optional<string>& whence, const string& str) {
expr = str;
- on(str);
+ on(whence, str);
}
DO_(args) {
- set_expr(args[0].to_string());
+ set_expr(args[0].to_string(), args[1].to_string());
});
OPTION_(report_t, total_data, DO() { // -J
- parent->HANDLER(format_).on_with(parent->HANDLER(plot_total_format_).value);
+ parent->HANDLER(format_).on_with(string("--total-data"),
+ parent->HANDLER(plot_total_format_).value);
});
OPTION_(report_t, truncate_, DO() {
#if 0
- string style(args[0].to_string());
+ string style(args[1].to_string());
if (style == "leading")
format_t::elision_style = format_t::TRUNCATE_LEADING;
else if (style == "middle")
@@ -637,54 +773,70 @@ public:
});
OPTION_(report_t, uncleared, DO() { // -U
- parent->HANDLER(limit_).on("uncleared|pending");
+ parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending");
});
OPTION_(report_t, unround, DO() {
- parent->HANDLER(amount_).set_expr("unrounded(amount)");
+ parent->HANDLER(amount_).set_expr(string("--uncleared"),
+ "unrounded(amount)");
});
OPTION_(report_t, weekly, DO() { // -W
- parent->HANDLER(period_).on("weekly");
+ parent->HANDLER(period_).on(string("--weekly"), "weekly");
});
OPTION_(report_t, wide, DO() { // -w
- parent->HANDLER(date_width_).on_with(9L);
+ parent->HANDLER(date_width_).on_with(string("--wide"), 9L);
parent->HANDLER(date_width_).specified = true;
- parent->HANDLER(payee_width_).on_with(35L);
+ parent->HANDLER(payee_width_).on_with(string("--wide"), 35L);
parent->HANDLER(payee_width_).specified = true;
- parent->HANDLER(account_width_).on_with(39L);
+ parent->HANDLER(account_width_).on_with(string("--wide"), 39L);
parent->HANDLER(account_width_).specified = true;
- parent->HANDLER(amount_width_).on_with(22L);
+ parent->HANDLER(amount_width_).on_with(string("--wide"), 22L);
parent->HANDLER(amount_width_).specified = true;
- parent->HANDLER(total_width_).on_with(22L);
+ parent->HANDLER(total_width_).on_with(string("--wide"), 22L);
parent->HANDLER(total_width_).specified = true;
});
OPTION_(report_t, yearly, DO() { // -Y
- parent->HANDLER(period_).on("yearly");
+ parent->HANDLER(period_).on(string("--yearly"), "yearly");
});
OPTION__(report_t, date_width_,
bool specified;
- CTOR(report_t, date_width_) { on_with(9L); specified = false; }
- DO_(args) { value = args[0].to_long(); specified = true; });
+ CTOR(report_t, date_width_) {
+ on_with(none, 9L);
+ specified = false;
+ }
+ DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, payee_width_,
bool specified;
- CTOR(report_t, payee_width_) { on_with(20L); specified = false; }
- DO_(args) { value = args[0].to_long(); specified = true; });
+ CTOR(report_t, payee_width_) {
+ on_with(none, 20L);
+ specified = false;
+ }
+ DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, account_width_,
bool specified;
- CTOR(report_t, account_width_) { on_with(23L); specified = false; }
- DO_(args) { value = args[0].to_long(); specified = true; });
+ CTOR(report_t, account_width_) {
+ on_with(none, 23L);
+ specified = false;
+ }
+ DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, amount_width_,
bool specified;
- CTOR(report_t, amount_width_) { on_with(12L); specified = false; }
- DO_(args) { value = args[0].to_long(); specified = true; });
+ CTOR(report_t, amount_width_) {
+ on_with(none, 12L);
+ specified = false;
+ }
+ DO_(args) { value = args[1].to_long(); specified = true; });
OPTION__(report_t, total_width_,
bool specified;
- CTOR(report_t, total_width_) { on_with(12L); specified = false; }
- DO_(args) { value = args[0].to_long(); specified = true; });
+ CTOR(report_t, total_width_) {
+ on_with(none, 12L);
+ specified = false;
+ }
+ DO_(args) { value = args[1].to_long(); specified = true; });
};
} // namespace ledger
diff --git a/src/session.cc b/src/session.cc
index bbc196df..2ae09e21 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -71,9 +71,9 @@ session_t::session_t()
TRACE_CTOR(session_t, "");
if (const char * home_var = std::getenv("HOME"))
- HANDLER(price_db_).on((path(home_var) / ".pricedb").string());
+ HANDLER(price_db_).on(none, (path(home_var) / ".pricedb").string());
else
- HANDLER(price_db_).on(path("./.pricedb").string());
+ HANDLER(price_db_).on(none, path("./.pricedb").string());
// Add time commodity conversions, so that timelog's may be parsed
// in terms of seconds, but reported as minutes or hours.
@@ -245,27 +245,12 @@ expr_t::ptr_op_t session_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
- case 'd':
- if (is_eq(p, "date"))
- return MAKE_FUNCTOR(session_t::fn_today);
- break;
-
- case 'n':
- if (is_eq(p, "now"))
- return MAKE_FUNCTOR(session_t::fn_now);
- break;
-
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<session_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(session_t, handler);
}
break;
-
- case 't':
- if (is_eq(p, "today"))
- return MAKE_FUNCTOR(session_t::fn_today);
- break;
}
// Check if they are trying to access an option's setting or value.
diff --git a/src/session.h b/src/session.h
index 1d700d1b..679fe679 100644
--- a/src/session.h
+++ b/src/session.h
@@ -101,11 +101,15 @@ public:
clean_accounts();
}
- value_t fn_now(call_scope_t&) {
- return CURRENT_TIME();
- }
- value_t fn_today(call_scope_t&) {
- return CURRENT_DATE();
+ void report_options(std::ostream& out)
+ {
+ HANDLER(account_).report(out);
+ HANDLER(download).report(out);
+ HANDLER(leeway_).report(out);
+ HANDLER(file_).report(out);
+ HANDLER(input_date_format_).report(out);
+ HANDLER(price_db_).report(out);
+ HANDLER(strict).report(out);
}
option_t<session_t> * lookup_option(const char * p);
@@ -123,7 +127,7 @@ public:
(session_t, leeway_,
CTOR(session_t, leeway_) { value = 24L * 3600L; }
DO_(args) {
- value = args[0].to_long() * 60L;
+ value = args[1].to_long() * 60L;
});
OPTION__
@@ -131,12 +135,12 @@ public:
std::list<path> data_files;
CTOR(session_t, file_) {}
DO_(args) {
- assert(args.size() == 1);
+ assert(args.size() == 2);
if (parent->flush_on_next_data_file) {
data_files.clear();
parent->flush_on_next_data_file = false;
}
- data_files.push_back(args[0].as_string());
+ data_files.push_back(args[1].as_string());
});
OPTION(session_t, input_date_format_);
diff --git a/src/textual.cc b/src/textual.cc
index 0c92f7bb..f2996a03 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -535,7 +535,7 @@ void instance_t::option_directive(char * line)
if (p)
*p++ = '\0';
}
- process_option(line + 2, session_scope, p, line);
+ process_option(pathname.string(), line + 2, session_scope, p, line);
}
void instance_t::automated_xact_directive(char * line)
@@ -863,6 +863,9 @@ post_t * instance_t::parse_post(char * line,
char * next = next_element(p, true);
char * e = p + std::strlen(p);
+ while (e > p && std::isspace(*(e - 1)))
+ e--;
+
if ((*p == '[' && *(e - 1) == ']') || (*p == '(' && *(e - 1) == ')')) {
post->add_flags(POST_VIRTUAL);
DEBUG("textual.parse", "line " << linenum << ": "
@@ -1014,7 +1017,8 @@ post_t * instance_t::parse_post(char * line,
<< "POST assign: parsed amt = " << *post->assigned_amount);
amount_t& amt(*post->assigned_amount);
- value_t account_total(post->account->self_total(false));
+ value_t account_total(post->account->self_total(false)
+ .strip_annotations(keep_details_t()));
DEBUG("post.assign", "line " << linenum << ": "
"account balance = " << account_total);
diff --git a/src/unistring.h b/src/unistring.h
index b3086c39..5467a151 100644
--- a/src/unistring.h
+++ b/src/unistring.h
@@ -99,10 +99,14 @@ public:
inline void justify(std::ostream& out,
const std::string& str,
int width,
- bool right = false)
+ bool right = false,
+ bool redden = false)
{
- if (! right)
+ if (! right) {
+ if (redden) out << "\e[31m";
out << str;
+ if (redden) out << "\e[0m";
+ }
unistring temp(str);
@@ -110,8 +114,11 @@ inline void justify(std::ostream& out,
while (spacing-- > 0)
out << ' ';
- if (right)
+ if (right) {
+ if (redden) out << "\e[31m";
out << str;
+ if (redden) out << "\e[0m";
+ }
}
} // namespace ledger
diff --git a/src/value.cc b/src/value.cc
index 53e2bdeb..6f6e665d 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -573,6 +573,7 @@ value_t& value_t::operator*=(const value_t& val)
return *this;
case AMOUNT:
if (as_amount().commodity() == val.as_amount().commodity() ||
+ ! as_amount().has_commodity() ||
! val.as_amount().has_commodity()) {
as_amount_lval() *= val.as_amount();
return *this;
@@ -1423,10 +1424,12 @@ void value_t::print(std::ostream& out,
const int first_width,
const int latter_width,
const bool right_justify,
+ const bool colorize,
const optional<string>& date_format) const
{
if (first_width > 0 &&
- ! is_amount() && ! is_balance() && ! is_string()) {
+ (! is_amount() || as_amount().is_zero()) &&
+ ! is_balance() && ! is_string()) {
out.width(first_width);
if (right_justify)
@@ -1459,17 +1462,20 @@ void value_t::print(std::ostream& out,
break;
case INTEGER:
- out << std::right << as_long();
+ if (colorize && as_long() < 0)
+ justify(out, to_string(), first_width, right_justify, true);
+ else
+ out << as_long();
break;
case AMOUNT: {
if (as_amount().is_zero()) {
- out.width(first_width);
- out << (right_justify ? std::right : std::left) << 0;
+ out << 0;
} else {
std::ostringstream buf;
buf << as_amount();
- justify(out, buf.str(), first_width, right_justify);
+ justify(out, buf.str(), first_width, right_justify,
+ colorize && as_amount().sign() < 0);
}
break;
}
@@ -1492,14 +1498,15 @@ void value_t::print(std::ostream& out,
out << ", ";
value.print(out, first_width, latter_width, right_justify,
- date_format);
+ colorize, date_format);
}
out << ')';
break;
}
case BALANCE:
- as_balance().print(out, first_width, latter_width, right_justify);
+ as_balance().print(out, first_width, latter_width, right_justify,
+ colorize);
break;
case POINTER:
diff --git a/src/value.h b/src/value.h
index 1e972558..a2b9c07f 100644
--- a/src/value.h
+++ b/src/value.h
@@ -915,6 +915,7 @@ public:
const int first_width = -1,
const int latter_width = -1,
const bool right_justify = false,
+ const bool colorize = false,
const optional<string>& date_format = none) const;
void dump(std::ostream& out, const bool relaxed = true) const;
diff --git a/src/xact.h b/src/xact.h
index 174a8cea..b7b4576b 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -206,7 +206,13 @@ class period_xact_t : public xact_base_t
}
virtual bool valid() const {
- return period.is_valid();
+#if 0
+ if (! period.is_valid()) {
+ DEBUG("ledger.validate", "period_xact_t: ! period.is_valid()");
+ return false;
+ }
+#endif
+ return true;
}
};
diff --git a/test/baseline/feat-balance-assignments.test b/test/baseline/feat-balance-assignments.test
new file mode 100644
index 00000000..74fe9ddb
--- /dev/null
+++ b/test/baseline/feat-balance-assignments.test
@@ -0,0 +1,37 @@
+bal
+<<<
+2009/01/01 Entry
+ Assets:Cash $10,000.00
+ Equity:Opening Balances
+
+2009/02/01 Entry
+ Expenses:Cash $100.00
+ Assets:Cash
+
+2009/02/02 Entry
+ Expenses:Cash $100.00
+ Assets:Cash
+
+2009/02/03 Entry
+ Expenses:Cash $100.00
+ Assets:Cash $-100.00 = $9,700.00
+
+2009/02/04 Entry
+ Expenses:Cash $100.00
+ Assets:Cash $-100.00 = $9,600.00
+
+2009/02/05 Entry
+ Expenses:Cash $100.00
+ Assets:Cash
+
+2009/02/05 Entry
+ Expenses:Cash
+ Assets:Cash = ($4,000.00 + $100.00)
+>>>1
+ $4,100.00 Assets:Cash
+ $-10,000.00 Equity:Opening Balances
+ $5,900.00 Expenses:Cash
+--------------------
+ 0
+>>>2
+=== 0
diff --git a/test/baseline/feature-fixated-prices.test b/test/baseline/feat-fixated-prices.test
index 11330dea..11330dea 100644
--- a/test/baseline/feature-fixated-prices.test
+++ b/test/baseline/feat-fixated-prices.test
diff --git a/test/input/drewr.dat b/test/input/drewr.dat
index 937daa5c..13b88844 100644
--- a/test/input/drewr.dat
+++ b/test/input/drewr.dat
@@ -57,3 +57,7 @@
2004/01/27 Book Store
Expenses:Books $20.00
Liabilities:MasterCard
+
+2004/02/01 Sale
+ Assets:Checking:Business $ 30.00
+ Income:Sales
diff --git a/test/regress/04C5E1CA.test b/test/regress/04C5E1CA.test
new file mode 100644
index 00000000..729ae6bf
--- /dev/null
+++ b/test/regress/04C5E1CA.test
@@ -0,0 +1,18 @@
+reg
+<<<
+2009/04/04 CS Club Sign
+ Expenses:School:CS Club:Home Depot:4" Brush 2 @ $3.97
+ Liabilities:Mastercard
+
+2009/04/04 CS Club Sign
+ Expenses:School:CS Club:Home Depot:4" Brush (2 * $3.97)
+ Liabilities:Mastercard
+>>>1
+09-Apr-04 CS Club Sign Ex:Sc:CS:Ho:4" Brush 2 2
+ Liabilities:Mastercard $-7.94 2
+ $-7.94
+09-Apr-04 CS Club Sign Ex:Sc:CS:Ho:4" Brush $7.94 2
+ Liabilities:Mastercard $-7.94 2
+ $-7.94
+>>>2
+=== 0
diff --git a/test/regress/5A03CFC3.test b/test/regress/5A03CFC3.test
new file mode 100644
index 00000000..a5a12af3
--- /dev/null
+++ b/test/regress/5A03CFC3.test
@@ -0,0 +1,72 @@
+bal assets
+<<<
+= account =~ /^Income/
+ (Liabilities:Tithe) 0.12
+
+~ Monthly
+ Assets:Checking $500.00
+ Income:Salary
+
+2003/12/01 * Checking balance
+ Assets:Checking $1,000.00
+ Equity:Opening Balances
+
+2003/12/20 Organic Co-op
+ Expenses:Food:Groceries $ 37.50 ; [=2004/01/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2004/02/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2004/03/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2004/04/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2004/05/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2004/06/01]
+ Assets:Checking $ -225.00
+
+2003/12/28=2004/01/01 Acme Mortgage
+ Liabilities:Mortgage:Principal $ 200.00
+ Expenses:Interest:Mortgage $ 500.00
+ Expenses:Escrow $ 300.00
+ Assets:Checking $ -1000.00
+
+2004/01/02 Grocery Store
+ Expenses:Food:Groceries $ 65.00
+ Assets:Checking
+
+2004/01/05 Employer
+ Assets:Checking $ 2000.00
+ Income:Salary
+
+2004/01/14 Bank
+ ; Regular monthly savings transfer
+ Assets:Savings $ 300.00
+ Assets:Checking
+
+2004/01/19 Grocery Store
+ Expenses:Food:Groceries $ 44.00
+ Assets:Checking
+
+2004/01/25 Bank
+ ; Transfer to cover car purchase
+ Assets:Checking $ 5,500.00
+ Assets:Savings
+ ; :nobudget:
+
+2004/01/25 Tom's Used Cars
+ Expenses:Auto $ 5,500.00
+ ; :nobudget:
+ Assets:Checking
+
+2004/01/27 Book Store
+ Expenses:Books $20.00
+ Liabilities:MasterCard
+
+2004/02/01 Sale
+ Assets:Checking:Business $ 30.00
+ Income:Sales
+>>>1
+ $ -3,804.00 Assets
+ $ 1,396.00 Checking
+ $ 30.00 Business
+ $ -5,200.00 Savings
+--------------------
+ $ -3,804.00
+>>>2
+=== 0
diff --git a/test/regress/5FBF2ED8.test b/test/regress/5FBF2ED8.test
new file mode 100644
index 00000000..78df5a6e
--- /dev/null
+++ b/test/regress/5FBF2ED8.test
@@ -0,0 +1,20 @@
+bal -B
+<<<
+2008/01/01 * Checking balance
+ Assets:Bank:Checking £0.00
+ Equity:Opening Balances
+
+2008/02/02 Salary
+ Income:Employer £-334.00
+ Assets:Bank:Checking $512.85 @@ £334.00
+
+2008/03/02 Salary
+ Income:Employer £-248.07
+ Assets:Bank:Checking $404.82 @@ £248.07
+>>>1
+ £582.07 Assets:Bank:Checking
+ £-582.07 Income:Employer
+--------------------
+ 0
+>>>2
+=== 0
diff --git a/test/regress/647D5DB9.test b/test/regress/647D5DB9.test
new file mode 100644
index 00000000..3f187b36
--- /dev/null
+++ b/test/regress/647D5DB9.test
@@ -0,0 +1,17 @@
+bal --end 2008/12/31 -JV bal Equities
+<<<
+2008/01/01 * Purchase Apple shares
+ Equities 1000 AAPL @ $2
+ Cash
+
+2008/06/30 * Sell some Apple shares
+ Equities -500 AAPL @ $2.5
+ Cash
+
+P 2008/10/01 02:18:02 AAPL $3
+P 2009/01/31 02:18:02 AAPL $4
+P 3000/01/01 02:18:02 APPL $100
+>>>1
+2008-12-31 1500
+>>>2
+=== 0
diff --git a/test/regress/6DAB9FE3.test b/test/regress/6DAB9FE3.test
new file mode 100644
index 00000000..50b944ae
--- /dev/null
+++ b/test/regress/6DAB9FE3.test
@@ -0,0 +1,10 @@
+reg -E
+<<<
+2009/01/01 Sample
+ assets 0 FOO @ $8.88
+ equity
+>>>1
+09-Jan-01 Sample assets 0 0
+ equity 0 0
+>>>2
+=== 0
diff --git a/test/regress/727B2DF8.test b/test/regress/727B2DF8.test
new file mode 100644
index 00000000..599090a1
--- /dev/null
+++ b/test/regress/727B2DF8.test
@@ -0,0 +1,81 @@
+reg --color
+<<<
+N $
+
+= account =~ /^Expenses:Books/
+ (Liabilities:Taxes) -0.10
+
+~ Monthly
+ Assets:Bank:Checking $500.00
+ Income:Salary
+
+2004/05/01 * Checking balance
+ Assets:Bank:Checking $1,000.00
+ Equity:Opening Balances
+
+2004/05/03=2004/05/01 * Investment balance
+ Assets:Brokerage 50 AAPL @ $30.00
+ Equity:Opening Balances
+
+2004/05/14 * Páy dày
+ Assets:Bank:Checking 500.00€
+ Income:Salary
+
+2004/05/14 * Another dày in which there is Páying
+ Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00
+ Income:Salary
+
+2004/05/14 * Another dày in which there is Páying
+ Русский язык:Русский язык:Русский язык:Русский язык $1000.00
+ Income:Salary
+
+2004/05/27 Book Store
+ Expenses:Books $20.00
+ Expenses:Cards $40.00
+ Expenses:Docs $30.00
+ Liabilities:MasterCard
+
+2004/05/27 (100) Credit card company
+ ; This is an xact note!
+ ; Sample: Value
+ Liabilities:MasterCard $20.00
+ ; This is a posting note!
+ ; Sample: Another Value
+ ; :MyTag:
+ Assets:Bank:Checking
+ ; :AnotherTag:
+>>>1
+04-May-01 Checking balance Assets:Bank:Checking  $1,000.00 $1,000.00
+ Eq:Opening Balances  $-1,000.00 0
+04-May-03 Investment balance Assets:Brokerage  50 AAPL 50 AAPL
+ Eq:Opening Balances  $-1,500.00 $-1,500.00
+ 50 AAPL
+04-May-14 Páy dày Assets:Bank:Checking  500.00€ $-1,500.00
+ 50 AAPL
+ 500.00€
+ Income:Salary  -500.00€ $-1,500.00
+ 50 AAPL
+04-May-14 Another dày in whic.. ..Bá:Ch:As:Bá:Chécking $500.00 $-1,000.00
+ 50 AAPL
+ Income:Salary  $-500.00 $-1,500.00
+ 50 AAPL
+04-May-14 Another dày in whic.. Ру:Ру:Ру:Русский язык  $1,000.00 $-500.00
+ 50 AAPL
+ Income:Salary  $-1,000.00 $-1,500.00
+ 50 AAPL
+04-May-27 Book Store  Expenses:Books  $20.00 $-1,480.00
+ 50 AAPL
+ Expenses:Cards  $40.00 $-1,440.00
+ 50 AAPL
+ Expenses:Docs  $30.00 $-1,410.00
+ 50 AAPL
+ Liabilities:MasterCard $-90.00 $-1,500.00
+ 50 AAPL
+ (Liabilities:Taxes)  $-2.00 $-1,502.00
+ 50 AAPL
+04-May-27 Credit card company  Liabilities:MasterCard $20.00 $-1,482.00
+ 50 AAPL
+ Assets:Bank:Checking  $-20.00 $-1,502.00
+ 50 AAPL
+>>>2
+=== 0
diff --git a/test/regress/793F6BF0.test b/test/regress/793F6BF0.test
new file mode 100644
index 00000000..a4e1234c
--- /dev/null
+++ b/test/regress/793F6BF0.test
@@ -0,0 +1,52 @@
+entry 2009/03/15 book 10
+<<<
+N $
+
+= account =~ /^Expenses:Books/
+ (Liabilities:Taxes) -0.10
+
+~ Monthly
+ Assets:Bank:Checking $500.00
+ Income:Salary
+
+2004/05/01 * Checking balance
+ Assets:Bank:Checking $1,000.00
+ Equity:Opening Balances
+
+2004/05/03=2004/05/01 * Investment balance
+ Assets:Brokerage 50 AAPL @ $30.00
+ Equity:Opening Balances
+
+2004/05/14 * Páy dày
+ Assets:Bank:Checking 500.00€
+ Income:Salary
+
+2004/05/14 * Another dày in which there is Páying
+ Asséts:Bánk:Chécking:Asséts:Bánk:Chécking $500.00
+ Income:Salary
+
+2004/05/14 * Another dày in which there is Páying
+ Русский язык:Русский язык:Русский язык:Русский язык $1000.00
+ Income:Salary
+
+2004/05/27 Book Store
+ Expenses:Books $20.00
+ Expenses:Cards $40.00
+ Expenses:Docs $30.00
+ Liabilities:MasterCard
+
+2004/05/27 (100) Credit card company
+ ; This is an xact note!
+ ; Sample: Value
+ Liabilities:MasterCard $20.00
+ ; This is a posting note!
+ ; Sample: Another Value
+ ; :MyTag:
+ Assets:Bank:Checking
+ ; :AnotherTag:
+>>>1
+2009/03/15 Book Store
+ Expenses:Books $10.00
+ Liabilities:MasterCard
+>>>2
+=== 0
diff --git a/test/regress/B68FFB0D.test b/test/regress/B68FFB0D.test
new file mode 100644
index 00000000..3a43df4c
--- /dev/null
+++ b/test/regress/B68FFB0D.test
@@ -0,0 +1,15 @@
+print
+<<<
+D $1,000.00
+
+2009/01/01 Sample
+ assets 134.123 FOO @ $8.88
+ assets 100 BAR @ $8.88
+ equity
+>>>1
+2009/01/01 Sample
+ assets 134.123 FOO @ $8.88
+ assets 100 BAR @ $8.88
+ equity
+>>>2
+=== 0
diff --git a/test/regress/C523E23F.test b/test/regress/C523E23F.test
new file mode 100644
index 00000000..b8b08e00
--- /dev/null
+++ b/test/regress/C523E23F.test
@@ -0,0 +1,21 @@
+bal
+<<<
+D 1.000,00€
+
+2009/03/16 * denn's
+ ; Kauf: Yogi-Tee
+ Aufwand:Einkauf:Lebensmittel 17,94€
+ Aktiva:Bank:Girokonto
+
+2009/03/24 Ansparen
+ Aktiva:Bank:Sparkonto 800,00€
+ Aktiva:Bank:Girokonto
+>>>1
+ -17,94€ Aktiva:Bank
+ -817,94€ Girokonto
+ 800,00€ Sparkonto
+ 17,94€ Aufwand:Einkauf:Lebensmittel
+--------------------
+ 0
+>>>2
+=== 0
diff --git a/test/regress/D943AE0F.test b/test/regress/D943AE0F.test
new file mode 100644
index 00000000..3e568db6
--- /dev/null
+++ b/test/regress/D943AE0F.test
@@ -0,0 +1,13 @@
+reg -V --end=2009/06/16
+<<<
+2008/04/15 * Paid expenses back from cie.
+ Expenses:Cie-Reimbursements 2000 CAD @ 1.10 EUR
+ Assets:Checking
+
+P 2008/04/20 00:00:00 CAD 1.20 EUR
+>>>1
+08-Apr-15 Paid expenses back .. Ex:Cie-Reimbursements 2200.00 EUR 2200.00 EUR
+ Assets:Checking -2200.00 EUR 0
+09-Jun-16 Commodities revalued <Revalued> 200.00 EUR 200.00 EUR
+>>>2
+=== 0
diff --git a/test/unit/t_amount.cc b/test/unit/t_amount.cc
index 8aa67137..ba863ca7 100644
--- a/test/unit/t_amount.cc
+++ b/test/unit/t_amount.cc
@@ -866,7 +866,7 @@ void AmountTestCase::testIntegerDivision()
x1 /= amount_t(456L);
assertEqual(string("0.269737"), x1.to_string());
x1 /= 456L;
- assertEqual(string("0.000591528162511542"), x1.to_string());
+ assertEqual(string("0.000591528163"), x1.to_string());
amount_t x4("123456789123456789123456789");
amount_t y4("56");
@@ -889,32 +889,32 @@ void AmountTestCase::testFractionalDivision()
amount_t y1("456.456");
assertThrow(x1 / 0L, amount_error);
- assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string());
- assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string());
+ assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string());
+ assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string());
assertEqual(x1, x1 / amount_t("1.0"));
- assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string());
- assertEqual(string("0.00812195934"), (amount_t("1.0") / x1).to_string());
+ assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string());
+ assertEqual(string("0.0081219593"), (amount_t("1.0") / x1).to_string());
assertEqual(- x1, x1 / amount_t("-1.0"));
- assertEqual(string("-0.00812195934"), (amount_t("-1.0") / x1).to_string());
- assertEqual(string("-0.00812195934"), (amount_t("-1.0") / x1).to_string());
- assertEqual(string("0.269736842105263"), (x1 / y1).to_string());
- assertEqual(string("3.707317073170732"), (y1 / x1).to_string());
- assertEqual(string("0.269736842105263"), (x1 / amount_t("456.456")).to_string());
- assertEqual(string("3.707317073170732"), (amount_t("456.456") / x1).to_string());
- assertEqual(string("3.707317073170732"), (amount_t("456.456") / x1).to_string());
+ assertEqual(string("-0.0081219593"), (amount_t("-1.0") / x1).to_string());
+ assertEqual(string("-0.0081219593"), (amount_t("-1.0") / x1).to_string());
+ assertEqual(string("0.269736842105"), (x1 / y1).to_string());
+ assertEqual(string("3.707317073171"), (y1 / x1).to_string());
+ assertEqual(string("0.269736842105"), (x1 / amount_t("456.456")).to_string());
+ assertEqual(string("3.707317073171"), (amount_t("456.456") / x1).to_string());
+ assertEqual(string("3.707317073171"), (amount_t("456.456") / x1).to_string());
x1 /= amount_t("456.456");
- assertEqual(string("0.269736842105263"), x1.to_string());
+ assertEqual(string("0.269736842105"), x1.to_string());
x1 /= amount_t("456.456");
- assertEqual(string("0.000590937225286255757169884601508201951"), x1.to_string());
+ assertEqual(string("0.000590937225286255757"), x1.to_string());
x1 /= 456L;
- assertEqual(string("0.00000129591496773301701133746621429767819329289006668733529828959526392431755859036"), x1.to_string());
+ assertEqual(string("0.000001295914967733017011337"), x1.to_string());
amount_t x4("1234567891234567.89123456789");
amount_t y4("56.789");
assertEqual(amount_t("1.0"), x4 / x4);
- assertEqual(string("21739560323910.7554497273748437197344556164046"), (x4 / y4).to_string());
+ assertEqual(string("21739560323910.75544972737484371973"), (x4 / y4).to_string());
assertValid(x1);
assertValid(y1);
@@ -946,9 +946,9 @@ void AmountTestCase::testCommodityDivision()
// Internal amounts retain their precision, even when being
// converted to strings
assertEqual(string("$0.99727201"), (x1 / x2).to_fullstring());
- assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_fullstring());
+ assertEqual(string("$1.00273545321637"), (x2 / x1).to_fullstring());
assertEqual(string("$1.00"), (x1 / x2).to_string());
- assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_string());
+ assertEqual(string("$1.00273545321637"), (x2 / x1).to_string());
assertThrow(x1 / x0, amount_error);
assertThrow(x0 / x1, amount_error);
@@ -970,9 +970,9 @@ void AmountTestCase::testCommodityDivision()
amount_t x7(internalAmount("$123456789123456789.123456789123456789"));
assertEqual(amount_t("$1"), x7 / x7);
- assertEqual(string("$0.0019216115121765559608381226612019501046413574469262"),
+ assertEqual(string("$0.0019216115121765559608381226612019501"),
(x6 / x7).to_fullstring());
- assertEqual(string("$520.39654928343335571379527154924040947271699678158689736256"),
+ assertEqual(string("$520.39654928343335571379527154924040947272"),
(x7 / x6).to_fullstring());
assertValid(x1);