diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/amount.cc | 4 | ||||
-rw-r--r-- | src/context.h | 1 | ||||
-rw-r--r-- | src/error.h | 5 | ||||
-rw-r--r-- | src/filters.cc | 25 | ||||
-rw-r--r-- | src/global.h | 2 | ||||
-rw-r--r-- | src/output.cc | 7 | ||||
-rw-r--r-- | src/print.cc | 4 | ||||
-rw-r--r-- | src/session.cc | 4 | ||||
-rw-r--r-- | src/session.h | 4 | ||||
-rw-r--r-- | src/system.hh.in | 4 | ||||
-rw-r--r-- | src/textual.cc | 57 | ||||
-rw-r--r-- | src/times.cc | 31 | ||||
-rw-r--r-- | src/utils.h | 10 | ||||
-rw-r--r-- | src/xact.cc | 10 |
14 files changed, 97 insertions, 71 deletions
diff --git a/src/amount.cc b/src/amount.cc index 05145f87..c6463b2b 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -604,7 +604,9 @@ void amount_t::in_place_invert() throw_(amount_error, _("Cannot invert an uninitialized amount")); _dup(); - mpq_inv(MP(quantity), MP(quantity)); + + if (sign() != 0) + mpq_inv(MP(quantity), MP(quantity)); } void amount_t::in_place_round() diff --git a/src/context.h b/src/context.h index 4a7a5441..0af59930 100644 --- a/src/context.h +++ b/src/context.h @@ -70,6 +70,7 @@ public: std::size_t errors; std::size_t count; std::size_t sequence; + std::string last; explicit parse_context_t(const path& cwd) : current_directory(cwd), master(NULL), scope(NULL), diff --git a/src/error.h b/src/error.h index a628c1e9..bc9953cd 100644 --- a/src/error.h +++ b/src/error.h @@ -95,8 +95,9 @@ string source_context(const path& file, struct error_count { std::size_t count; - explicit error_count(std::size_t _count) : count(_count) {} - const char * what() const { return ""; } + std::string message; + explicit error_count(std::size_t _count, std::string _msg) : count(_count), message(_msg) {} + const char * what() const { return message.c_str(); } }; } // namespace ledger diff --git a/src/filters.cc b/src/filters.cc index 4e9e633a..3dfd2327 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1245,19 +1245,34 @@ void generate_posts::add_post(const date_interval_t& period, post_t& post) void budget_posts::report_budget_items(const date_t& date) { + { // Cleanup pending items that finished before date + // We have to keep them until the last day they apply because operator() needs them to see if a + // posting is budgeted or not + std::list<pending_posts_list::iterator> posts_to_erase; + for (pending_posts_list::iterator i = pending_posts.begin(); i != pending_posts.end(); i++) { + pending_posts_list::value_type& pair(*i); + if (pair.first.finish && ! pair.first.start && pair.first.finish < date) { + posts_to_erase.push_back(i); + } + } + foreach (pending_posts_list::iterator& i, posts_to_erase) + pending_posts.erase(i); + } + if (pending_posts.size() == 0) return; bool reported; do { - std::list<pending_posts_list::iterator> posts_to_erase; - reported = false; for (pending_posts_list::iterator i = pending_posts.begin(); i != pending_posts.end(); i++) { pending_posts_list::value_type& pair(*i); + if (pair.first.finish && ! pair.first.start) + continue; // skip expired posts + optional<date_t> begin = pair.first.start; if (! begin) { optional<date_t> range_begin; @@ -1285,9 +1300,6 @@ void budget_posts::report_budget_items(const date_t& date) post_t& post = *pair.second; ++pair.first; - if (! pair.first.start) - posts_to_erase.push_back(i); - DEBUG("budget.generate", "Reporting budget for " << post.reported_account()->fullname()); @@ -1312,9 +1324,6 @@ void budget_posts::report_budget_items(const date_t& date) reported = true; } } - - foreach (pending_posts_list::iterator& i, posts_to_erase) - pending_posts.erase(i); } while (reported); } diff --git a/src/global.h b/src/global.h index 01ad60cc..8f8266ac 100644 --- a/src/global.h +++ b/src/global.h @@ -166,7 +166,7 @@ See LICENSE file included with the distribution for details and disclaimer."); OPTION_(global_scope_t, version, DO() { // -v parent->show_version_info(std::cout); - throw error_count(0); // exit immediately + throw error_count(0, ""); // exit immediately }); }; diff --git a/src/output.cc b/src/output.cc index c2fa83ac..09d3ad9e 100644 --- a/src/output.cc +++ b/src/output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2018, John Wiegley. All rights reserved. + * Copyright (c) 2003-2019, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -284,8 +284,9 @@ void report_accounts::flush() std::ostream& out(report.output_stream); format_t prepend_format; std::size_t prepend_width; + bool do_prepend_format; - if (report.HANDLED(prepend_format_)) { + if ((do_prepend_format = report.HANDLED(prepend_format_))) { prepend_format.parse_format(report.HANDLER(prepend_format_).str()); prepend_width = report.HANDLED(prepend_width_) ? lexical_cast<std::size_t>(report.HANDLER(prepend_width_).str()) @@ -293,7 +294,7 @@ void report_accounts::flush() } foreach (accounts_pair& entry, accounts) { - if (prepend_format) { + if (do_prepend_format) { bind_scope_t bound_scope(report, *entry.first); out.width(static_cast<std::streamsize>(prepend_width)); out << prepend_format(bound_scope); diff --git a/src/print.cc b/src/print.cc index 9fa75eab..92323777 100644 --- a/src/print.cc +++ b/src/print.cc @@ -103,11 +103,13 @@ namespace { void print_xact(report_t& report, std::ostream& out, xact_t& xact) { format_type_t format_type = FMT_WRITTEN; + string format_str; optional<const char *> format; if (report.HANDLED(date_format_)) { format_type = FMT_CUSTOM; - format = report.HANDLER(date_format_).str().c_str(); + format_str = report.HANDLER(date_format_).str(); + format = format_str.c_str(); } std::ostringstream buf; diff --git a/src/session.cc b/src/session.cc index e95123e8..427850d9 100644 --- a/src/session.cc +++ b/src/session.cc @@ -76,7 +76,7 @@ std::size_t session_t::read_data(const string& master_account) file = path(home_var) / ".ledger"; if (! file.empty() && exists(file)) - HANDLER(file_).data_files.insert(file); + HANDLER(file_).data_files.push_back(file); else throw_(parse_error, "No journal file was specified (please use -f)"); @@ -214,7 +214,7 @@ journal_t * session_t::read_journal_files() journal_t * session_t::read_journal(const path& pathname) { HANDLER(file_).data_files.clear(); - HANDLER(file_).data_files.insert(pathname); + HANDLER(file_).data_files.push_back(pathname); return read_journal_files(); } diff --git a/src/session.h b/src/session.h index 47732a3d..4dce3816 100644 --- a/src/session.h +++ b/src/session.h @@ -153,14 +153,14 @@ public: OPTION__ (session_t, file_, // -f - std::set<path COMMA ComparePaths> data_files; + std::list<path> data_files; CTOR(session_t, file_) {} DO_(str) { if (parent->flush_on_next_data_file) { data_files.clear(); parent->flush_on_next_data_file = false; } - data_files.insert(str); + data_files.push_back(str); }); OPTION_(session_t, input_date_format_, DO_(str) { diff --git a/src/system.hh.in b/src/system.hh.in index 38ac1e63..799bb47c 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2018, John Wiegley. All rights reserved. + * Copyright (c) 2003-2019, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -60,8 +60,6 @@ #define HAVE_EDIT @HAVE_EDIT@ #define HAVE_GETTEXT @HAVE_GETTEXT@ -#cmakedefine HAVE_ACCESS -#cmakedefine HAVE_REALPATH #cmakedefine HAVE_GETPWUID #cmakedefine HAVE_GETPWNAM #cmakedefine HAVE_IOCTL diff --git a/src/textual.cc b/src/textual.cc index 8fbc5c08..3416073b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -282,6 +282,10 @@ void instance_t::parse() std::cerr << _("Error: ") << err.what() << std::endl; context.errors++; + if (! current_context.empty()) + context.last = current_context + "\n" + err.what(); + else + context.last = err.what(); } } @@ -1644,29 +1648,30 @@ post_t * instance_t::parse_post(char * line, } DEBUG("textual.parse", "line " << context.linenum << ": " - << "POST assign: parsed amt = " << *post->assigned_amount); + << "POST assign: parsed balance amount = " << *post->assigned_amount); - amount_t& amt(*post->assigned_amount); + const amount_t& amt(*post->assigned_amount); value_t account_total (post->account->amount().strip_annotations(keep_details_t())); DEBUG("post.assign", "line " << context.linenum << ": " << "account balance = " << account_total); - DEBUG("post.assign", - "line " << context.linenum << ": " << "post amount = " << amt); + DEBUG("post.assign", "line " << context.linenum << ": " + << "post amount = " << amt << " (is_zero = " << amt.is_zero() << ")"); - amount_t diff = amt; + balance_t diff = amt; switch (account_total.type()) { case value_t::AMOUNT: - if (account_total.as_amount().commodity_ptr() == diff.commodity_ptr()) - diff -= account_total.as_amount(); + diff -= account_total.as_amount(); + DEBUG("textual.parse", "line " << context.linenum << ": " + << "Subtracting amount " << account_total.as_amount() << " from diff, yielding " << diff); break; case value_t::BALANCE: - if (optional<amount_t> comm_bal = - account_total.as_balance().commodity_amount(amt.commodity())) - diff -= *comm_bal; + diff -= account_total.as_balance(); + DEBUG("textual.parse", "line " << context.linenum << ": " + << "Subtracting balance " << account_total.as_balance() << " from diff, yielding " << diff); break; default: @@ -1680,18 +1685,34 @@ post_t * instance_t::parse_post(char * line, // Subtract amounts from previous posts to this account in the xact. for (post_t* p : xact->posts) { - if (p->account == post->account && - p->amount.commodity_ptr() == diff.commodity_ptr()) { + if (p->account == post->account) { diff -= p->amount; DEBUG("textual.parse", "line " << context.linenum << ": " - << "Subtract " << p->amount << ", diff = " << diff); + << "Subtracting " << p->amount << ", diff = " << diff); } } + // If amt has a commodity, restrict balancing to that. Otherwise, it's the blanket '0' and + // check that all of them are zero. + if (amt.has_commodity()) { + DEBUG("textual.parse", "line " << context.linenum << ": " + << "Finding commodity " << amt.commodity() << " (" << amt << ") in balance " << diff); + optional<amount_t> wanted_commodity = diff.commodity_amount(amt.commodity()); + if (!wanted_commodity) { + diff = amt - amt; // this is '0' with the correct commodity. + } else { + diff = *wanted_commodity; + } + DEBUG("textual.parse", "line " << context.linenum << ": " + << "Diff is now " << diff); + } + if (post->amount.is_null()) { // balance assignment if (! diff.is_zero()) { - post->amount = diff; + // This will fail if there are more than 1 commodity in diff, which is wanted, + // as amount cannot store more than 1 commodity. + post->amount = diff.to_amount(); DEBUG("textual.parse", "line " << context.linenum << ": " << "Overwrite null posting"); } @@ -1699,10 +1720,11 @@ post_t * instance_t::parse_post(char * line, // balance assertion diff -= post->amount; if (! no_assertions && ! diff.is_zero()) { - amount_t tot = amt - diff; + balance_t tot = -diff + amt; + DEBUG("textual.parse", "Balance assertion: off by " << diff << " (expected to see " << tot << ")"); throw_(parse_error, _f("Balance assertion off by %1% (expected to see %2%)") - % diff % tot); + % diff.to_string() % tot.to_string()); } } @@ -2012,7 +2034,8 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack) TRACE_FINISH(parsing_total, 1); if (context_stack.get_current().errors > 0) - throw error_count(context_stack.get_current().errors); + throw error_count(context_stack.get_current().errors, + context_stack.get_current().last); return context_stack.get_current().count; } diff --git a/src/times.cc b/src/times.cc index 8e4df020..eda71ae7 100644 --- a/src/times.cc +++ b/src/times.cc @@ -420,7 +420,6 @@ class date_parser_t TOK_DASH, TOK_DOT, - TOK_A_YEAR, TOK_A_MONTH, TOK_A_WDAY, @@ -512,9 +511,6 @@ class date_parser_t case TOK_SLASH: return "/"; case TOK_DASH: return "-"; case TOK_DOT: return "."; - case TOK_A_YEAR: - out << boost::get<date_specifier_t::year_type>(*value); - break; case TOK_A_MONTH: out << date_specifier_t::month_type (boost::get<date_time::months_of_year>(*value)); @@ -566,7 +562,6 @@ class date_parser_t case TOK_SLASH: out << "TOK_SLASH"; break; case TOK_DASH: out << "TOK_DASH"; break; case TOK_DOT: out << "TOK_DOT"; break; - case TOK_A_YEAR: out << "TOK_A_YEAR"; break; case TOK_A_MONTH: out << "TOK_A_MONTH"; break; case TOK_A_WDAY: out << "TOK_A_WDAY"; break; case TOK_AGO: out << "TOK_AGO"; break; @@ -727,7 +722,11 @@ void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok, when += gregorian::days(amount * adjust); break; default: - specifier.day = date_specifier_t::day_type(amount); + if (amount > 31) { + specifier.year = date_specifier_t::year_type(amount); + } else { + specifier.day = date_specifier_t::day_type(amount); + } break; } @@ -832,16 +831,13 @@ void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok, break; } - case lexer_t::token_t::TOK_A_YEAR: - specifier.year = boost::get<date_specifier_t::year_type>(*tok.value); - break; case lexer_t::token_t::TOK_A_MONTH: specifier.month = date_specifier_t::month_type (boost::get<date_time::months_of_year>(*tok.value)); tok = lexer.peek_token(); switch (tok.kind) { - case lexer_t::token_t::TOK_A_YEAR: + case lexer_t::token_t::TOK_INT: specifier.year = boost::get<date_specifier_t::year_type>(*tok.value); break; case lexer_t::token_t::END_REACHED: @@ -898,12 +894,6 @@ date_interval_t date_parser_t::parse() determine_when(tok, *inclusion_specifier); break; - case lexer_t::token_t::TOK_A_YEAR: - if (! inclusion_specifier) - inclusion_specifier = date_specifier_t(); - determine_when(tok, *inclusion_specifier); - break; - case lexer_t::token_t::TOK_A_MONTH: if (! inclusion_specifier) inclusion_specifier = date_specifier_t(); @@ -1612,13 +1602,8 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token() if (! term.empty()) { if (std::isdigit(term[0])) { - if (term.length() == 4) - return token_t(token_t::TOK_A_YEAR, - token_t::content_t - (lexical_cast<date_specifier_t::year_type>(term))); - else - return token_t(token_t::TOK_INT, - token_t::content_t(lexical_cast<unsigned short>(term))); + return token_t(token_t::TOK_INT, + token_t::content_t(lexical_cast<unsigned short>(term))); } else if (std::isalpha(term[0])) { to_lower(term); diff --git a/src/utils.h b/src/utils.h index b21dff7a..c9146dd7 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2018, John Wiegley. All rights reserved. + * Copyright (c) 2003-2019, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -44,7 +44,11 @@ #ifndef _UTILS_H #define _UTILS_H +#if (BOOST_VERSION >= 106600) +#include <boost/uuid/detail/sha1.hpp> +#else #include <boost/uuid/sha1.hpp> +#endif /** * @name Default values @@ -496,10 +500,6 @@ inline T& downcast(U& object) { path resolve_path(const path& pathname); -#ifdef HAVE_REALPATH -extern "C" char * realpath(const char *, char resolved_path[]); -#endif - inline const string& either_or(const string& first, const string& second) { return first.empty() ? second : first; diff --git a/src/xact.cc b/src/xact.cc index d29072d4..5df9ebc5 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -396,9 +396,9 @@ bool xact_base_t::finalize() } if (post->has_flags(POST_DEFERRED)) - post->account->add_deferred_post(id(), post); - else - post->account->add_post(post); + post->account->add_deferred_post(id(), post); + else + post->account->add_post(post); post->xdata().add_flags(POST_EXT_VISITED); post->account->xdata().add_flags(ACCOUNT_EXT_VISITED); @@ -806,6 +806,10 @@ void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context) xact.add_post(new_post); new_post->account->add_post(new_post); + // Add flags so this post updates the account balance + new_post->xdata().add_flags(POST_EXT_VISITED); + new_post->account->xdata().add_flags(ACCOUNT_EXT_VISITED); + if (new_post->must_balance()) needs_further_verification = true; } |