summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/account.cc30
-rw-r--r--src/account.h18
-rw-r--r--src/filters.cc14
-rw-r--r--src/journal.cc67
-rw-r--r--src/journal.h1
-rw-r--r--src/post.h1
-rw-r--r--src/session.cc16
-rw-r--r--src/system.hh.in3
-rw-r--r--src/textual.cc35
-rw-r--r--src/utils.h15
-rw-r--r--src/xact.cc5
12 files changed, 160 insertions, 51 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6304a50c..a77422db 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -254,7 +254,7 @@ include(GNUInstallDirs)
if(BUILD_LIBRARY)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
- add_library(libledger SHARED ${LEDGER_SOURCES} ${PROJECT_SOURCE_DIR}/lib/sha1.cpp)
+ add_library(libledger SHARED ${LEDGER_SOURCES})
add_ledger_library_dependencies(libledger)
set_target_properties(libledger PROPERTIES
PREFIX ""
@@ -267,11 +267,9 @@ if(BUILD_LIBRARY)
install(TARGETS libledger DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES ${LEDGER_INCLUDES}
- ${PROJECT_SOURCE_DIR}/lib/sha1.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ledger)
else()
- add_executable(ledger
- ${LEDGER_SOURCES} ${PROJECT_SOURCE_DIR}/lib/sha1.cpp main.cc global.cc)
+ add_executable(ledger ${LEDGER_SOURCES} main.cc global.cc)
add_ledger_library_dependencies(ledger)
endif()
diff --git a/src/account.cc b/src/account.cc
index 4b3a4e97..216b15bd 100644
--- a/src/account.cc
+++ b/src/account.cc
@@ -139,6 +139,36 @@ void account_t::add_post(post_t * post)
}
}
+void account_t::add_deferred_post(const string& uuid, post_t * post)
+{
+ if (! deferred_posts)
+ deferred_posts = deferred_posts_map_t();
+
+ deferred_posts_map_t::iterator i = deferred_posts->find(uuid);
+ if (i == deferred_posts->end()) {
+ posts_list lst;
+ lst.push_back(post);
+ deferred_posts->insert(deferred_posts_map_t::value_type(uuid, lst));
+ } else {
+ (*i).second.push_back(post);
+ }
+}
+
+void account_t::apply_deferred_posts()
+{
+ if (deferred_posts) {
+ foreach (deferred_posts_map_t::value_type& pair, *deferred_posts) {
+ foreach (post_t * post, pair.second)
+ post->account->add_post(post);
+ }
+ deferred_posts = none;
+ }
+
+ // Also apply in child accounts
+ foreach (const accounts_map::value_type& pair, accounts)
+ pair.second->apply_deferred_posts();
+}
+
bool account_t::remove_post(post_t * post)
{
// It's possible that 'post' wasn't yet in this account, but try to
diff --git a/src/account.h b/src/account.h
index f6f764cc..d1377c39 100644
--- a/src/account.h
+++ b/src/account.h
@@ -52,6 +52,7 @@ class post_t;
typedef std::list<post_t *> posts_list;
typedef std::map<string, account_t *> accounts_map;
+typedef std::map<string, posts_list> deferred_posts_map_t;
class account_t : public supports_flags<>, public scope_t
{
@@ -61,13 +62,14 @@ class account_t : public supports_flags<>, public scope_t
#define ACCOUNT_GENERATED 0x04 // account never actually existed
public:
- account_t * parent;
- string name;
- optional<string> note;
- unsigned short depth;
- accounts_map accounts;
- posts_list posts;
- optional<expr_t> value_expr;
+ account_t * parent;
+ string name;
+ optional<string> note;
+ unsigned short depth;
+ accounts_map accounts;
+ posts_list posts;
+ optional<deferred_posts_map_t> deferred_posts;
+ optional<expr_t> value_expr;
mutable string _fullname;
#if DOCUMENT_MODEL
@@ -136,6 +138,8 @@ public:
}
void add_post(post_t * post);
+ void add_deferred_post(const string& uuid, post_t * post);
+ void apply_deferred_posts();
bool remove_post(post_t * post);
posts_list::iterator posts_begin() {
diff --git a/src/filters.cc b/src/filters.cc
index 4046ebda..bdc2983b 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -237,7 +237,7 @@ void anonymize_posts::render_commodity(amount_t& amt)
void anonymize_posts::operator()(post_t& post)
{
- SHA1 sha;
+ boost::uuids::detail::sha1 sha;
unsigned int message_digest[5];
bool copy_xact_details = false;
@@ -256,9 +256,9 @@ void anonymize_posts::operator()(post_t& post)
buf << reinterpret_cast<uintmax_t>(post.xact->payee.c_str())
<< integer_gen() << post.xact->payee.c_str();
- sha.Reset();
- sha << buf.str().c_str();
- sha.Result(message_digest);
+ sha.reset();
+ sha.process_bytes(buf.str().c_str(), buf.str().length());
+ sha.get_digest(message_digest);
xact.payee = to_hex(message_digest);
xact.note = none;
@@ -274,9 +274,9 @@ void anonymize_posts::operator()(post_t& post)
std::ostringstream buf;
buf << integer_gen() << acct << acct->fullname();
- sha.Reset();
- sha << buf.str().c_str();
- sha.Result(message_digest);
+ sha.reset();
+ sha.process_bytes(buf.str().c_str(), buf.str().length());
+ sha.get_digest(message_digest);
account_names.push_front(to_hex(message_digest));
}
diff --git a/src/journal.cc b/src/journal.cc
index 160abe06..804c9ee2 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -95,7 +95,7 @@ void journal_t::initialize()
force_checking = false;
check_payees = false;
day_break = false;
- checking_style = CHECK_PERMISSIVE;
+ checking_style = CHECK_NORMAL;
recursive_aliases = false;
}
@@ -363,6 +363,21 @@ namespace {
}
}
+bool lt_posting_account(post_t * left, post_t * right) {
+ return left->account < right->account;
+}
+
+bool is_equivalent_posting(post_t * left, post_t * right)
+{
+ if (left->account != right->account)
+ return false;
+
+ if (left->amount != right->amount)
+ return false;
+
+ return true;
+}
+
bool journal_t::add_xact(xact_t * xact)
{
xact->journal = this;
@@ -385,12 +400,54 @@ bool journal_t::add_xact(xact_t * xact)
// will have been performed by extend_xact, so asserts can still be
// applied to it.
if (optional<value_t> ref = xact->get_tag(_("UUID"))) {
+ std::string uuid = ref->to_string();
std::pair<checksum_map_t::iterator, bool> result
- = checksum_map.insert(checksum_map_t::value_type(ref->to_string(), xact));
+ = checksum_map.insert(checksum_map_t::value_type(uuid, xact));
if (! result.second) {
- // jww (2012-02-27): Confirm that the xact in
- // (*result.first).second is exact match in its significant
- // details to xact.
+ // This UUID has been seen before; apply any postings which the
+ // earlier version may have deferred.
+ foreach (post_t * post, xact->posts) {
+ account_t * acct = post->account;
+ if (acct->deferred_posts) {
+ auto i = acct->deferred_posts->find(uuid);
+ if (i != acct->deferred_posts->end()) {
+ for (post_t * rpost : (*i).second)
+ if (acct == rpost->account)
+ acct->add_post(rpost);
+ acct->deferred_posts->erase(i);
+ }
+ }
+ }
+
+ xact_t * other = (*result.first).second;
+
+ // Copy the two lists of postings (which should be relatively
+ // short), and make sure that the intersection is the empty set
+ // (i.e., that they are the same list).
+ std::vector<post_t *> this_posts(xact->posts.begin(),
+ xact->posts.end());
+ std::sort(this_posts.begin(), this_posts.end(),
+ lt_posting_account);
+ std::vector<post_t *> other_posts(other->posts.begin(),
+ other->posts.end());
+ std::sort(other_posts.begin(), other_posts.end(),
+ lt_posting_account);
+ bool match = std::equal(this_posts.begin(), this_posts.end(),
+ other_posts.begin(), is_equivalent_posting);
+
+ if (! match || this_posts.size() != other_posts.size()) {
+ add_error_context(_("While comparing this previously seen transaction:"));
+ add_error_context(source_context(other->pos->pathname,
+ other->pos->beg_pos,
+ other->pos->end_pos, "> "));
+ add_error_context(_("to this later transaction:"));
+ add_error_context(source_context(xact->pos->pathname,
+ xact->pos->beg_pos,
+ xact->pos->end_pos, "> "));
+ throw_(std::runtime_error,
+ _f("Transactions with the same UUID must have equivalent postings"));
+ }
+
xact->journal = NULL;
return false;
}
diff --git a/src/journal.h b/src/journal.h
index e4763482..ad7b4b48 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -144,6 +144,7 @@ public:
enum checking_style_t {
CHECK_PERMISSIVE,
+ CHECK_NORMAL,
CHECK_WARNING,
CHECK_ERROR
} checking_style;
diff --git a/src/post.h b/src/post.h
index 379844dc..51a75ade 100644
--- a/src/post.h
+++ b/src/post.h
@@ -60,6 +60,7 @@ public:
#define POST_COST_FIXATED 0x0200 // cost is fixed using = indicator
#define POST_COST_VIRTUAL 0x0400 // cost is virtualized: (@)
#define POST_ANONYMIZED 0x0800 // a temporary, anonymous posting
+#define POST_DEFERRED 0x1000 // the account was specified with <angles>
xact_t * xact; // only set for posts of regular xacts
account_t * account;
diff --git a/src/session.cc b/src/session.cc
index b386607a..ec57eab3 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -106,10 +106,6 @@ std::size_t session_t::read_data(const string& master_account)
}
}
- if (HANDLED(explicit))
- journal->force_checking = true;
- if (HANDLED(check_payees))
- journal->check_payees = true;
if (HANDLED(day_break))
journal->day_break = true;
@@ -117,15 +113,21 @@ std::size_t session_t::read_data(const string& master_account)
journal->recursive_aliases = true;
if (HANDLED(no_aliases))
journal->no_aliases = true;
-
+
+ if (HANDLED(explicit))
+ journal->force_checking = true;
+ if (HANDLED(check_payees))
+ journal->check_payees = true;
+
if (HANDLED(permissive))
journal->checking_style = journal_t::CHECK_PERMISSIVE;
else if (HANDLED(pedantic))
journal->checking_style = journal_t::CHECK_ERROR;
else if (HANDLED(strict))
journal->checking_style = journal_t::CHECK_WARNING;
- else if (HANDLED(value_expr_))
- journal->value_expr = HANDLER(value_expr_).str();
+
+ if (HANDLED(value_expr_))
+ journal->value_expr = HANDLER(value_expr_).str();
#if HAVE_BOOST_SERIALIZATION
optional<archive_t> cache;
diff --git a/src/system.hh.in b/src/system.hh.in
index fd5c47c6..8f501587 100644
--- a/src/system.hh.in
+++ b/src/system.hh.in
@@ -56,8 +56,6 @@
#define Ledger_VERSION_PATCH @Ledger_VERSION_PATCH@
#define Ledger_VERSION_DATE @Ledger_VERSION_DATE@
-#define HAVE_CXX11 @HAVE_CXX11@
-
#define HAVE_EDIT @HAVE_EDIT@
#define HAVE_GETTEXT @HAVE_GETTEXT@
@@ -160,7 +158,6 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <gmp.h>
#include <mpfr.h>
-#include "sha1.h"
#include "utf8.h"
#if HAVE_EDIT
diff --git a/src/textual.cc b/src/textual.cc
index 627a1835..55f22a2c 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -77,16 +77,18 @@ namespace {
std::istream& in;
instance_t * parent;
std::list<application_t> apply_stack;
+ bool no_assertions;
#if defined(TIMELOG_SUPPORT)
time_log_t timelog;
#endif
instance_t(parse_context_stack_t& _context_stack,
parse_context_t& _context,
- instance_t * _parent = NULL)
+ instance_t * _parent = NULL,
+ const bool _no_assertions = false)
: context_stack(_context_stack), context(_context),
in(*context.stream.get()), parent(_parent),
- timelog(context) {}
+ no_assertions(_no_assertions), timelog(context) {}
virtual string description() {
return _("textual parser");
@@ -779,8 +781,8 @@ void instance_t::include_directive(char * line)
context_stack.get_current().master = master;
context_stack.get_current().scope = scope;
try {
- instance_t instance(context_stack,
- context_stack.get_current(), this);
+ instance_t instance(context_stack, context_stack.get_current(),
+ this, no_assertions);
instance.apply_stack.push_front(application_t("account", master));
instance.parse();
}
@@ -1430,6 +1432,12 @@ post_t * instance_t::parse_post(char * line,
}
p++; e--;
}
+ else if (*p == '<' && *(e - 1) == '>') {
+ post->add_flags(POST_DEFERRED);
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Parsed a deferred account name");
+ p++; e--;
+ }
string name(p, static_cast<string::size_type>(e - p));
DEBUG("textual.parse", "line " << context.linenum << ": "
@@ -1601,22 +1609,25 @@ post_t * instance_t::parse_post(char * line,
"line " << context.linenum << ": " << "post amount = " << amt);
amount_t diff = amt;
+ amount_t tot;
switch (account_total.type()) {
case value_t::AMOUNT:
- diff -= account_total.as_amount();
+ tot = account_total.as_amount();
break;
case value_t::BALANCE:
if (optional<amount_t> comm_bal =
account_total.as_balance().commodity_amount(amt.commodity()))
- diff -= *comm_bal;
+ tot = *comm_bal;
break;
default:
break;
}
+ diff -= tot;
+
DEBUG("post.assign",
"line " << context.linenum << ": " << "diff = " << diff);
DEBUG("textual.parse", "line " << context.linenum << ": "
@@ -1625,8 +1636,10 @@ post_t * instance_t::parse_post(char * line,
if (! diff.is_zero()) {
if (! post->amount.is_null()) {
diff -= post->amount;
- if (! diff.is_zero())
- throw_(parse_error, _f("Balance assertion off by %1%") % diff);
+ if (! no_assertions && ! diff.is_zero())
+ throw_(parse_error,
+ _f("Balance assertion off by %1% (expected to see %2%)")
+ % diff % tot);
} else {
post->amount = diff;
DEBUG("textual.parse", "line " << context.linenum << ": "
@@ -1909,13 +1922,17 @@ std::size_t journal_t::read_textual(parse_context_stack_t& context_stack)
{
TRACE_START(parsing_total, 1, "Total time spent parsing text:");
{
- instance_t instance(context_stack, context_stack.get_current());
+ instance_t instance(context_stack, context_stack.get_current(), NULL,
+ checking_style == journal_t::CHECK_PERMISSIVE);
instance.apply_stack.push_front
(application_t("account", context_stack.get_current().master));
instance.parse();
}
TRACE_STOP(parsing_total, 1);
+ // Apply any deferred postings at this time
+ master->apply_deferred_posts();
+
// These tracers were started in textual.cc
TRACE_FINISH(xact_text, 1);
TRACE_FINISH(xact_details, 1);
diff --git a/src/utils.h b/src/utils.h
index 70b3bae9..c3dcf562 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -44,6 +44,8 @@
#ifndef _UTILS_H
#define _UTILS_H
+#include <boost/uuid/sha1.hpp>
+
/**
* @name Default values
*/
@@ -483,11 +485,7 @@ inline void check_for_signal() {
/*@{*/
#define foreach BOOST_FOREACH
-#if HAVE_CXX11
using std::unique_ptr;
-#else
-#define unique_ptr std::auto_ptr
-#endif
namespace ledger {
@@ -625,11 +623,12 @@ inline string to_hex(unsigned int * message_digest, const int len = 1)
inline string sha1sum(const string& str)
{
- SHA1 sha;
- sha.Reset();
- sha << str.c_str();
+ boost::uuids::detail::sha1 sha;
+
+ sha.process_bytes(str.c_str(), str.length());
+
unsigned int message_digest[5];
- sha.Result(message_digest);
+ sha.get_digest(message_digest);
return to_hex(message_digest, 5);
}
diff --git a/src/xact.cc b/src/xact.cc
index 58d2ac33..c7a9c6e3 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -393,7 +393,10 @@ bool xact_base_t::finalize()
some_null = true;
}
- post->account->add_post(post);
+ if (post->has_flags(POST_DEFERRED))
+ 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);