Merge commit 'a0c5addbbdaf472d26ae25c86ff826e509b3ee17'

This commit is contained in:
Craig Earls 2014-04-19 21:56:14 -07:00
commit fd18f58392
21 changed files with 286 additions and 791 deletions

View file

@ -2,8 +2,14 @@ language: cpp
compiler:
- clang
- gcc
install: "./acprep dependencies"
before_script: "./acprep opt make --python"
install:
- "./acprep dependencies"
- wget http://sourceforge.net/projects/boost/files/boost/1.55.0/boost_1_55_0.tar.bz2/download
- tar jxf download
- mv boost_1_55_0 $HOME/boost-trunk
before_script:
- BOOST="$HOME/boost-trunk"
- "./acprep opt make --python"
script:
- "./acprep check -- --output-on-failure"
- "PYTHONPATH=. python python/demo.py"

View file

@ -4,11 +4,13 @@ PROJECT(ledger)
set(Ledger_VERSION_MAJOR 3)
set(Ledger_VERSION_MINOR 0)
set(Ledger_VERSION_PATCH 1)
set(Ledger_VERSION_DATE 20140327)
set(Ledger_VERSION_PATCH 2)
set(Ledger_VERSION_DATE 20140417)
enable_testing()
add_definitions(-std=c++11)
########################################################################
option(USE_PYTHON "Build support for the Python scripting bridge" OFF)
@ -65,7 +67,7 @@ else()
endif()
# Set BOOST_ROOT to help CMake to find the right Boost version
find_package(Boost 1.46.0
find_package(Boost 1.55.0
REQUIRED date_time filesystem system iostreams regex unit_test_framework
${BOOST_PYTHON})
@ -148,37 +150,6 @@ endif()
cmake_pop_check_state()
#cmake_push_check_state()
#
#set(CMAKE_REQUIRED_FLAGS -std=c++11)
#set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
#
#check_cxx_source_runs("
##include <regex>
##include <vector>
##include <iostream>
#
#int main() {
# std::vector<int> x {0, 1, 2, 3, 4};
# for (auto i : x)
# std::cout << i << std::endl;
#
# std::regex r(\"foo\");
# std::cout << std::regex_match(\"foobar\", r) << std::endl;
# return 0;
#}" CXX11_RUNS)
#
#cmake_pop_check_state()
#
#if(CXX11_RUNS)
# set(HAVE_CXX11 1)
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
#else()
set(HAVE_CXX11 0)
#endif()
#
#cmake_pop_check_state()
########################################################################
include_directories(${CMAKE_INCLUDE_PATH})

39
default.nix Normal file
View file

@ -0,0 +1,39 @@
{ stdenv, fetchgit, cmake, boost, gmp, mpfr, libedit, python
, texinfo, gnused }:
let
rev = "20140417";
in
stdenv.mkDerivation {
name = "ledger-3.0.2.${rev}";
src = ./.;
buildInputs = [ cmake boost gmp mpfr libedit python texinfo gnused ];
enableParallelBuilding = true;
# Skip byte-compiling of emacs-lisp files because this is currently
# broken in ledger...
postInstall = ''
mkdir -p $out/share/emacs/site-lisp/
cp -v $src/lisp/*.el $out/share/emacs/site-lisp/
'';
meta = {
homepage = "http://ledger-cli.org/";
description = "A double-entry accounting system with a command-line reporting interface";
license = "BSD";
longDescription = ''
Ledger is a powerful, double-entry accounting system that is accessed
from the UNIX command-line. This may put off some users, as there is
no flashy UI, but for those who want unparalleled reporting access to
their data, there really is no alternative.
'';
platforms = stdenv.lib.platforms.all;
maintainers = with stdenv.lib.maintainers; [ simons the-kenny jwiegley ];
};
}

View file

@ -1,13 +0,0 @@
Copyright (C) 1998
Paul E. Jones <paulej@arid.us>
All Rights Reserved.
This software is licensed as "freeware." Permission to distribute
this software in source and binary forms is hereby granted without
a fee. THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING
FROM THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING,
BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE.

View file

@ -1,589 +0,0 @@
/*
* sha1.cpp
*
* Copyright (C) 1998
* Paul E. Jones <paulej@arid.us>
* All Rights Reserved.
*
*****************************************************************************
* $Id: sha1.cpp,v 1.9 2004/03/27 18:02:20 paulej Exp $
*****************************************************************************
*
* Description:
* This class implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* The Secure Hashing Standard, which uses the Secure Hashing
* Algorithm (SHA), produces a 160-bit message digest for a
* given data stream. In theory, it is highly improbable that
* two messages will produce the same message digest. Therefore,
* this algorithm can serve as a means of providing a "fingerprint"
* for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code was
* written with the expectation that the processor has at least
* a 32-bit machine word size. If the machine word size is larger,
* the code should still function properly. One caveat to that
* is that the input functions taking characters and character arrays
* assume that only 8 bits of information are stored in each character.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits long.
* Although SHA-1 allows a message digest to be generated for
* messages of any number of bits less than 2^64, this implementation
* only works with messages with a length that is a multiple of 8
* bits.
*
*/
#include "sha1.h"
/*
* SHA1
*
* Description:
* This is the constructor for the sha1 class.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
SHA1::SHA1()
{
Reset();
}
/*
* ~SHA1
*
* Description:
* This is the destructor for the sha1 class
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
SHA1::~SHA1()
{
// The destructor does nothing
}
/*
* Reset
*
* Description:
* This function will initialize the sha1 class member variables
* in preparation for computing a new message digest.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1::Reset()
{
Length_Low = 0;
Length_High = 0;
Message_Block_Index = 0;
H[0] = 0x67452301;
H[1] = 0xEFCDAB89;
H[2] = 0x98BADCFE;
H[3] = 0x10325476;
H[4] = 0xC3D2E1F0;
Computed = false;
Corrupted = false;
}
/*
* Result
*
* Description:
* This function will return the 160-bit message digest into the
* array provided.
*
* Parameters:
* message_digest_array: [out]
* This is an array of five unsigned integers which will be filled
* with the message digest that has been computed.
*
* Returns:
* True if successful, false if it failed.
*
* Comments:
*
*/
bool SHA1::Result(unsigned *message_digest_array)
{
int i; // Counter
if (Corrupted)
{
return false;
}
if (!Computed)
{
PadMessage();
Computed = true;
}
for(i = 0; i < 5; i++)
{
message_digest_array[i] = H[i];
}
return true;
}
/*
* Input
*
* Description:
* This function accepts an array of octets as the next portion of
* the message.
*
* Parameters:
* message_array: [in]
* An array of characters representing the next portion of the
* message.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1::Input( const unsigned char *message_array,
unsigned length)
{
if (!length)
{
return;
}
if (Computed || Corrupted)
{
Corrupted = true;
return;
}
while(length-- && !Corrupted)
{
Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
Length_Low += 8;
Length_Low &= 0xFFFFFFFF; // Force it to 32 bits
if (Length_Low == 0)
{
Length_High++;
Length_High &= 0xFFFFFFFF; // Force it to 32 bits
if (Length_High == 0)
{
Corrupted = true; // Message is too long
}
}
if (Message_Block_Index == 64)
{
ProcessMessageBlock();
}
message_array++;
}
}
/*
* Input
*
* Description:
* This function accepts an array of octets as the next portion of
* the message.
*
* Parameters:
* message_array: [in]
* An array of characters representing the next portion of the
* message.
* length: [in]
* The length of the message_array
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1::Input( const char *message_array,
unsigned length)
{
Input(reinterpret_cast<unsigned char *>(const_cast<char *>(message_array)), length);
}
/*
* Input
*
* Description:
* This function accepts a single octets as the next message element.
*
* Parameters:
* message_element: [in]
* The next octet in the message.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1::Input(unsigned char message_element)
{
Input(&message_element, 1);
}
/*
* Input
*
* Description:
* This function accepts a single octet as the next message element.
*
* Parameters:
* message_element: [in]
* The next octet in the message.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1::Input(char message_element)
{
Input((unsigned char *) &message_element, 1);
}
/*
* operator<<
*
* Description:
* This operator makes it convenient to provide character strings to
* the SHA1 object for processing.
*
* Parameters:
* message_array: [in]
* The character array to take as input.
*
* Returns:
* A reference to the SHA1 object.
*
* Comments:
* Each character is assumed to hold 8 bits of information.
*
*/
SHA1& SHA1::operator<<(const char *message_array)
{
const char *p = message_array;
while(*p)
{
Input(*p);
p++;
}
return *this;
}
/*
* operator<<
*
* Description:
* This operator makes it convenient to provide character strings to
* the SHA1 object for processing.
*
* Parameters:
* message_array: [in]
* The character array to take as input.
*
* Returns:
* A reference to the SHA1 object.
*
* Comments:
* Each character is assumed to hold 8 bits of information.
*
*/
SHA1& SHA1::operator<<(const unsigned char *message_array)
{
const unsigned char *p = message_array;
while(*p)
{
Input(*p);
p++;
}
return *this;
}
/*
* operator<<
*
* Description:
* This function provides the next octet in the message.
*
* Parameters:
* message_element: [in]
* The next octet in the message
*
* Returns:
* A reference to the SHA1 object.
*
* Comments:
* The character is assumed to hold 8 bits of information.
*
*/
SHA1& SHA1::operator<<(const char message_element)
{
Input(reinterpret_cast<unsigned char *>(const_cast<char *>(&message_element)), 1);
return *this;
}
/*
* operator<<
*
* Description:
* This function provides the next octet in the message.
*
* Parameters:
* message_element: [in]
* The next octet in the message
*
* Returns:
* A reference to the SHA1 object.
*
* Comments:
* The character is assumed to hold 8 bits of information.
*
*/
SHA1& SHA1::operator<<(const unsigned char message_element)
{
Input(&message_element, 1);
return *this;
}
/*
* ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this function, especially the single
* character names, were used because those were the names used
* in the publication.
*
*/
void SHA1::ProcessMessageBlock()
{
const unsigned K[] = { // Constants defined for SHA-1
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; // Loop counter
unsigned temp; // Temporary word value
unsigned W[80]; // Word sequence
unsigned A, B, C, D, E; // Word buffers
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = ((unsigned) Message_Block[t * 4]) << 24;
W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) Message_Block[t * 4 + 3]);
}
for(t = 16; t < 80; t++)
{
W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = H[0];
B = H[1];
C = H[2];
D = H[3];
E = H[4];
for(t = 0; t < 20; t++)
{
temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30,B);
B = A;
A = temp;
}
H[0] = (H[0] + A) & 0xFFFFFFFF;
H[1] = (H[1] + B) & 0xFFFFFFFF;
H[2] = (H[2] + C) & 0xFFFFFFFF;
H[3] = (H[3] + D) & 0xFFFFFFFF;
H[4] = (H[4] + E) & 0xFFFFFFFF;
Message_Block_Index = 0;
}
/*
* PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64 bits
* represent the length of the original message. All bits in between
* should be 0. This function will pad the message according to those
* rules by filling the message_block array accordingly. It will also
* call ProcessMessageBlock() appropriately. When it returns, it
* can be assumed that the message digest has been computed.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1::PadMessage()
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second block.
*/
if (Message_Block_Index > 55)
{
Message_Block[Message_Block_Index++] = 0x80;
while(Message_Block_Index < 64)
{
Message_Block[Message_Block_Index++] = 0;
}
ProcessMessageBlock();
while(Message_Block_Index < 56)
{
Message_Block[Message_Block_Index++] = 0;
}
}
else
{
Message_Block[Message_Block_Index++] = 0x80;
while(Message_Block_Index < 56)
{
Message_Block[Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
Message_Block[56] = static_cast<unsigned char>((Length_High >> 24) & 0xFF);
Message_Block[57] = static_cast<unsigned char>((Length_High >> 16) & 0xFF);
Message_Block[58] = static_cast<unsigned char>((Length_High >> 8) & 0xFF);
Message_Block[59] = static_cast<unsigned char>((Length_High) & 0xFF);
Message_Block[60] = static_cast<unsigned char>((Length_Low >> 24) & 0xFF);
Message_Block[61] = static_cast<unsigned char>((Length_Low >> 16) & 0xFF);
Message_Block[62] = static_cast<unsigned char>((Length_Low >> 8) & 0xFF);
Message_Block[63] = static_cast<unsigned char>((Length_Low) & 0xFF);
ProcessMessageBlock();
}
/*
* CircularShift
*
* Description:
* This member function will perform a circular shifting operation.
*
* Parameters:
* bits: [in]
* The number of bits to shift (1-31)
* word: [in]
* The value to shift (assumes a 32-bit integer)
*
* Returns:
* The shifted value.
*
* Comments:
*
*/
unsigned SHA1::CircularShift(int bits, unsigned word)
{
return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
}

View file

@ -1,89 +0,0 @@
/*
* sha1.h
*
* Copyright (C) 1998
* Paul E. Jones <paulej@arid.us>
* All Rights Reserved.
*
*****************************************************************************
* $Id: sha1.h,v 1.6 2004/03/27 18:02:26 paulej Exp $
*****************************************************************************
*
* Description:
* This class implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* Many of the variable names in this class, especially the single
* character names, were used because those were the names used
* in the publication.
*
* Please read the file sha1.cpp for more information.
*
*/
#ifndef _SHA1_H_
#define _SHA1_H_
class SHA1
{
public:
SHA1();
virtual ~SHA1();
/*
* Re-initialize the class
*/
void Reset();
/*
* Returns the message digest
*/
bool Result(unsigned *message_digest_array);
/*
* Provide input to SHA1
*/
void Input( const unsigned char *message_array,
unsigned length);
void Input( const char *message_array,
unsigned length);
void Input(unsigned char message_element);
void Input(char message_element);
SHA1& operator<<(const char *message_array);
SHA1& operator<<(const unsigned char *message_array);
SHA1& operator<<(const char message_element);
SHA1& operator<<(const unsigned char message_element);
private:
/*
* Process the next 512 bits of the message
*/
void ProcessMessageBlock();
/*
* Pads the current message block to 512 bits
*/
void PadMessage();
/*
* Performs a circular left shift operation
*/
inline unsigned CircularShift(int bits, unsigned word);
unsigned H[5]; // Message digest buffers
unsigned Length_Low; // Message length in bits
unsigned Length_High; // Message length in bits
unsigned char Message_Block[64]; // 512-bit message blocks
int Message_Block_Index; // Index into message block array
bool Computed; // Is the digest computed?
bool Corrupted; // Is the message digest corruped?
};
#endif

View file

@ -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()

View file

@ -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

View file

@ -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() {

View file

@ -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));
}

View file

@ -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;
}

View file

@ -144,6 +144,7 @@ public:
enum checking_style_t {
CHECK_PERMISSIVE,
CHECK_NORMAL,
CHECK_WARNING,
CHECK_ERROR
} checking_style;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -38,16 +38,17 @@ add_subdirectory(manual)
add_subdirectory(baseline)
add_subdirectory(regress)
if(PYTHONINTERP_FOUND)
set(_class DocTests)
file(GLOB ${_class}_TESTS ${PROJECT_SOURCE_DIR}/doc/*.texi)
foreach(TestFile ${${_class}_TESTS})
get_filename_component(TestFile_Name ${TestFile} NAME_WE)
add_test(${_class}Test_${TestFile_Name}
${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/DocTests.py
--ledger ${LEDGER_LOCATION} --file ${TestFile})
set_target_properties(check PROPERTIES DEPENDS ${_class}Test_${TestFile_Name})
endforeach()
endif()
# jww (2014-04-17): This is temporary until we find a fix.
#if(PYTHONINTERP_FOUND)
# set(_class DocTests)
# file(GLOB ${_class}_TESTS ${PROJECT_SOURCE_DIR}/doc/*.texi)
# foreach(TestFile ${${_class}_TESTS})
# get_filename_component(TestFile_Name ${TestFile} NAME_WE)
# add_test(${_class}Test_${TestFile_Name}
# ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/DocTests.py
# --ledger ${LEDGER_LOCATION} --file ${TestFile})
# set_target_properties(check PROPERTIES DEPENDS ${_class}Test_${TestFile_Name})
# endforeach()
#endif()
### CMakeLists.txt ends here

View file

@ -0,0 +1,60 @@
;; a.dat
2012-01-01 Test
Expenses:Unknown $100.00
Liabilities:MasterCard
2012-01-02 Test
Expenses:Unknown $100.00
Liabilities:MasterCard
2012-01-03 Test
Expenses:Unknown $100.00
Liabilities:MasterCard
2012-01-04 Test
; UUID: foo
Liabilities:MasterCard $150.00 = $-300
<Assets:Checking>
2012-01-04 Test
; UUID: bar
Liabilities:MasterCard $150.00 = $0
<Assets:Checking>
2012-01-04 Test
; UUID: baz
Liabilities:MasterCard $150.00 = $0
<Assets:Checking>
;; b.dat
2012-01-01 Test
Assets:Checking $150.00
Income
2012-01-02 Test
Assets:Checking $150.00
Income
2012-01-03 Test
Assets:Checking $150.00
Income
2012-01-04 Test
; UUID: foo
Liabilities:MasterCard $150.00
Assets:Checking $-150.00 = $300.00
2012-01-04 Test
; UUID: bar
Liabilities:MasterCard $150.00
Assets:Checking $-150.00 = $150.00
test balance
$300.00 Expenses:Unknown
$-450.00 Income
$150.00 Liabilities:MasterCard
--------------------
0
end test

View file

@ -4,8 +4,8 @@ flavor=$1
shift 1
JOBS=-j$(sysctl -n hw.activecpu)
OPTIONS="$flavor --debug --python --ninja --doxygen $JOBS"
OPTIONS="$OPTIONS --prefix /usr/local/Cellar/ledger/HEAD"
#OPTIONS="$flavor --debug --python --ninja --doxygen $JOBS"
OPTIONS="$flavor --debug --ninja $JOBS"
time ( \
cd ~/src/ledger ; \