diff options
Diffstat (limited to 'src')
131 files changed, 1248 insertions, 413 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fd7d295..7b10061f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,9 +49,10 @@ set(LEDGER_SOURCES times.cc error.cc utils.cc - strptime.cc) + strptime.cc + wcwidth.cc) -if(HAVE_BOOST_PYTHON) +if (HAVE_BOOST_PYTHON) list(APPEND LEDGER_SOURCES py_account.cc py_amount.cc @@ -134,8 +135,8 @@ set(LEDGER_INCLUDES strptime.h ${PROJECT_BINARY_DIR}/system.hh) -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - if((CMAKE_CXX_COMPILER MATCHES "clang") OR (CMAKE_CXX_COMPILER MATCHES "cxx")) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + if ((CMAKE_CXX_COMPILER MATCHES "clang") OR (CMAKE_CXX_COMPILER MATCHES "cxx")) add_definitions( -Weverything -Wno-disabled-macro-expansion @@ -155,7 +156,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") set_source_files_properties( ${${_src_list}} PROPERTIES COMPILE_FLAGS "-include ${_header_filename}") - if(_other_srcs) + if (_other_srcs) set_source_files_properties( ${_other_srcs} PROPERTIES COMPILE_FLAGS "-include ${_header_filename}") endif() @@ -163,6 +164,13 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(_args ${CMAKE_CXX_FLAGS}) list(APPEND _args ${CMAKE_CXX_FLAGS_DEBUG}) + if (BUILD_LIBRARY) + list(APPEND _args ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}) + endif() + list(APPEND _args "-std=c++11 ") + if (CYGWIN) + list(APPEND _args "-U__STRICT_ANSI__") + endif() list(APPEND _args "-x c++-header " ${_inc}) list(APPEND _args -c ${_header_filename} -o ${_pch_filename}) @@ -181,7 +189,6 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") elseif(CMAKE_CXX_COMPILER MATCHES "g\\+\\+") set(GXX_WARNING_FLAGS - -ansi -pedantic -Wall -Winvalid-pch @@ -207,7 +214,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") set_source_files_properties( ${${_src_list}} PROPERTIES COMPILE_FLAGS "-Winvalid-pch") - if(_other_srcs) + if (_other_srcs) set_source_files_properties( ${_other_srcs} PROPERTIES COMPILE_FLAGS "-Winvalid-pch") endif() @@ -215,7 +222,14 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(_args ${CMAKE_CXX_FLAGS}) list(APPEND _args ${CMAKE_CXX_FLAGS_DEBUG}) + if (BUILD_LIBRARY) + list(APPEND _args ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}) + endif() list(APPEND _args ${GXX_WARNING_FLAGS}) + list(APPEND _args "-std=c++11 ") + if (CYGWIN) + list(APPEND _args "-U__STRICT_ANSI__") + endif() list(APPEND _args "-x c++-header " ${_inc}) list(APPEND _args -c ${_header_filename} -o ${_gch_filename}) @@ -243,21 +257,63 @@ endif() add_pch_rule(${PROJECT_BINARY_DIR}/system.hh LEDGER_SOURCES main.cc global.cc) -if(BUILD_LIBRARY) - add_library(libledger ${LEDGER_SOURCES} ${PROJECT_SOURCE_DIR}/lib/sha1.cpp) - set_target_properties(libledger PROPERTIES OUTPUT_NAME ledger) +include(GNUInstallDirs) + +if (BUILD_LIBRARY) + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + add_library(libledger SHARED ${LEDGER_SOURCES}) + add_ledger_library_dependencies(libledger) + set_target_properties(libledger PROPERTIES + PREFIX "" + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" + VERSION ${Ledger_VERSION_MAJOR} + SOVERSION ${Ledger_VERSION_MAJOR}) add_executable(ledger main.cc global.cc) + target_link_libraries(ledger libledger) - install(TARGETS libledger DESTINATION lib) - install(FILES ${LEDGER_INCLUDES} DESTINATION include/ledger) + install(TARGETS libledger DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES ${LEDGER_INCLUDES} + 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() -add_ledger_library_dependencies(ledger) +if (USE_PYTHON) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c + "from __future__ import print_function +import distutils.sysconfig as s +print(s.get_python_lib(True, prefix=''))" + OUTPUT_VARIABLE _TMP_PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PYTHON_SITE_PACKAGES ${_TMP_PYTHON_SITE_PACKAGES} + CACHE PATH "python module directory (${_TMP_PYTHON_SITE_PACKAGES})") + + if (PYTHON_SITE_PACKAGES) + if (WIN32 AND NOT CYGWIN) + set(_ledger_python_module_name "ledger.pyd") + elseif(CMAKE_HOST_APPLE) + set(_ledger_python_module_name "ledger.so") + else() + set(_ledger_python_module_name "ledger${CMAKE_SHARED_LIBRARY_SUFFIX}") + endif() + + # FIXME: symlink would be sufficient: + # maybe using install(CODE "...") and + # execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink ...). + # Windows will need a special case due to not supporting symlinks. + add_custom_command( + TARGET libledger POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $<TARGET_FILE:libledger> "${CMAKE_BINARY_DIR}/${_ledger_python_module_name}") + install( + FILES "${CMAKE_BINARY_DIR}/${_ledger_python_module_name}" + DESTINATION ${PYTHON_SITE_PACKAGES}) + else() + message(WARNING "PYTHON_SITE_PACKAGES not set. Will not install python module.") + endif() +endif() -install(TARGETS ledger DESTINATION bin) +install(TARGETS ledger DESTINATION ${CMAKE_INSTALL_BINDIR}) ### CMakeLists.txt ends here diff --git a/src/account.cc b/src/account.cc index 58812db9..358aa09a 100644 --- a/src/account.cc +++ b/src/account.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -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 b726facf..76e839eb 100644 --- a/src/account.h +++ b/src/account.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -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() { @@ -257,7 +261,11 @@ public: mutable optional<xdata_t> xdata_; bool has_xdata() const { +#if BOOST_VERSION >= 105600 + return xdata_ != NULL; +#else return xdata_; +#endif } void clear_xdata(); xdata_t& xdata() { diff --git a/src/amount.cc b/src/amount.cc index 6ff2f4dd..6ddcdb4f 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -301,7 +301,7 @@ void amount_t::_copy(const amount_t& amt) quantity = new bigint_t(*amt.quantity); } else { quantity = amt.quantity; - DEBUG("amounts.refs", + DEBUG("amount.refs", quantity << " refc++, now " << (quantity->refc + 1)); quantity->refc++; } @@ -339,7 +339,7 @@ void amount_t::_release() { VERIFY(valid()); - DEBUG("amounts.refs", quantity << " refc--, now " << (quantity->refc - 1)); + DEBUG("amount.refs", quantity << " refc--, now " << (quantity->refc - 1)); if (--quantity->refc == 0) { if (quantity->has_flags(BIGINT_BULK_ALLOC)) @@ -928,7 +928,7 @@ void amount_t::annotate(const annotation_t& details) } assert(this_base); - DEBUG("amounts.commodities", "Annotating commodity for amount " + DEBUG("amount.commodities", "Annotating commodity for amount " << *this << std::endl << details); if (commodity_t * ann_comm = @@ -939,7 +939,7 @@ void amount_t::annotate(const annotation_t& details) assert(false); #endif - DEBUG("amounts.commodities", "Annotated amount is " << *this); + DEBUG("amount.commodities", "Annotated amount is " << *this); } bool amount_t::has_annotation() const diff --git a/src/amount.h b/src/amount.h index 938d4b7b..ea5cadd6 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,9 +1,9 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 met: + * modification, are permitted provided that the following conditions are + * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. diff --git a/src/annotate.cc b/src/annotate.cc index 8816a89c..7c54edef 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -187,8 +187,8 @@ void annotation_t::parse(std::istream& in) } while (true); #if DEBUG_ON - if (SHOW_DEBUG("amounts.commodities") && *this) { - DEBUG("amounts.commodities", + if (SHOW_DEBUG("amount.commodities") && *this) { + DEBUG("amount.commodities", "Parsed commodity annotations: " << std::endl << *this); } #endif @@ -206,7 +206,7 @@ void annotation_t::print(std::ostream& out, bool keep_base, if (date && (! no_computed_annotations || ! has_flags(ANNOTATION_DATE_CALCULATED))) - out << " [" << format_date(*date, FMT_WRITTEN) << ']'; + out << " [" << format_date(*date, FMT_PRINTED) << ']'; if (tag && (! no_computed_annotations || ! has_flags(ANNOTATION_TAG_CALCULATED))) diff --git a/src/annotate.h b/src/annotate.h index 998f7c83..c0fbcd3d 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/archive.cc b/src/archive.cc index 6ac1c580..9ae04e85 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/archive.h b/src/archive.h index 5c15a9fb..485f9606 100644 --- a/src/archive.h +++ b/src/archive.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/balance.cc b/src/balance.cc index b702cb7b..36eab7d4 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/balance.h b/src/balance.h index a1ae51ef..752bb4d6 100644 --- a/src/balance.h +++ b/src/balance.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/chain.cc b/src/chain.cc index b2f6f203..42f474c4 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/chain.h b/src/chain.h index de7f68c7..224bed21 100644 --- a/src/chain.h +++ b/src/chain.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/commodity.cc b/src/commodity.cc index 535b31c9..398245fb 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/commodity.h b/src/commodity.h index d521d80e..3d1ddf04 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/compare.cc b/src/compare.cc index 946cd835..db169cfc 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/compare.h b/src/compare.h index 4a91f49f..554e84aa 100644 --- a/src/compare.h +++ b/src/compare.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/context.h b/src/context.h index e5457641..e787f335 100644 --- a/src/context.h +++ b/src/context.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -101,10 +101,10 @@ public: } void warning(const string& what) const { - warning_func(location() + what); + warning_func(location() + " " + what); } void warning(const boost::format& what) const { - warning_func(location() + string(what.str())); + warning_func(location() + " " + string(what.str())); } }; diff --git a/src/convert.cc b/src/convert.cc index 38ef2c22..a4454c14 100644 --- a/src/convert.cc +++ b/src/convert.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -86,7 +86,7 @@ value_t convert_command(call_scope_t& args) if (entry != journal.checksum_map.end()) { INFO(file_context(reader.get_pathname(), reader.get_linenum()) - << "Ignoring known UUID " << ref); + << " " << "Ignoring known UUID " << ref); checked_delete(xact); // ignore it continue; } diff --git a/src/convert.h b/src/convert.h index 17fb4505..020dfea2 100644 --- a/src/convert.h +++ b/src/convert.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -186,7 +186,7 @@ xact_t * csv_reader::read_xact(bool rich_data) case FIELD_PAYEE: { bool found = false; - foreach (payee_mapping_t& value, context.journal->payee_mappings) { + foreach (payee_alias_mapping_t& value, context.journal->payee_alias_mappings) { DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); if (value.first.match(field)) { xact->payee = value.second; @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/draft.cc b/src/draft.cc index 6aef2f12..78e53ccd 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/draft.h b/src/draft.h index 1cc362ba..7ba40640 100644 --- a/src/draft.h +++ b/src/draft.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/emacs.cc b/src/emacs.cc index 9bfd1e4b..dc16c002 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -30,7 +30,7 @@ */ #include <system.hh> - +#include <boost/algorithm/string.hpp> #include "emacs.h" #include "xact.h" #include "post.h" @@ -38,78 +38,84 @@ namespace ledger { -void format_emacs_posts::write_xact(xact_t& xact) -{ - if (xact.pos) - out << "\"" << xact.pos->pathname.string() << "\" " - << xact.pos->beg_line << " "; - else - out << "\"\" " << -1 << " "; - - tm when = gregorian::to_tm(xact.date()); - std::time_t date = std::mktime(&when); - - out << "(" << (date / 65536) << " " << (date % 65536) << " 0) "; - - if (xact.code) - out << "\"" << *xact.code << "\" "; - else - out << "nil "; - - if (xact.payee.empty()) - out << "nil"; - else - out << "\"" << xact.payee << "\""; - - out << "\n"; -} - -void format_emacs_posts::operator()(post_t& post) -{ - if (! post.has_xdata() || - ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { - if (! last_xact) { - out << "(("; - write_xact(*post.xact); - } - else if (post.xact != last_xact) { - out << ")\n ("; - write_xact(*post.xact); - } - else { - out << "\n"; - } - - if (post.pos) - out << " (" << post.pos->beg_line << " "; - else - out << " (" << -1 << " "; - - out << "\"" << post.reported_account()->fullname() << "\" \"" - << post.amount << "\""; - - switch (post.state()) { - case item_t::UNCLEARED: - out << " nil"; - break; - case item_t::CLEARED: - out << " t"; - break; - case item_t::PENDING: - out << " pending"; - break; - } - - if (post.cost) - out << " \"" << *post.cost << "\""; - if (post.note) - out << " \"" << *post.note << "\""; - out << ")"; - - last_xact = post.xact; - - post.xdata().add_flags(POST_EXT_DISPLAYED); - } -} + void format_emacs_posts::write_xact(xact_t& xact) + { + if (xact.pos) + out << "\"" << xact.pos->pathname.string() << "\" " + << xact.pos->beg_line << " "; + else + out << "\"\" " << -1 << " "; + + tm when = gregorian::to_tm(xact.date()); + std::time_t date = std::mktime(&when); + + out << "(" << (date / 65536) << " " << (date % 65536) << " 0) "; + + if (xact.code) + out << "\"" << *xact.code << "\" "; + else + out << "nil "; + + if (xact.payee.empty()) + out << "nil"; + else + out << "\"" << xact.payee << "\""; + + out << "\n"; + } + + void format_emacs_posts::operator()(post_t& post) + { + if (! post.has_xdata() || + ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { + if (! last_xact) { + out << "(("; + write_xact(*post.xact); + } + else if (post.xact != last_xact) { + out << ")\n ("; + write_xact(*post.xact); + } + else { + out << "\n"; + } + + if (post.pos) + out << " (" << post.pos->beg_line << " "; + else + out << " (" << -1 << " "; + + out << "\"" << post.reported_account()->fullname() << "\" \"" + << post.amount << "\""; + + switch (post.state()) { + case item_t::UNCLEARED: + out << " nil"; + break; + case item_t::CLEARED: + out << " t"; + break; + case item_t::PENDING: + out << " pending"; + break; + } + + if (post.cost) + out << " \"" << *post.cost << "\""; + if (post.note) + out << " \"" << escape_string(*post.note) << "\""; + out << ")"; + + last_xact = post.xact; + + post.xdata().add_flags(POST_EXT_DISPLAYED); + } + } + + string format_emacs_posts::escape_string(string raw){ + replace_all(raw, "\\", "\\\\"); + replace_all(raw, "\"", "\\\""); + return raw; + } } // namespace ledger diff --git a/src/emacs.h b/src/emacs.h index 368c4c4d..fef7a882 100644 --- a/src/emacs.h +++ b/src/emacs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -72,6 +72,7 @@ public: out.flush(); } virtual void operator()(post_t& post); + virtual string escape_string(string raw); }; } // namespace ledger diff --git a/src/error.cc b/src/error.cc index acee85c9..90697c5f 100644 --- a/src/error.cc +++ b/src/error.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -49,7 +49,7 @@ string error_context() string file_context(const path& file, const std::size_t line) { std::ostringstream buf; - buf << '"' << file.string() << "\", line " << line << ": "; + buf << '"' << file.string() << "\", line " << line << ":"; return buf.str(); } diff --git a/src/error.h b/src/error.h index 93d92109..31fa76a3 100644 --- a/src/error.h +++ b/src/error.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/expr.cc b/src/expr.cc index aa1e07d2..e38d794f 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/exprbase.h b/src/exprbase.h index 88d6f118..b88fcd7d 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/filters.cc b/src/filters.cc index 2b4fe1bf..fd30c966 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -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)); } @@ -1002,7 +1002,7 @@ void interval_posts::flush() sort_posts_by_date()); // Determine the beginning interval by using the earliest post - if (all_posts.front() && + if (all_posts.size() > 0 && all_posts.front() && ! interval.find_period(all_posts.front()->date())) throw_(std::logic_error, _("Failed to find period for interval report")); diff --git a/src/filters.h b/src/filters.h index d6e1b6fc..9b745235 100644 --- a/src/filters.h +++ b/src/filters.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/flags.h b/src/flags.h index d584e2d0..f3593517 100644 --- a/src/flags.h +++ b/src/flags.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/format.cc b/src/format.cc index e0f74616..2887a387 100644 --- a/src/format.cc +++ b/src/format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -74,17 +74,18 @@ namespace { char letter; const char * expr; } single_letter_mappings[] = { - { 'd', "date" }, + { 'd', "aux_date ? format_date(date) + \"=\" + format_date(aux_date) : format_date(date)" }, + { 'D', "date" }, { 'S', "filename" }, { 'B', "beg_pos" }, { 'b', "beg_line" }, { 'E', "end_pos" }, { 'e', "end_line" }, - { 'X', "cleared" }, - { 'Y', "xact.cleared" }, - { 'C', "code" }, + { 'X', "\"* \" if cleared" }, + { 'Y', "\"* \" if xact.cleared" }, + { 'C', "\"(\" + code + \") \" if code" }, { 'P', "payee" }, - { 'a', "account.name" }, + { 'a', "account" }, { 'A', "account" }, { 't', "justify(scrub(display_amount), $min, $max, $left, color)" }, { 'T', "justify(scrub(display_total), $min, $max, $left, color)" }, @@ -206,7 +207,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, sizeof(format_mapping_t)); i++) { if (*p == single_letter_mappings[i].letter) { std::ostringstream expr; - for (const char * ptr = single_letter_mappings[i].expr; *ptr; ){ + for (const char * ptr = single_letter_mappings[i].expr; *ptr;) { if (*ptr == '$') { const char * beg = ++ptr; while (*ptr && std::isalpha(*ptr)) diff --git a/src/format.h b/src/format.h index 38f567bd..6ac4075e 100644 --- a/src/format.h +++ b/src/format.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/generate.cc b/src/generate.cc index 5bf424bb..6503f414 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/generate.h b/src/generate.h index 697feb64..b17116fa 100644 --- a/src/generate.h +++ b/src/generate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/global.cc b/src/global.cc index 0061ad76..602e216c 100644 --- a/src/global.cc +++ b/src/global.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -142,13 +142,13 @@ void global_scope_t::read_init() throw_(parse_error, _f("Could not find specified init file %1%") % init_file); } } else { - if (const char * home_var = std::getenv("HOME")){ + if (const char * home_var = std::getenv("HOME")) { init_file = (path(home_var) / ".ledgerrc"); } else { init_file = ("./.ledgerrc"); } } - if(exists(init_file)){ + if (exists(init_file)) { parse_init(init_file); } } @@ -388,7 +388,7 @@ void global_scope_t::read_environment_settings(char * envp[]) process_environment(const_cast<const char **>(envp), "LEDGER_", report()); #if 1 - // These are here for backwards compatability, but are deprecated. + // These are here for backwards compatibility, but are deprecated. if (const char * p = std::getenv("LEDGER")) { if (! std::getenv("LEDGER_FILE")) @@ -514,7 +514,7 @@ void handle_debug_options(int argc, char * argv[]) #if TRACING_ON _log_level = LOG_TRACE; try { - _trace_level = boost::lexical_cast<uint8_t>(argv[i + 1]); + _trace_level = boost::lexical_cast<uint16_t>(argv[i + 1]); } catch (const boost::bad_lexical_cast&) { throw std::logic_error(_("Argument to --trace must be an integer")); diff --git a/src/global.h b/src/global.h index 5fa67d2c..f36dbe3d 100644 --- a/src/global.h +++ b/src/global.h @@ -1,9 +1,9 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 met: + * modification, are permitted provided that the following conditions are + * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. @@ -127,7 +127,7 @@ public: out << '-' << Ledger_VERSION_DATE; out << _(", the command-line accounting tool"); out << - _("\n\nCopyright (c) 2003-2013, John Wiegley. All rights reserved.\n\n\ + _("\n\nCopyright (c) 2003-2015, John Wiegley. All rights reserved.\n\n\ This program is made available under the terms of the BSD Public License.\n\ See LICENSE file included with the distribution for details and disclaimer."); out << std::endl; diff --git a/src/history.cc b/src/history.cc index 93883ae0..e3c459f3 100644 --- a/src/history.cc +++ b/src/history.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/history.h b/src/history.h index fa69d8da..dd776383 100644 --- a/src/history.h +++ b/src/history.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/item.cc b/src/item.cc index a29a3fd3..9effcd23 100644 --- a/src/item.cc +++ b/src/item.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -191,7 +191,11 @@ public: static bool use_aux_date; virtual bool has_date() const { +#if BOOST_VERSION >= 105600 + return _date != NULL; +#else return _date; +#endif } virtual date_t date() const { diff --git a/src/iterators.cc b/src/iterators.cc index 738f33be..21bec5d9 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -204,9 +204,9 @@ void sorted_accounts_iterator::push_back(account_t& account) compare_items<account_t>(sort_cmp)); #if DEBUG_ON - if (SHOW_DEBUG("accounts.sorted")) { + if (SHOW_DEBUG("account.sorted")) { foreach (account_t * acct, accounts_list.back()) - DEBUG("accounts.sorted", + DEBUG("account.sorted", "Account (flat): " << acct->fullname()); } #endif @@ -237,9 +237,9 @@ void sorted_accounts_iterator::sort_accounts(account_t& account, compare_items<account_t>(sort_cmp)); #if DEBUG_ON - if (SHOW_DEBUG("accounts.sorted")) { + if (SHOW_DEBUG("account.sorted")) { foreach (account_t * acct, deque) - DEBUG("accounts.sorted", "Account: " << acct->fullname()); + DEBUG("account.sorted", "Account: " << acct->fullname()); } #endif } diff --git a/src/iterators.h b/src/iterators.h index 3ece313c..86eb86ca 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/journal.cc b/src/journal.cc index 7eeaabe8..ae545477 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -95,7 +95,9 @@ void journal_t::initialize() force_checking = false; check_payees = false; day_break = false; - checking_style = CHECK_PERMISSIVE; + checking_style = CHECK_NORMAL; + recursive_aliases = false; + no_aliases = false; } void journal_t::add_account(account_t * acct) @@ -121,26 +123,9 @@ account_t * journal_t::find_account_re(const string& regexp) account_t * journal_t::register_account(const string& name, post_t * post, account_t * master_account) { - account_t * result = NULL; - - // If there any account aliases, substitute before creating an account + // If there are any account aliases, substitute before creating an account // object. - if (account_aliases.size() > 0) { - accounts_map::const_iterator i = account_aliases.find(name); - if (i != account_aliases.end()) { - result = (*i).second; - } else { - // only check the very first account for alias expansion, in case - // that can be expanded successfully - size_t colon = name.find(':'); - if(colon != string::npos) { - accounts_map::const_iterator j = account_aliases.find(name.substr(0, colon)); - if (j != account_aliases.end()) { - result = find_account((*j).second->fullname() + name.substr(colon)); - } - } - } - } + account_t * result = expand_aliases(name); // Create the account object and associate it with the journal; this // is registering the account. @@ -178,7 +163,63 @@ account_t * journal_t::register_account(const string& name, post_t * post, } } } + return result; +} + +account_t * journal_t::expand_aliases(string name) { + // Aliases are expanded recursively, so if both alias Foo=Bar:Foo and + // alias Bar=Baaz:Bar are in effect, first Foo will be expanded to Bar:Foo, + // then Bar:Foo will be expanded to Baaz:Bar:Foo. + // The expansion loop keeps a list of already expanded names in order to + // prevent infinite excursion. Each alias may only be expanded at most once. + account_t * result = NULL; + if (no_aliases) + return result; + + bool keep_expanding = true; + std::list<string> already_seen; + // loop until no expansion can be found + do { + if (account_aliases.size() > 0) { + accounts_map::const_iterator i = account_aliases.find(name); + if (i != account_aliases.end()) { + if (std::find(already_seen.begin(), already_seen.end(), name) != already_seen.end()) { + throw_(std::runtime_error, + _f("Infinite recursion on alias expansion for %1%") + % name); + } + // there is an alias for the full account name, including colons + already_seen.push_back(name); + result = (*i).second; + name = result->fullname(); + } else { + // only check the very first account for alias expansion, in case + // that can be expanded successfully + size_t colon = name.find(':'); + if (colon != string::npos) { + string first_account_name = name.substr(0, colon); + accounts_map::const_iterator j = account_aliases.find(first_account_name); + if (j != account_aliases.end()) { + if (std::find(already_seen.begin(), already_seen.end(), first_account_name) != already_seen.end()) { + throw_(std::runtime_error, + _f("Infinite recursion on alias expansion for %1%") + % first_account_name); + } + already_seen.push_back(first_account_name); + result = find_account((*j).second->fullname() + name.substr(colon)); + name = result->fullname(); + } else { + keep_expanding = false; + } + } else { + keep_expanding = false; + } + } + } else { + keep_expanding = false; + } + } while(keep_expanding && recursive_aliases); return result; } @@ -208,7 +249,7 @@ string journal_t::register_payee(const string& name, xact_t * xact) } } - foreach (payee_mapping_t& value, payee_mappings) { + foreach (payee_alias_mapping_t& value, payee_alias_mappings) { if (value.first.match(name)) { payee = value.second; break; @@ -323,6 +364,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; @@ -345,12 +401,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 716e2910..613b2b96 100644 --- a/src/journal.h +++ b/src/journal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -58,15 +58,17 @@ class account_t; class parse_context_t; class parse_context_stack_t; -typedef std::list<xact_t *> xacts_list; -typedef std::list<auto_xact_t *> auto_xacts_list; -typedef std::list<period_xact_t *> period_xacts_list; -typedef std::pair<mask_t, string> payee_mapping_t; -typedef std::list<payee_mapping_t> payee_mappings_t; -typedef std::pair<mask_t, account_t *> account_mapping_t; -typedef std::list<account_mapping_t> account_mappings_t; -typedef std::map<string, account_t *> accounts_map; -typedef std::map<string, xact_t *> checksum_map_t; +typedef std::list<xact_t *> xacts_list; +typedef std::list<auto_xact_t *> auto_xacts_list; +typedef std::list<period_xact_t *> period_xacts_list; +typedef std::pair<mask_t, string> payee_alias_mapping_t; +typedef std::list<payee_alias_mapping_t> payee_alias_mappings_t; +typedef std::pair<string, string> payee_uuid_mapping_t; +typedef std::list<payee_uuid_mapping_t> payee_uuid_mappings_t; +typedef std::pair<mask_t, account_t *> account_mapping_t; +typedef std::list<account_mapping_t> account_mappings_t; +typedef std::map<string, account_t *> accounts_map; +typedef std::map<string, xact_t *> checksum_map_t; typedef std::multimap<string, expr_t::check_expr_pair> tag_check_exprs_map; @@ -115,33 +117,37 @@ public: #endif // HAVE_BOOST_SERIALIZATION }; - account_t * master; - account_t * bucket; - xacts_list xacts; - auto_xacts_list auto_xacts; - period_xacts_list period_xacts; - std::list<fileinfo_t> sources; - std::set<string> known_payees; - std::set<string> known_tags; - bool fixed_accounts; - bool fixed_payees; - bool fixed_commodities; - bool fixed_metadata; - bool was_loaded; - bool force_checking; - bool check_payees; - bool day_break; - payee_mappings_t payee_mappings; - account_mappings_t account_mappings; - accounts_map account_aliases; - account_mappings_t payees_for_unknown_accounts; - checksum_map_t checksum_map; - tag_check_exprs_map tag_check_exprs; - optional<expr_t> value_expr; - parse_context_t * current_context; + account_t * master; + account_t * bucket; + xacts_list xacts; + auto_xacts_list auto_xacts; + period_xacts_list period_xacts; + std::list<fileinfo_t> sources; + std::set<string> known_payees; + std::set<string> known_tags; + bool fixed_accounts; + bool fixed_payees; + bool fixed_commodities; + bool fixed_metadata; + bool was_loaded; + bool force_checking; + bool check_payees; + bool day_break; + bool recursive_aliases; + bool no_aliases; + payee_alias_mappings_t payee_alias_mappings; + payee_uuid_mappings_t payee_uuid_mappings; + account_mappings_t account_mappings; + accounts_map account_aliases; + account_mappings_t payees_for_unknown_accounts; + checksum_map_t checksum_map; + tag_check_exprs_map tag_check_exprs; + optional<expr_t> value_expr; + parse_context_t * current_context; enum checking_style_t { CHECK_PERMISSIVE, + CHECK_NORMAL, CHECK_WARNING, CHECK_ERROR } checking_style; @@ -167,6 +173,8 @@ public: account_t * find_account(const string& name, bool auto_create = true); account_t * find_account_re(const string& regexp); + account_t * expand_aliases(string name); + account_t * register_account(const string& name, post_t * post, account_t * master = NULL); string register_payee(const string& name, xact_t * xact); diff --git a/src/lookup.cc b/src/lookup.cc index 2a602569..6dbeb502 100644 --- a/src/lookup.cc +++ b/src/lookup.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/lookup.h b/src/lookup.h index ee80faac..fc7063ec 100644 --- a/src/lookup.h +++ b/src/lookup.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/main.cc b/src/main.cc index accae197..c26a280a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/mask.cc b/src/mask.cc index 56ef02d1..29b53781 100644 --- a/src/mask.cc +++ b/src/mask.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/option.cc b/src/option.cc index 256ee45b..8c003e08 100644 --- a/src/option.cc +++ b/src/option.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/option.h b/src/option.h index fe3e257b..4646121d 100644 --- a/src/option.h +++ b/src/option.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -229,7 +229,7 @@ public: inline bool is_eq(const char * p, const char * n) { // Test whether p matches n, substituting - in p for _ in n. for (; *p && *n; p++, n++) { - if (! (*p == '-' && *n == '_' ) && *p != *n) + if (! (*p == '-' && *n == '_') && *p != *n) return false; } // Ignore any trailing underscore @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -43,7 +43,8 @@ namespace ledger { posts_to_org_table::posts_to_org_table(report_t& _report, const optional<string>& _prepend_format) - : report(_report), last_xact(NULL), last_post(NULL) + : report(_report), last_xact(NULL), last_post(NULL), + header_printed(false), first_report_title(true) { first_line_format.parse_format ("|%(format_date(date))" @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/output.cc b/src/output.cc index 77d28eed..ffd144e1 100644 --- a/src/output.cc +++ b/src/output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -331,10 +331,10 @@ void report_tags::flush() void report_tags::operator()(post_t& post) { - if(post.metadata){ - foreach (const item_t::string_map::value_type& data, *post.metadata){ + if (post.metadata) { + foreach (const item_t::string_map::value_type& data, *post.metadata) { string tag=data.first; - if(report.HANDLED(values) && (data.second).first){ + if (report.HANDLED(values) && (data.second).first) { tag+=": "+ (data.second).first.get().to_string(); } std::map<string, std::size_t>::iterator i = tags.find(tag); diff --git a/src/output.h b/src/output.h index 31afbd13..ec7ec6c2 100644 --- a/src/output.h +++ b/src/output.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/parser.cc b/src/parser.cc index bcb23f48..8044338f 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/parser.h b/src/parser.h index 8cc8027f..e46fc719 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/pool.cc b/src/pool.cc index 2627c816..95c0f49d 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -267,7 +267,8 @@ commodity_pool_t::exchange(const amount_t& amount, // Do not record commodity exchanges where amount's commodity has a // fixated price, since this does not establish a market value for the // base commodity. - if (! per_unit_cost.is_realzero() && + if (add_price && + ! per_unit_cost.is_realzero() && (current_annotation == NULL || ! (current_annotation->price && current_annotation->has_flags(ANNOTATION_PRICE_FIXATED))) && @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/post.cc b/src/post.cc index 1a24429b..269e2e6c 100644 --- a/src/post.cc +++ b/src/post.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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,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; @@ -67,6 +68,7 @@ public: amount_t amount; // can be null until finalization optional<expr_t> amount_expr; optional<amount_t> cost; + optional<amount_t> given_cost; optional<amount_t> assigned_amount; optional<datetime_t> checkin; optional<datetime_t> checkout; @@ -203,7 +205,11 @@ public: mutable optional<xdata_t> xdata_; bool has_xdata() const { +#if BOOST_VERSION >= 105600 + return xdata_ != NULL; +#else return xdata_; +#endif } void clear_xdata() { xdata_ = none; diff --git a/src/precmd.cc b/src/precmd.cc index e9c966e9..2da6ba8e 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/precmd.h b/src/precmd.h index 27925f42..bf2732d7 100644 --- a/src/precmd.h +++ b/src/precmd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/predicate.h b/src/predicate.h index 6a1cc01c..30d07223 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/print.cc b/src/print.cc index 0683f2e3..02e518f7 100644 --- a/src/print.cc +++ b/src/print.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -243,13 +243,21 @@ namespace { amtbuf << string(2 - (slip + amt_slip), ' '); amtbuf << amt; - if (post->cost && + if (post->given_cost && ! post->has_flags(POST_CALCULATED | POST_COST_CALCULATED)) { + std::string cost_op; if (post->has_flags(POST_COST_IN_FULL)) - amtbuf << " @@ " << post->cost->abs(); + cost_op = "@@"; else - amtbuf << " @ " - << (*post->cost / post->amount).abs(); + cost_op = "@"; + if (post->has_flags(POST_COST_VIRTUAL)) + cost_op = "(" + cost_op + ")"; + + if (post->has_flags(POST_COST_IN_FULL)) + amtbuf << " " << cost_op << " " << post->given_cost->abs(); + else + amtbuf << " " << cost_op << " " + << (*post->given_cost / post->amount).abs(); } if (post->assigned_amount) diff --git a/src/print.h b/src/print.h index 8d78e8cc..c587b499 100644 --- a/src/print.h +++ b/src/print.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/pstream.h b/src/pstream.h index 4886dc0b..3b669aca 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -1,9 +1,9 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 met: + * modification, are permitted provided that the following conditions are + * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. @@ -83,7 +83,10 @@ class ptristream : public std::istream virtual pos_type seekoff(off_type off, ios_base::seekdir way, ios_base::openmode) { - switch (way) { + // cast to avoid gcc '-Wswitch' warning + // as ios_base::beg/cur/end are not necesssarily values of 'way' enum type ios_base::seekdir + // based on https://svn.boost.org/trac/boost/ticket/7644 + switch (static_cast<int>(way)) { case std::ios::cur: setg(ptr, gptr()+off, ptr+len); break; diff --git a/src/ptree.cc b/src/ptree.cc index e7afdcd1..3efe4451 100644 --- a/src/ptree.cc +++ b/src/ptree.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -80,7 +80,12 @@ void format_ptree::flush() switch (format) { case FORMAT_XML: +#if BOOST_VERSION >= 105600 + auto indented = property_tree::xml_writer_make_settings<std::string> (' ', 2); +#else property_tree::xml_writer_settings<char> indented(' ', 2); +#endif + property_tree::write_xml(out, pt, indented); out << std::endl; break; diff --git a/src/ptree.h b/src/ptree.h index 154c8837..36708dcf 100644 --- a/src/ptree.h +++ b/src/ptree.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_account.cc b/src/py_account.cc index fbd68140..1cc28b92 100644 --- a/src/py_account.cc +++ b/src/py_account.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -35,6 +35,7 @@ #include "pyutils.h" #include "account.h" #include "post.h" +#include "expr.h" namespace ledger { @@ -97,6 +98,26 @@ namespace { return str_to_py_unicode(account.fullname()); } + value_t py_amount_0(const account_t& account) + { + return account.amount(); + } + + value_t py_amount_1(const account_t& account, const boost::optional<expr_t&>& expr) + { + return account.amount(expr); + } + + value_t py_total_0(const account_t& account) + { + return account.total(); + } + + value_t py_total_1(const account_t& account, const boost::optional<expr_t&>& expr) + { + return account.total(expr); + } + } // unnamed namespace void export_account() @@ -221,8 +242,10 @@ void export_account() .def("xdata", py_xdata, return_internal_reference<>()) - .def("amount", &account_t::amount) - .def("total", &account_t::total) + .def("amount", py_amount_0) + .def("amount", py_amount_1, args("expr")) + .def("total", py_total_0) + .def("total", py_total_1, args("expr")) .def("self_details", &account_t::self_details, return_internal_reference<>()) diff --git a/src/py_amount.cc b/src/py_amount.cc index 50e16a70..84558a29 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_balance.cc b/src/py_balance.cc index 65e3c401..98ed8c60 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -56,6 +56,11 @@ namespace { const datetime_t& moment) { return balance.value(moment, in_terms_of); } + boost::optional<balance_t> py_value_2d(const balance_t& balance, + const commodity_t * in_terms_of, + const date_t& moment) { + return balance.value(datetime_t(moment), in_terms_of); + } boost::optional<amount_t> py_commodity_amount_0(const balance_t& balance) { @@ -64,7 +69,7 @@ namespace { boost::optional<amount_t> py_commodity_amount_1(const balance_t& balance, - const boost::optional<const commodity_t&>& commodity) { + const commodity_t& commodity) { return balance.commodity_amount(commodity); } @@ -200,6 +205,7 @@ void export_balance() .def("value", py_value_0) .def("value", py_value_1, args("in_terms_of")) .def("value", py_value_2, args("in_terms_of", "moment")) + .def("value", py_value_2d, args("in_terms_of", "moment")) .def("__nonzero__", &balance_t::is_nonzero) .def("is_nonzero", &balance_t::is_nonzero) diff --git a/src/py_commodity.cc b/src/py_commodity.cc index fd932fc7..27a0e105 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -315,6 +315,7 @@ void export_commodity() scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED; scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED; scope().attr("COMMODITY_STYLE_DECIMAL_COMMA") = COMMODITY_STYLE_DECIMAL_COMMA; + scope().attr("COMMODITY_STYLE_TIME_COLON") = COMMODITY_STYLE_TIME_COLON; scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS; scope().attr("COMMODITY_NOMARKET") = COMMODITY_NOMARKET; scope().attr("COMMODITY_BUILTIN") = COMMODITY_BUILTIN; diff --git a/src/py_expr.cc b/src/py_expr.cc index c680bab4..803388f2 100644 --- a/src/py_expr.cc +++ b/src/py_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_format.cc b/src/py_format.cc index 43266b29..6086d6cb 100644 --- a/src/py_format.cc +++ b/src/py_format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_item.cc b/src/py_item.cc index 02b978c5..473bbef8 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_journal.cc b/src/py_journal.cc index 7cce6b11..abbcd866 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_post.cc b/src/py_post.cc index 6061d6ee..851828c7 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_session.cc b/src/py_session.cc index 7e2f9e8a..f6053180 100644 --- a/src/py_session.cc +++ b/src/py_session.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -61,6 +61,8 @@ void export_session() .def("read_journal_files", &session_t::read_journal_files, return_internal_reference<>()) .def("close_journal_files", &session_t::close_journal_files) + .def("journal", &session_t::get_journal, + return_internal_reference<>()) ; scope().attr("session") = diff --git a/src/py_times.cc b/src/py_times.cc index 599aa60c..906dcfaa 100644 --- a/src/py_times.cc +++ b/src/py_times.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -108,7 +108,7 @@ struct datetime_from_python static void* convertible(PyObject* obj_ptr) { MY_PyDateTime_IMPORT; - if(PyDateTime_Check(obj_ptr)) return obj_ptr; + if (PyDateTime_Check(obj_ptr)) return obj_ptr; return 0; } diff --git a/src/py_utils.cc b/src/py_utils.cc index dc572621..df87bded 100644 --- a/src/py_utils.cc +++ b/src/py_utils.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_value.cc b/src/py_value.cc index 8b565661..2828e3b8 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/py_xact.cc b/src/py_xact.cc index 0b07f582..5b4c14d4 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/pyfstream.h b/src/pyfstream.h index 12bbecfd..11f514a2 100644 --- a/src/pyfstream.h +++ b/src/pyfstream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/pyinterp.cc b/src/pyinterp.cc index e15ff503..54e62946 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -34,6 +34,7 @@ #include "pyinterp.h" #include "pyutils.h" #include "account.h" +#include "report.h" #include "xact.h" #include "post.h" @@ -76,6 +77,12 @@ void initialize_for_python() export_xact(); export_session(); export_journal(); + + if (! scope_t::default_scope) { + python_session.reset(new ledger::python_interpreter_t); + shared_ptr<session_t> session_ptr = python_session; + scope_t::default_scope = new report_t(*session_ptr); + } } struct python_run diff --git a/src/pyinterp.h b/src/pyinterp.h index e961f4c0..32becbf6 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/pyledger.cc b/src/pyledger.cc index ee7b99c9..1f478dec 100644 --- a/src/pyledger.cc +++ b/src/pyledger.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/pyutils.h b/src/pyutils.h index 6bb9d0bd..039b2605 100644 --- a/src/pyutils.h +++ b/src/pyutils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/query.cc b/src/query.cc index 209205ae..04b18581 100644 --- a/src/query.cc +++ b/src/query.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/query.h b/src/query.h index 10820a59..b7608bc7 100644 --- a/src/query.h +++ b/src/query.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/quotes.cc b/src/quotes.cc index 2df51ec1..6d179287 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/quotes.h b/src/quotes.h index 813bdfa3..c14bc495 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/report.cc b/src/report.cc index 29077f10..4b240611 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -181,10 +181,17 @@ void report_t::normalize_options(const string& verb) } long cols = 0; +#if HAVE_IOCTL + struct winsize ws; +#endif if (HANDLED(columns_)) cols = lexical_cast<long>(HANDLER(columns_).value); else if (const char * columns = std::getenv("COLUMNS")) cols = lexical_cast<long>(columns); +#if HAVE_IOCTL + else if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1) + cols = ws.ws_col; +#endif else cols = 80L; @@ -1113,6 +1120,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) OPT(csv_format_); else OPT_ALT(gain, change); else OPT(cleared); + else OPT(cleared_format_); else OPT(collapse); else OPT(collapse_if_zero); else OPT(color); @@ -1189,6 +1197,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'n': OPT_CH(collapse); else OPT(no_color); + else OPT(no_pager); else OPT(no_rounding); else OPT(no_titles); else OPT(no_total); diff --git a/src/report.h b/src/report.h index 5897e8f6..67e95884 100644 --- a/src/report.h +++ b/src/report.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -302,6 +302,7 @@ public: HANDLER(market).report(out); HANDLER(meta_).report(out); HANDLER(monthly).report(out); + HANDLER(no_pager).report(out); HANDLER(no_rounding).report(out); HANDLER(no_titles).report(out); HANDLER(no_total).report(out); @@ -464,8 +465,8 @@ public: " 12 + 1 + 12 + 1 + 12, true, color))" " %(ansify_if(" " justify((get_at(display_total, 1) ? " - " (100% * scrub(get_at(display_total, 0))) / " - " -scrub(get_at(display_total, 1)) : 0), " + " (100% * quantity(scrub(get_at(display_total, 0)))) / " + " -quantity(scrub(get_at(display_total, 1))) : 0), " " 5, -1, true, false)," " magenta if (color and get_at(display_total, 1) and " " (abs(quantity(scrub(get_at(display_total, 0))) / " @@ -521,7 +522,7 @@ public: "%(quoted(code))," "%(quoted(payee))," "%(quoted(display_account))," - "%(quoted(commodity))," + "%(quoted(commodity(scrub(display_amount))))," "%(quoted(quantity(scrub(display_amount))))," "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," "%(quoted(join(note | xact.note)))\n"); diff --git a/src/scope.cc b/src/scope.cc index 10ae45a9..3ca9e0c0 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/scope.h b/src/scope.h index d6291439..8ad3afac 100644 --- a/src/scope.h +++ b/src/scope.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/select.cc b/src/select.cc index b7e4c920..81800f16 100644 --- a/src/select.cc +++ b/src/select.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -145,10 +145,17 @@ value_t select_command(call_scope_t& args) string thus_far = ""; std::size_t cols = 0; +#if HAVE_IOCTL + struct winsize ws; +#endif if (report.HANDLED(columns_)) cols = lexical_cast<std::size_t>(report.HANDLER(columns_).value); else if (const char * columns_env = std::getenv("COLUMNS")) cols = lexical_cast<std::size_t>(columns_env); +#if HAVE_IOCTL + else if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1) + cols = ws.ws_col; +#endif else cols = 80; @@ -291,17 +298,17 @@ value_t select_command(call_scope_t& args) thus_far += "int(payee_width) + 1"; } else if (ident == "account") { - formatter << "ansify_if(" - << "ansify_if("; + formatter << "ansify_if("; if (accounts_report) { + formatter << "ansify_if("; formatter << "partial_account(options.flat), blue if color),"; } else { formatter << "justify(truncated("; as_expr(column)->print(formatter); formatter << ", int(account_width), int(abbrev_len))," - << "int(account_width)),"; - formatter << "true, color),"; + << "int(account_width), -1, "; + formatter << "false, color),"; if (! thus_far.empty()) thus_far += " + "; @@ -358,10 +365,9 @@ value_t select_command(call_scope_t& args) formatter << ")"; } formatter << "\\n"; + DEBUG("select.parse", "formatter: " << formatter.str()); } else if (keyword == "from") { - DEBUG("select.parse", "formatter: " << formatter.str()); - if (arg == "xacts" || arg == "txns" || arg == "transactions") { report_functor = expr_t::op_t::wrap_functor (reporter<>(post_handler_ptr(new print_xacts(report, diff --git a/src/select.h b/src/select.h index 346067a2..c95e15f7 100644 --- a/src/select.h +++ b/src/select.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/session.cc b/src/session.cc index e5425a50..b0d31be9 100644 --- a/src/session.cc +++ b/src/session.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -93,25 +93,31 @@ std::size_t session_t::read_data(const string& master_account) acct = journal->find_account(master_account); optional<path> price_db_path; - if (HANDLED(price_db_)){ + if (HANDLED(price_db_)) { price_db_path = resolve_path(HANDLER(price_db_).str()); - if (!exists(price_db_path.get())){ + if (!exists(price_db_path.get())) { throw_(parse_error, _f("Could not find specified price-db file %1%") % price_db_path); } } else { - if (const char * home_var = std::getenv("HOME")){ + if (const char * home_var = std::getenv("HOME")) { price_db_path = (path(home_var) / ".pricedb"); } else { price_db_path = ("./.ledgerrc"); } } + if (HANDLED(day_break)) + journal->day_break = true; + + if (HANDLED(recursive_aliases)) + 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(day_break)) - journal->day_break = true; if (HANDLED(permissive)) journal->checking_style = journal_t::CHECK_PERMISSIVE; @@ -119,8 +125,9 @@ std::size_t session_t::read_data(const string& master_account) 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; @@ -258,6 +265,11 @@ void session_t::close_journal_files() amount_t::initialize(); } +journal_t * session_t::get_journal() +{ + return journal.get(); +} + value_t session_t::fn_account(call_scope_t& args) { if (args[0].is_string()) @@ -344,12 +356,18 @@ option_t<session_t> * session_t::lookup_option(const char * p) case 'm': OPT(master_account_); break; + case 'n': + OPT(no_aliases); + break; case 'p': OPT(price_db_); else OPT(price_exp_); else OPT(pedantic); else OPT(permissive); break; + case 'r': + OPT(recursive_aliases); + break; case 's': OPT(strict); break; diff --git a/src/session.h b/src/session.h index 21be3cc7..b287b19e 100644 --- a/src/session.h +++ b/src/session.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -84,6 +84,8 @@ public: journal_t * read_journal_files(); void close_journal_files(); + journal_t * get_journal(); + value_t fn_account(call_scope_t& scope); value_t fn_min(call_scope_t& scope); value_t fn_max(call_scope_t& scope); @@ -109,6 +111,8 @@ public: HANDLER(permissive).report(out); HANDLER(price_db_).report(out); HANDLER(price_exp_).report(out); + HANDLER(recursive_aliases).report(out); + HANDLER(no_aliases).report(out); HANDLER(strict).report(out); HANDLER(value_expr_).report(out); } @@ -164,6 +168,8 @@ public: OPTION(session_t, price_db_); OPTION(session_t, strict); OPTION(session_t, value_expr_); + OPTION(session_t, recursive_aliases); + OPTION(session_t, no_aliases); }; /** diff --git a/src/stats.cc b/src/stats.cc index 81d71a46..406ecd3b 100644 --- a/src/stats.cc +++ b/src/stats.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/stats.h b/src/stats.h index 5583e704..f0408591 100644 --- a/src/stats.h +++ b/src/stats.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/stream.cc b/src/stream.cc index c00c4cc5..c5a40e87 100644 --- a/src/stream.cc +++ b/src/stream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -80,18 +80,10 @@ namespace { close(pfd[1]); close(pfd[0]); - // Find command name: its the substring starting right of the - // rightmost '/' character in the pager pathname. See manpage for - // strrchr. -#if BOOST_VERSION >= 103700 - path basename(pager_path.filename()); -#else - path basename(pager_path.leaf()); -#endif - execlp(pager_path.string().c_str(), basename.string().c_str(), NULL); + execlp("/bin/sh", "/bin/sh", "-c", pager_path.string().c_str(), NULL); // We should never, ever reach here - perror((std::string("execlp: ") + pager_path.string()).c_str()); + perror("execlp: /bin/sh"); exit(1); } else { // parent diff --git a/src/stream.h b/src/stream.h index bb63873a..05572cb4 100644 --- a/src/stream.h +++ b/src/stream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/system.hh.in b/src/system.hh.in index ff7ea75b..21417e09 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -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@ @@ -65,6 +63,7 @@ #define HAVE_REALPATH @HAVE_REALPATH@ #define HAVE_GETPWUID @HAVE_GETPWUID@ #define HAVE_GETPWNAM @HAVE_GETPWNAM@ +#define HAVE_IOCTL @HAVE_IOCTL@ #define HAVE_ISATTY @HAVE_ISATTY@ #define HAVE_UNIX_PIPES @HAVE_UNIX_PIPES@ @@ -153,14 +152,18 @@ typedef std::ostream::pos_type ostream_pos_type; #include <pwd.h> #endif +#if HAVE_IOCTL +#include <sys/ioctl.h> +#endif + #if HAVE_UNIX_PIPES #include <sys/types.h> #include <sys/wait.h> #endif +#include <cstddef> /* needed for gcc 4.9 */ #include <gmp.h> #include <mpfr.h> -#include "sha1.h" #include "utf8.h" #if HAVE_EDIT diff --git a/src/temps.cc b/src/temps.cc index b4778b49..15ccbc3a 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/temps.h b/src/temps.h index 99110496..77a7824e 100644 --- a/src/temps.h +++ b/src/temps.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/textual.cc b/src/textual.cc index 9e19ee37..8007ca0d 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -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"); @@ -153,6 +155,7 @@ namespace { void payee_directive(char * line); void payee_alias_directive(const string& payee, string alias); + void payee_uuid_directive(const string& payee, string uuid); void commodity_directive(char * line); void commodity_alias_directive(commodity_t& comm, string alias); @@ -282,6 +285,11 @@ void instance_t::parse() } } + if (apply_stack.front().value.type() == typeid(optional<datetime_t>)) + epoch = boost::get<optional<datetime_t> >(apply_stack.front().value); + + apply_stack.pop_front(); + #if defined(TIMELOG_SUPPORT) timelog.close(); #endif // TIMELOG_SUPPORT @@ -416,7 +424,9 @@ void instance_t::read_next_directive(bool& error_flag) price_xact_directive(line); break; case 'Y': // set the current year - apply_year_directive(line); + if (std::strlen(line+1) == 0) + throw_(parse_error, _f("Directive '%1%' requires an argument") % line[0]); + apply_year_directive(line+1); break; } } @@ -779,8 +789,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(); } @@ -860,14 +870,17 @@ void instance_t::apply_rate_directive(char * line) void instance_t::apply_year_directive(char * line) { - apply_stack.push_front(application_t("year", epoch)); - - // This must be set to the last day of the year, otherwise partial - // dates like "11/01" will refer to last year's november, not the - // current year. - unsigned short year(lexical_cast<unsigned short>(skip_ws(line + 1))); - DEBUG("times.epoch", "Setting current year to " << year); - epoch = datetime_t(date_t(year, 12, 31)); + try { + unsigned short year(lexical_cast<unsigned short>(skip_ws(line))); + apply_stack.push_front(application_t("year", epoch)); + DEBUG("times.epoch", "Setting current year to " << year); + // This must be set to the last day of the year, otherwise partial + // dates like "11/01" will refer to last year's november, not the + // current year. + epoch = datetime_t(date_t(year, 12, 31)); + } catch(bad_lexical_cast &) { + throw_(parse_error, _f("Argument '%1%' not a valid year") % skip_ws(line)); + } } void instance_t::end_apply_directive(char * kind) @@ -915,6 +928,10 @@ void instance_t::account_directive(char * line) char * b = next_element(q); string keyword(q); + // Ensure there's an argument for the directives that need one. + if (! b && keyword != "default") + throw_(parse_error, _f("Account directive '%1%' requires an argument") % keyword); + if (keyword == "alias") { account_alias_directive(account, b); } @@ -977,6 +994,11 @@ void instance_t::account_alias_directive(account_t * account, string alias) // (account), add a reference to the account in the `account_aliases' // map, which is used by the post parser to resolve alias references. trim(alias); + // Ensure that no alias like "alias Foo=Foo" is registered. + if ( alias == account->fullname()) { + throw_(parse_error, _f("Illegal alias %1%=%2%") + % alias % account->fullname()); + } std::pair<accounts_map::iterator, bool> result = context.journal->account_aliases.insert (accounts_map::value_type(alias, account)); @@ -1026,16 +1048,28 @@ void instance_t::payee_directive(char * line) char * b = next_element(p); string keyword(p); + if (! b) + throw_(parse_error, _f("Payee directive '%1%' requires an argument") % keyword); + if (keyword == "alias") payee_alias_directive(payee, b); + if (keyword == "uuid") + payee_uuid_directive(payee, b); } } void instance_t::payee_alias_directive(const string& payee, string alias) { trim(alias); - context.journal->payee_mappings - .push_back(payee_mapping_t(mask_t(alias), payee)); + context.journal->payee_alias_mappings + .push_back(payee_alias_mapping_t(mask_t(alias), payee)); +} + +void instance_t::payee_uuid_directive(const string& payee, string uuid) +{ + trim(uuid); + context.journal->payee_uuid_mappings + .push_back(payee_uuid_mapping_t(uuid, payee)); } void instance_t::commodity_directive(char * line) @@ -1056,6 +1090,10 @@ void instance_t::commodity_directive(char * line) char * b = next_element(q); string keyword(q); + // Ensure there's an argument for the directives that need one. + if (! b && keyword != "nomarket" && keyword != "default") + throw_(parse_error, _f("Commodity directive '%1%' requires an argument") % keyword); + if (keyword == "alias") commodity_alias_directive(*commodity, b); else if (keyword == "value") @@ -1217,13 +1255,13 @@ void instance_t::python_directive(char * line) void instance_t::import_directive(char *) { throw_(parse_error, - _("'python' directive seen, but Python support is missing")); + _("'import' directive seen, but Python support is missing")); } void instance_t::python_directive(char *) { throw_(parse_error, - _("'import' directive seen, but Python support is missing")); + _("'python' directive seen, but Python support is missing")); } #endif // HAVE_BOOST_PYTHON @@ -1240,6 +1278,14 @@ bool instance_t::general_directive(char * line) if (*p == '@' || *p == '!') p++; + // Ensure there's an argument for all directives that need one. + if (! arg && + std::strcmp(p, "comment") != 0 && std::strcmp(p, "end") != 0 + && std::strcmp(p, "python") != 0 && std::strcmp(p, "test") != 0 && + *p != 'Y') { + throw_(parse_error, _f("Directive '%1%' requires an argument") % p); + } + switch (*p) { case 'a': if (std::strcmp(p, "account") == 0) { @@ -1339,6 +1385,13 @@ bool instance_t::general_directive(char * line) return true; } break; + + case 'y': + if (std::strcmp(p, "year") == 0) { + apply_year_directive(arg); + return true; + } + break; } if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) { @@ -1398,8 +1451,7 @@ post_t * instance_t::parse_post(char * line, } if (xact && - ((xact->_state == item_t::CLEARED && post->_state != item_t::CLEARED) || - (xact->_state == item_t::PENDING && post->_state == item_t::UNCLEARED))) + (xact->_state != item_t::UNCLEARED && post->_state == item_t::UNCLEARED)) post->set_state(xact->_state); // Parse the account name @@ -1425,6 +1477,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 << ": " @@ -1490,13 +1548,12 @@ post_t * instance_t::parse_post(char * line, post->add_flags(POST_COST_IN_FULL); DEBUG("textual.parse", "line " << context.linenum << ": " << "And it's for a total price"); + next++; } - if (post->has_flags(POST_COST_VIRTUAL) && *(next + 1) == ')') + if (post->has_flags(POST_COST_VIRTUAL) && *next == ')') ++next; - beg = static_cast<std::streamsize>(++next - line); - p = skip_ws(next); if (*p) { post->cost = amount_t(); @@ -1538,6 +1595,8 @@ post_t * instance_t::parse_post(char * line, if (fixed_cost) post->add_flags(POST_COST_FIXATED); + post->given_cost = post->cost; + DEBUG("textual.parse", "line " << context.linenum << ": " << "Total cost is " << *post->cost); DEBUG("textual.parse", "line " << context.linenum << ": " @@ -1612,6 +1671,8 @@ post_t * instance_t::parse_post(char * line, break; } + amount_t tot = amt - diff; + DEBUG("post.assign", "line " << context.linenum << ": " << "diff = " << diff); DEBUG("textual.parse", "line " << context.linenum << ": " @@ -1620,8 +1681,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 << ": " @@ -1770,7 +1833,7 @@ xact_t * instance_t::parse_xact(char * line, char *q = p - 1; while (q > next && std::isspace(*q)) --q; - if (q > next) + if (q >= next) *(q + 1) = '\0'; break; } @@ -1844,6 +1907,17 @@ xact_t * instance_t::parse_xact(char * line, else { reveal_context = false; + if (!last_post) { + if (xact->has_tag(_("UUID"))) { + string uuid = xact->get_tag(_("UUID"))->to_string(); + foreach (payee_uuid_mapping_t value, context.journal->payee_uuid_mappings) { + if (value.first.compare(uuid) == 0) { + xact->payee = value.second; + } + } + } + } + if (post_t * post = parse_post(p, len - (p - line), account, xact.get())) { reveal_context = true; @@ -1904,13 +1978,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/timelog.cc b/src/timelog.cc index 64017d50..91ee2697 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/timelog.h b/src/timelog.h index 1ce0be28..af621ee6 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/times.cc b/src/times.cc index b1cb3494..b6c15dc3 100644 --- a/src/times.cc +++ b/src/times.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -839,6 +839,16 @@ void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok, 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: + specifier.year = boost::get<date_specifier_t::year_type>(*tok.value); + break; + case lexer_t::token_t::END_REACHED: + break; + default: + break; + } break; case lexer_t::token_t::TOK_A_WDAY: specifier.wday = @@ -1802,6 +1812,7 @@ void times_initialize() readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m/%d", true))); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m", true))); readers.push_back(shared_ptr<date_io_t>(new date_io_t("%y/%m/%d", true))); + readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y-%m-%d", true))); is_initialized = true; } diff --git a/src/times.h b/src/times.h index 4b89cc0c..c1bfb1cc 100644 --- a/src/times.h +++ b/src/times.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -568,7 +568,11 @@ public: void stabilize(const optional<date_t>& date = none); bool is_valid() const { +#if BOOST_VERSION >= 105600 + return start != NULL; +#else return start; +#endif } /** Find the current or next period containing date. Returns false if diff --git a/src/token.cc b/src/token.cc index d0ec185b..9fe7873f 100644 --- a/src/token.cc +++ b/src/token.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -522,7 +522,8 @@ void expr_t::token_t::expected(const char wanted, char c) void expr_t::token_t::expected(const kind_t wanted) { try { - if (wanted == '\0' || wanted == -1) + if (wanted == expr_t::token_t::ERROR || + wanted == expr_t::token_t::UNKNOWN) throw_(parse_error, _f("Invalid token '%1%'") % *this); else throw_(parse_error, diff --git a/src/token.h b/src/token.h index 51fa1d78..3fe249aa 100644 --- a/src/token.h +++ b/src/token.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/unistring.h b/src/unistring.h index eb38f8b1..3f459d83 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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,6 +44,8 @@ namespace ledger { +int mk_wcwidth(boost::uint32_t ucs); + /** * @class unistring * @@ -81,6 +83,14 @@ public: return utf32chars.size(); } + std::size_t width() const { + std::size_t width = 0; + foreach (const boost::uint32_t& ch, utf32chars) { + width += mk_wcwidth(ch); + } + return width; + } + std::string extract(const std::string::size_type begin = 0, const std::string::size_type len = 0) const { @@ -133,7 +143,7 @@ inline void justify(std::ostream& out, unistring temp(str); - int spacing = width - int(temp.length()); + int spacing = width - int(temp.width()); while (spacing-- > 0) out << ' '; diff --git a/src/utils.cc b/src/utils.cc index 4f810f93..9b528f54 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -229,7 +229,11 @@ static void trace_delete_func(void * ptr, const char * which) //#if !defined(__has_feature) || !__has_feature(address_sanitizer) +#ifdef _GLIBCXX_THROW +void * operator new(std::size_t size) _GLIBCXX_THROW(std::bad_alloc) { +#else void * operator new(std::size_t size) throw (std::bad_alloc) { +#endif void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new", size); @@ -241,7 +245,11 @@ void * operator new(std::size_t size, const std::nothrow_t&) throw() { ledger::trace_new_func(ptr, "new", size); return ptr; } +#ifdef _GLIBCXX_THROW +void * operator new[](std::size_t size) _GLIBCXX_THROW(std::bad_alloc) { +#else void * operator new[](std::size_t size) throw (std::bad_alloc) { +#endif void * ptr = std::malloc(size); if (DO_VERIFY() && ledger::memory_tracing_active) ledger::trace_new_func(ptr, "new[]", size); @@ -566,7 +574,7 @@ std::ostream * _log_stream = &std::cerr; std::ostringstream _log_buffer; #if TRACING_ON -uint8_t _trace_level; +uint16_t _trace_level; #endif static bool logger_has_run = false; diff --git a/src/utils.h b/src/utils.h index c4d11636..b92a6f49 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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,6 +44,8 @@ #ifndef _UTILS_H #define _UTILS_H +#include <boost/uuid/sha1.hpp> + /** * @name Default values */ @@ -249,7 +251,7 @@ void logger_func(log_level_t level); #if TRACING_ON -extern uint8_t _trace_level; +extern uint16_t _trace_level; #define SHOW_TRACE(lvl) \ (ledger::_log_level >= ledger::LOG_TRACE && lvl <= ledger::_trace_level) @@ -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/value.cc b/src/value.cc index 98e48c2f..4ea7c7a8 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -341,6 +341,10 @@ value_t& value_t::operator+=(const value_t& val) } switch (type()) { + case VOID: + *this = value_t(val); + return *this; + case DATETIME: switch (val.type()) { case INTEGER: @@ -870,6 +874,8 @@ bool value_t::is_less_than(const value_t& val) const return as_long() < val.as_long(); case AMOUNT: return val.as_amount() > as_long(); + case BALANCE: + return val.to_amount() > as_long(); default: break; } @@ -886,6 +892,8 @@ bool value_t::is_less_than(const value_t& val) const return as_amount() < val.as_amount(); else return commodity_t::compare_by_commodity()(&as_amount(), &val.as_amount()); + case BALANCE: + return val.to_amount() > as_amount(); default: break; } @@ -904,6 +912,8 @@ bool value_t::is_less_than(const value_t& val) const } return ! no_amounts; } + case BALANCE: + return val.to_amount() > to_amount(); default: break; } @@ -990,6 +1000,8 @@ bool value_t::is_greater_than(const value_t& val) const return as_long() > val.as_long(); case AMOUNT: return val.as_amount() < as_long(); + case BALANCE: + return val.to_amount() < as_long(); default: break; } @@ -1001,6 +1013,8 @@ bool value_t::is_greater_than(const value_t& val) const return as_amount() > val.as_long(); case AMOUNT: return as_amount() > val.as_amount(); + case BALANCE: + return val.to_amount() < as_amount(); default: break; } @@ -1019,6 +1033,8 @@ bool value_t::is_greater_than(const value_t& val) const } return ! no_amounts; } + case BALANCE: + return val.to_amount() < to_amount(); default: break; } diff --git a/src/value.h b/src/value.h index 93760234..c224ce04 100644 --- a/src/value.h +++ b/src/value.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/views.cc b/src/views.cc index 2646e84b..61b824cd 100644 --- a/src/views.cc +++ b/src/views.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/views.h b/src/views.h index 94bedda5..2be3d978 100644 --- a/src/views.h +++ b/src/views.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 diff --git a/src/wcwidth.c.patch b/src/wcwidth.c.patch new file mode 100644 index 00000000..5864a056 --- /dev/null +++ b/src/wcwidth.c.patch @@ -0,0 +1,74 @@ +--- wcwidth.c 2007-05-26 18:06:24.000000000 +0800 ++++ wcwidth.cc 2014-02-13 18:36:18.668331252 +0800 +@@ -59,15 +59,23 @@ + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +-#include <wchar.h> ++/* This file is modified to work with C++ and present Unicode ++ * characters in uint32_t type. ++ */ ++ ++#include <system.hh> ++ ++namespace ledger { + +-struct interval { +- int first; +- int last; +-}; ++namespace { ++ struct interval { ++ int first; ++ int last; ++ }; ++} + + /* auxiliary function for binary search in interval table */ +-static int bisearch(wchar_t ucs, const struct interval *table, int max) { ++static int bisearch(boost::uint32_t ucs, const struct interval *table, int max) { + int min = 0; + int mid; + +@@ -119,7 +127,7 @@ + * in ISO 10646. + */ + +-int mk_wcwidth(wchar_t ucs) ++int mk_wcwidth(boost::uint32_t ucs) + { + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ +@@ -204,7 +212,7 @@ + } + + +-int mk_wcswidth(const wchar_t *pwcs, size_t n) ++int mk_wcswidth(const boost::uint32_t *pwcs, size_t n) + { + int w, width = 0; + +@@ -227,7 +235,7 @@ + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +-int mk_wcwidth_cjk(wchar_t ucs) ++int mk_wcwidth_cjk(boost::uint32_t ucs) + { + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ +@@ -295,7 +303,7 @@ + } + + +-int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) ++int mk_wcswidth_cjk(const boost::uint32_t *pwcs, size_t n) + { + int w, width = 0; + +@@ -307,3 +315,5 @@ + + return width; + } ++ ++} // namespace ledger diff --git a/src/wcwidth.cc b/src/wcwidth.cc new file mode 100644 index 00000000..c23f83d7 --- /dev/null +++ b/src/wcwidth.cc @@ -0,0 +1,319 @@ +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +/* This file is modified to work with C++ and present Unicode + * characters in uint32_t type. + */ + +#include <system.hh> + +namespace ledger { + +namespace { + struct interval { + boost::uint32_t first; + boost::uint32_t last; + }; +} + +/* auxiliary function for binary search in interval table */ +static int bisearch(boost::uint32_t ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +int mk_wcwidth(boost::uint32_t ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + + +int mk_wcswidth(const boost::uint32_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcswidth(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +int mk_wcwidth_cjk(boost::uint32_t ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, + { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, + { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, + { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, + { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, + { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, + { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, + { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, + { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, + { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, + { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, + { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, + { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, + { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, + { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, + { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, + { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, + { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, + { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, + { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, + { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, + { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, + { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, + { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, + { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, + { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } + }; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, ambiguous, + sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return mk_wcwidth(ucs); +} + + +int mk_wcswidth_cjk(const boost::uint32_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + +} // namespace ledger diff --git a/src/xact.cc b/src/xact.cc index 7ea15322..5369c138 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 @@ -185,7 +185,7 @@ bool xact_base_t::finalize() if (post_account_bad || null_post_account_bad) throw_(std::logic_error, - _f("Posting with null amount's account may be mispelled:\n \"%1%\"") + _f("Posting with null amount's account may be misspelled:\n \"%1%\"") % (post_account_bad ? post->account->fullname() : null_post->account->fullname())); else @@ -292,9 +292,9 @@ bool xact_base_t::finalize() _("A posting's cost must be of a different commodity than its amount")); cost_breakdown_t breakdown = - commodity_pool_t::current_pool->exchange - (post->amount, *post->cost, false, ! post->has_flags(POST_COST_VIRTUAL), - datetime_t(date(), time_duration(0, 0, 0, 0))); + commodity_pool_t::current_pool->exchange( + post->amount, *post->cost, false, ! post->has_flags(POST_COST_VIRTUAL), + datetime_t(date(), time_duration(0, 0, 0, 0))); if (post->amount.has_annotation() && post->amount.annotation().price) { if (breakdown.basis_cost.commodity() == breakdown.final_cost.commodity()) { @@ -304,10 +304,9 @@ bool xact_base_t::finalize() DEBUG("xact.finalize", "gain_loss = " << gain_loss); gain_loss.in_place_round(); DEBUG("xact.finalize", "gain_loss rounds to = " << gain_loss); - if (post->must_balance()) add_or_set_value(balance, gain_loss.reduced()); - +#if 0 account_t * account; if (gain_loss.sign() > 0) account = journal->find_account(_("Equity:Capital Gains")); @@ -321,6 +320,9 @@ bool xact_base_t::finalize() p->add_flags(post->flags() & (POST_VIRTUAL | POST_MUST_BALANCE)); } add_post(p); +#else + *post->cost += gain_loss; +#endif DEBUG("xact.finalize", "added gain_loss, balance = " << balance); } else { DEBUG("xact.finalize", "gain_loss would have displayed as zero"); @@ -393,7 +395,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); @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013, John Wiegley. All rights reserved. + * Copyright (c) 2003-2015, 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 |