diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/account.cc | 4 | ||||
-rw-r--r-- | src/amount.cc | 12 | ||||
-rw-r--r-- | src/balance.cc | 6 | ||||
-rw-r--r-- | src/balance.h | 7 | ||||
-rw-r--r-- | src/commodity.cc | 6 | ||||
-rw-r--r-- | src/derive.cc | 144 | ||||
-rw-r--r-- | src/filters.cc | 9 | ||||
-rw-r--r-- | src/filters.h | 8 | ||||
-rw-r--r-- | src/global.cc | 81 | ||||
-rw-r--r-- | src/global.h | 7 | ||||
-rw-r--r-- | src/item.cc | 8 | ||||
-rw-r--r-- | src/option.cc | 18 | ||||
-rw-r--r-- | src/option.h | 48 | ||||
-rw-r--r-- | src/output.cc | 4 | ||||
-rw-r--r-- | src/report.cc | 48 | ||||
-rw-r--r-- | src/report.h | 388 | ||||
-rw-r--r-- | src/session.cc | 19 | ||||
-rw-r--r-- | src/session.h | 20 | ||||
-rw-r--r-- | src/textual.cc | 8 | ||||
-rw-r--r-- | src/unistring.h | 13 | ||||
-rw-r--r-- | src/value.cc | 21 | ||||
-rw-r--r-- | src/value.h | 1 | ||||
-rw-r--r-- | src/xact.h | 8 |
23 files changed, 614 insertions, 274 deletions
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; @@ -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; } }; |