diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/account.cc | 30 | ||||
-rw-r--r-- | src/account.h | 18 | ||||
-rw-r--r-- | src/filters.cc | 14 | ||||
-rw-r--r-- | src/journal.cc | 67 | ||||
-rw-r--r-- | src/journal.h | 1 | ||||
-rw-r--r-- | src/post.h | 1 | ||||
-rw-r--r-- | src/session.cc | 16 | ||||
-rw-r--r-- | src/system.hh.in | 3 | ||||
-rw-r--r-- | src/textual.cc | 35 | ||||
-rw-r--r-- | src/utils.h | 15 | ||||
-rw-r--r-- | src/xact.cc | 5 |
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; @@ -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); |