diff options
author | John Wiegley <johnw@newartisans.com> | 2009-10-31 04:17:40 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-10-31 04:17:40 -0400 |
commit | 97a9b42b2c5ef908e0c47ecfd39771a79f8fa8a2 (patch) | |
tree | 8eadb82cc54e66353e05309fe42c2900b93628bf /src | |
parent | a2cb549b1dff9024e3f700203e424e496b25fd91 (diff) | |
parent | a0a980b9f4ebf1493682ecf1eb745bf52649aac5 (diff) | |
download | fork-ledger-97a9b42b2c5ef908e0c47ecfd39771a79f8fa8a2.tar.gz fork-ledger-97a9b42b2c5ef908e0c47ecfd39771a79f8fa8a2.tar.bz2 fork-ledger-97a9b42b2c5ef908e0c47ecfd39771a79f8fa8a2.zip |
Merge branch 'next'
Diffstat (limited to 'src')
-rw-r--r-- | src/chain.cc | 6 | ||||
-rw-r--r-- | src/derive.cc | 46 | ||||
-rw-r--r-- | src/emacs.cc | 4 | ||||
-rw-r--r-- | src/filters.cc | 6 | ||||
-rw-r--r-- | src/filters.h | 13 | ||||
-rw-r--r-- | src/item.cc | 27 | ||||
-rw-r--r-- | src/iterators.cc | 71 | ||||
-rw-r--r-- | src/main.cc | 14 | ||||
-rw-r--r-- | src/option.cc | 9 | ||||
-rw-r--r-- | src/option.h | 15 | ||||
-rw-r--r-- | src/report.cc | 42 | ||||
-rw-r--r-- | src/report.h | 5 | ||||
-rw-r--r-- | src/session.cc | 8 | ||||
-rw-r--r-- | src/utils.h | 18 |
14 files changed, 223 insertions, 61 deletions
diff --git a/src/chain.cc b/src/chain.cc index d7d1460b..db52269c 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -209,7 +209,11 @@ post_handler_ptr chain_post_handlers(report_t& report, = new forecast_posts(handler, item_predicate(report.HANDLER(forecast_while_).str(), report.what_to_keep()), - report); + report, + report.HANDLED(forecast_years_) ? + static_cast<std::size_t> + (report.HANDLER(forecast_years_).value.to_long()) : + 5UL); forecast_handler->add_period_xacts(report.session.journal->period_xacts); handler.reset(forecast_handler); diff --git a/src/derive.cc b/src/derive.cc index ef2d1e51..df27d4b6 100644 --- a/src/derive.cc +++ b/src/derive.cc @@ -54,6 +54,8 @@ namespace { bool from; optional<mask_t> account_mask; optional<amount_t> amount; + optional<string> cost_operator; + optional<amount_t> cost; post_template_t() : from(false) {} }; @@ -111,6 +113,10 @@ namespace { if (post.amount) out << _(" Amount: ") << *post.amount << std::endl; + + if (post.cost) + out << _(" Cost: ") << *post.cost_operator + << " " << *post.cost << std::endl; } } } @@ -148,6 +154,8 @@ namespace { string arg = (*begin).to_string(); if (arg == "at") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); tmpl.payee_mask = (*++begin).to_string(); } else if (arg == "to" || arg == "from") { @@ -155,22 +163,41 @@ namespace { tmpl.posts.push_back(xact_template_t::post_template_t()); post = &tmpl.posts.back(); } + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); post->account_mask = mask_t((*++begin).to_string()); post->from = arg == "from"; } else if (arg == "on") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); tmpl.date = parse_date((*++begin).to_string()); check_for_date = false; } else if (arg == "code") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); tmpl.code = (*++begin).to_string(); } else if (arg == "note") { + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); tmpl.note = (*++begin).to_string(); } else if (arg == "rest") { ; // just ignore this argument } + else if (arg == "@" || arg == "@@") { + amount_t cost; + post->cost_operator = arg; + if (begin == end) + throw std::runtime_error(_("Invalid xact command arguments")); + arg = (*++begin).to_string(); + if (! cost.parse(arg, amount_t::PARSE_SOFT_FAIL | + amount_t::PARSE_NO_MIGRATE)) + throw std::runtime_error(_("Invalid xact command arguments")); + post->cost = cost; + } else { // Without a preposition, it is either: // @@ -443,6 +470,25 @@ namespace { } } + if (post.cost) { + if (post.cost->sign() < 0) + throw parse_error(_("A posting's cost may not be negative")); + + post.cost->in_place_unround(); + + if (*post.cost_operator == "@") { + // For the sole case where the cost might be uncommoditized, + // guarantee that the commodity of the cost after multiplication + // is the same as it was before. + commodity_t& cost_commodity(post.cost->commodity()); + *post.cost *= new_post->amount; + post.cost->set_commodity(cost_commodity); + } + + new_post->cost = *post.cost; + DEBUG("derive.xact", "Copied over posting cost"); + } + if (found_commodity && ! new_post->amount.is_null() && ! new_post->amount.has_commodity()) { diff --git a/src/emacs.cc b/src/emacs.cc index 24d3f1c1..1d3f28a3 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -41,7 +41,7 @@ namespace ledger { void format_emacs_posts::write_xact(xact_t& xact) { out << "\"" << xact.pos->pathname << "\" " - << (xact.pos->beg_line + 1) << " "; + << xact.pos->beg_line << " "; tm when = gregorian::to_tm(xact.date()); std::time_t date = std::mktime(&when); // jww (2008-04-20): Is this GMT or local? @@ -77,7 +77,7 @@ void format_emacs_posts::operator()(post_t& post) out << "\n"; } - out << " (" << (post.pos->beg_line + 1) << " "; + out << " (" << post.pos->beg_line << " "; out << "\"" << post.reported_account()->fullname() << "\" \"" << post.amount << "\""; diff --git a/src/filters.cc b/src/filters.cc index 814cf276..fe8761a5 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -871,9 +871,11 @@ void forecast_posts::flush() date_t next = *(*least).first.next; assert(next > begin); - if ((next - last).days() > 365 * 5) { + if (static_cast<std::size_t>((next - last).days()) > + static_cast<std::size_t>(365U) * forecast_years) { DEBUG("filters.forecast", - "Forecast transaction exceeds 5 years beyond today"); + "Forecast transaction exceeds " << forecast_years + << " years beyond today"); pending_posts.erase(least); continue; } diff --git a/src/filters.h b/src/filters.h index 3eb5da8d..7c1f169c 100644 --- a/src/filters.h +++ b/src/filters.h @@ -764,16 +764,19 @@ public: */ class forecast_posts : public generate_posts { - item_predicate pred; - scope_t& context; + item_predicate pred; + scope_t& context; + const std::size_t forecast_years; public: forecast_posts(post_handler_ptr handler, const item_predicate& predicate, - scope_t& _context) - : generate_posts(handler), pred(predicate), context(_context) { + scope_t& _context, + const std::size_t _forecast_years) + : generate_posts(handler), pred(predicate), context(_context), + forecast_years(_forecast_years) { TRACE_CTOR(forecast_posts, - "post_handler_ptr, const item_predicate&, scope_t&"); + "post_handler_ptr, item_predicate, scope_t&, std::size_t"); } virtual ~forecast_posts() throw() { TRACE_DTOR(forecast_posts); diff --git a/src/item.cc b/src/item.cc index 631423a9..debda7a3 100644 --- a/src/item.cc +++ b/src/item.cc @@ -32,6 +32,7 @@ #include <system.hh> #include "item.h" +#include "interactive.h" namespace ledger { @@ -210,15 +211,31 @@ namespace { return item.has_tag(args[0].as_string()); else if (args[0].is_mask()) return item.has_tag(args[0].as_mask()); - } else { - return item.has_tag(args[0].to_mask(), args[1].to_mask()); + else + throw_(std::logic_error, + _("Expected string for argument 1, but received %1") + << args[0].label()); + } + else if (args.size() == 2) { + if (args[0].is_mask() && args[1].is_mask()) + return item.has_tag(args[0].to_mask(), args[1].to_mask()); + else + throw_(std::logic_error, + _("Expected masks for arguments 1 and 2, but received %1 and %2") + << args[0].label() << args[1].label()); + } + else if (args.size() == 0) { + throw_(std::logic_error, _("Too few arguments to function")); + } + else { + throw_(std::logic_error, _("Too many arguments to function")); } return false; } - value_t get_tag(call_scope_t& args) { - item_t& item(find_scope<item_t>(args)); - if (optional<string> value = item.get_tag(args[0].as_string())) + value_t get_tag(call_scope_t& scope) { + in_context_t<item_t> env(scope, "s"); + if (optional<string> value = env->get_tag(env.get<string>(0))) return string_value(*value); return false; } diff --git a/src/iterators.cc b/src/iterators.cc index e9560cc0..50c67ade 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -90,37 +90,48 @@ void posts_commodities_iterator::reset(journal_t& journal) std::map<string, xact_t *> xacts_by_commodity; foreach (commodity_t * comm, commodities) { - optional<commodity_t::varied_history_t&> history = comm->varied_history(); - if (! history) - continue; - - account_t * account = journal.master->find_account(comm->symbol()); - - foreach (commodity_t::base_t::history_by_commodity_map::value_type pair, - history->histories) { - foreach (commodity_t::base_t::history_map::value_type hpair, - pair.second.prices) { - xact_t * xact; - string symbol = hpair.second.commodity().symbol(); - - std::map<string, xact_t *>::iterator i = - xacts_by_commodity.find(symbol); - if (i != xacts_by_commodity.end()) { - xact = (*i).second; - } else { - xact = &temps.create_xact(); - xact_temps.push_back(xact); - xact->payee = symbol; - xact->_date = hpair.first.date(); - xacts_by_commodity.insert - (std::pair<string, xact_t *>(symbol, xact)); + if (optional<commodity_t::varied_history_t&> history = + comm->varied_history()) { + account_t * account = journal.master->find_account(comm->symbol()); + + foreach (commodity_t::base_t::history_by_commodity_map::value_type pair, + history->histories) { + foreach (commodity_t::base_t::history_map::value_type hpair, + pair.second.prices) { + xact_t * xact; + string symbol = hpair.second.commodity().symbol(); + + std::map<string, xact_t *>::iterator i = + xacts_by_commodity.find(symbol); + if (i != xacts_by_commodity.end()) { + xact = (*i).second; + } else { + xact = &temps.create_xact(); + xact_temps.push_back(xact); + xact->payee = symbol; + xact->_date = hpair.first.date(); + xacts_by_commodity.insert + (std::pair<string, xact_t *>(symbol, xact)); + } + + bool post_already_exists = false; + + foreach (post_t * post, xact->posts) { + if (post->_date == hpair.first.date() && + post->amount == hpair.second) { + post_already_exists = true; + break; + } + } + + if (! post_already_exists) { + post_t& temp = temps.create_post(*xact, account); + temp._date = hpair.first.date(); + temp.amount = hpair.second; + + temp.xdata().datetime = hpair.first; + } } - - post_t& temp = temps.create_post(*xact, account); - temp._date = hpair.first.date(); - temp.amount = hpair.second; - - temp.xdata().datetime = hpair.first; } } } diff --git a/src/main.cc b/src/main.cc index 9f0e8690..0aec8886 100644 --- a/src/main.cc +++ b/src/main.cc @@ -76,11 +76,13 @@ int main(int argc, char * argv[], char * envp[]) ::textdomain("ledger"); #endif - // Create the session object, which maintains nearly all state relating to - // this invocation of Ledger; and register all known journal parsers. - std::auto_ptr<global_scope_t> global_scope(new global_scope_t(envp)); + std::auto_ptr<global_scope_t> global_scope; try { + // Create the session object, which maintains nearly all state relating to + // this invocation of Ledger; and register all known journal parsers. + global_scope.reset(new global_scope_t(envp)); + global_scope->session().set_flush_on_next_data_file(true); // Construct an STL-style argument list from the process command arguments @@ -181,7 +183,11 @@ int main(int argc, char * argv[], char * envp[]) } } catch (const std::exception& err) { - global_scope->report_error(err); + if (global_scope.get()) + global_scope->report_error(err); + else + std::cerr << "Exception during initialization: " << err.what() + << std::endl; } catch (int _status) { status = _status; // used for a "quick" exit, and is used only diff --git a/src/option.cc b/src/option.cc index 883080e2..8da66b36 100644 --- a/src/option.cc +++ b/src/option.cc @@ -116,13 +116,16 @@ void process_environment(const char ** envp, const string& tag, const char * tag_p = tag.c_str(); string::size_type tag_len = tag.length(); + assert(tag_p); + assert(tag_len > 0); + for (const char ** p = envp; *p; p++) { - if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) { - char buf[128]; + if (std::strlen(*p) >= tag_len && std::strncmp(*p, tag_p, tag_len) == 0) { + char buf[8192]; char * r = buf; const char * q; for (q = *p + tag_len; - *q && *q != '=' && r - buf < 128; + *q && *q != '=' && r - buf < 8191; q++) if (*q == '_') *r++ = '-'; diff --git a/src/option.h b/src/option.h index 83710a1c..0600779c 100644 --- a/src/option.h +++ b/src/option.h @@ -162,10 +162,21 @@ public: virtual void handler(call_scope_t& args) { if (wants_arg) { - if (args.empty() || args.size() == 1) + if (args.size() < 2) throw_(std::runtime_error, _("No argument provided for %1") << desc()); + else if (args.size() > 2) + throw_(std::runtime_error, _("To many arguments provided for %1") << desc()); + else if (! args[0].is_string()) + throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); on_with(args[0].as_string(), args[1]); - } else { + } + else if (args.size() < 1) { + throw_(std::runtime_error, _("No argument provided for %1") << desc()); + } + else if (! args[0].is_string()) { + throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); + } + else { on_only(args[0].as_string()); } diff --git a/src/report.cc b/src/report.cc index bc0680d1..096536c9 100644 --- a/src/report.cc +++ b/src/report.cc @@ -235,12 +235,13 @@ value_t report_t::fn_justify(call_scope_t& scope) return string_value(out.str()); } -value_t report_t::fn_quoted(call_scope_t& args) +value_t report_t::fn_quoted(call_scope_t& scope) { + interactive_t args(scope, "s"); std::ostringstream out; out << '"'; - foreach (const char ch, args[0].to_string()) { + foreach (const char ch, args.get<string>(0)) { if (ch == '"') out << "\\\""; else @@ -253,8 +254,7 @@ value_t report_t::fn_quoted(call_scope_t& args) value_t report_t::fn_join(call_scope_t& scope) { - interactive_t args(scope, "s"); - + interactive_t args(scope, "s"); std::ostringstream out; foreach (const char ch, args.get<string>(0)) { @@ -315,6 +315,39 @@ value_t report_t::fn_price(call_scope_t& scope) return args.value_at(0).price(); } +value_t report_t::fn_lot_date(call_scope_t& scope) +{ + interactive_t args(scope, "v"); + if (args.value_at(0).is_annotated()) { + const annotation_t& details(args.value_at(0).annotation()); + if (details.date) + return *details.date; + } + return NULL_VALUE; +} + +value_t report_t::fn_lot_price(call_scope_t& scope) +{ + interactive_t args(scope, "v"); + if (args.value_at(0).is_annotated()) { + const annotation_t& details(args.value_at(0).annotation()); + if (details.price) + return *details.price; + } + return NULL_VALUE; +} + +value_t report_t::fn_lot_tag(call_scope_t& scope) +{ + interactive_t args(scope, "v"); + if (args.value_at(0).is_annotated()) { + const annotation_t& details(args.value_at(0).annotation()); + if (details.tag) + return string_value(*details.tag); + } + return NULL_VALUE; +} + namespace { value_t fn_black(call_scope_t&) { return string_value("black"); @@ -559,6 +592,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'f': OPT(flat); else OPT_ALT(forecast_while_, forecast_); + else OPT(forecast_years_); else OPT(format_); else OPT(force_color); else OPT(force_pager); diff --git a/src/report.h b/src/report.h index 4d028e33..0fe7d05b 100644 --- a/src/report.h +++ b/src/report.h @@ -160,6 +160,9 @@ public: value_t fn_ansify_if(call_scope_t& scope); value_t fn_percent(call_scope_t& scope); value_t fn_price(call_scope_t& scope); + value_t fn_lot_date(call_scope_t& scope); + value_t fn_lot_price(call_scope_t& scope); + value_t fn_lot_tag(call_scope_t& scope); value_t fn_now(call_scope_t&) { return terminus; @@ -240,6 +243,7 @@ public: HANDLER(force_color).report(out); HANDLER(force_pager).report(out); HANDLER(forecast_while_).report(out); + HANDLER(forecast_years_).report(out); HANDLER(format_).report(out); HANDLER(gain).report(out); HANDLER(head_).report(out); @@ -544,6 +548,7 @@ public: OPTION(report_t, force_color); OPTION(report_t, force_pager); OPTION(report_t, forecast_while_); + OPTION(report_t, forecast_years_); OPTION(report_t, format_); // -F OPTION_(report_t, gain, DO() { // -G diff --git a/src/session.cc b/src/session.cc index b7fdf275..6efc03f2 100644 --- a/src/session.cc +++ b/src/session.cc @@ -146,9 +146,11 @@ std::size_t session_t::read_data(const string& master_account) cache->should_load(HANDLER(file_).data_files) && cache->load(journal))) { if (price_db_path) { - if (exists(*price_db_path) && read_journal(*price_db_path) > 0) - throw_(parse_error, _("Transactions not allowed in price history file")); - journal->sources.push_back(journal_t::fileinfo_t(*price_db_path)); + if (exists(*price_db_path)) { + if (read_journal(*price_db_path) > 0) + throw_(parse_error, _("Transactions not allowed in price history file")); + journal->sources.push_back(journal_t::fileinfo_t(*price_db_path)); + } HANDLER(file_).data_files.remove(*price_db_path); } diff --git a/src/utils.h b/src/utils.h index c662acbe..cdb43037 100644 --- a/src/utils.h +++ b/src/utils.h @@ -580,6 +580,15 @@ inline char peek_next_nonws(std::istream& in) { str.get(var); \ if (in.eof()) \ break; \ + switch (var) { \ + case 'b': var = '\b'; break; \ + case 'f': var = '\f'; break; \ + case 'n': var = '\n'; break; \ + case 'r': var = '\r'; break; \ + case 't': var = '\t'; break; \ + case 'v': var = '\v'; break; \ + default: break; \ + } \ } \ *_p++ = var; \ var = static_cast<char>(str.peek()); \ @@ -600,6 +609,15 @@ inline char peek_next_nonws(std::istream& in) { str.get(var); \ if (in.eof()) \ break; \ + switch (var) { \ + case 'b': var = '\b'; break; \ + case 'f': var = '\f'; break; \ + case 'n': var = '\n'; break; \ + case 'r': var = '\r'; break; \ + case 't': var = '\t'; break; \ + case 'v': var = '\v'; break; \ + default: break; \ + } \ idx++; \ } \ *_p++ = var; \ |