summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/amount.cc4
-rw-r--r--src/context.h1
-rw-r--r--src/error.h5
-rw-r--r--src/filters.cc25
-rw-r--r--src/global.h2
-rw-r--r--src/output.cc7
-rw-r--r--src/print.cc4
-rw-r--r--src/session.cc4
-rw-r--r--src/session.h4
-rw-r--r--src/system.hh.in4
-rw-r--r--src/textual.cc57
-rw-r--r--src/times.cc31
-rw-r--r--src/utils.h10
-rw-r--r--src/xact.cc10
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;
}